From 745f0032f7295a7a18894252adebd00dbdfbf902 Mon Sep 17 00:00:00 2001 From: Ramis Date: Sat, 27 Jun 2026 20:34:27 -0700 Subject: [PATCH] fix: make MongoDB mandatory, fail loud instead of degrading Reverse the best-effort error swallowing. MongoDB is core to PodMan's continual-learning story and must always be used, so a broken memory layer must surface immediately rather than silently masquerade as working (which is how observations stayed at 0 unnoticed). - agent verifies Mongo via initMemory() at boot; bad creds / unreachable Atlas now fail loudly before joining the room, not mid-demo - server exits on Mongo init failure instead of warning and limping on - getGitStates and onScreenFrame no longer swallow Mongo errors - memory persist() logs the failure and rethrows instead of warning Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01AaCFWMkYQmTcuPsxaaACft --- backend/src/agent.ts | 5 +++++ backend/src/agent/podman.ts | 32 +++++++++++++------------------- backend/src/memory/db.ts | 22 ++++++++-------------- backend/src/memory/store.ts | 8 +++++--- backend/src/server.ts | 6 +++++- 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/backend/src/agent.ts b/backend/src/agent.ts index 2062744..c565c56 100644 --- a/backend/src/agent.ts +++ b/backend/src/agent.ts @@ -14,6 +14,7 @@ import sharp from 'sharp'; import { AccessToken } from 'livekit-server-sdk'; import { env } from './env.js'; import { PodMan } from './agent/podman.js'; +import { initMemory } from './memory/db.js'; const POD_ROOM = process.env.POD_ROOM ?? 'demo-pod'; const HERMES_IDENTITY = 'podman-hermes'; @@ -30,6 +31,10 @@ async function agentToken(room: string): Promise { } async function main() { + // MongoDB is mandatory. Verify the connection before joining the room so bad + // creds / unreachable Atlas fail loudly at boot, not silently mid-demo. + await initMemory(); + const room = new Room(); const podman = new PodMan(room, POD_ROOM); await room.connect(env.LIVEKIT_URL, await agentToken(POD_ROOM), { diff --git a/backend/src/agent/podman.ts b/backend/src/agent/podman.ts index e4fec6d..97cf920 100644 --- a/backend/src/agent/podman.ts +++ b/backend/src/agent/podman.ts @@ -43,27 +43,21 @@ export class PodMan { } async onScreenFrame(engineerId: string, jpeg: Buffer): Promise { - // Whole-frame guard: a Gemini/GitHub/Mongo failure on one frame must degrade - // gracefully, never crash the long-running live agent loop. - try { - const ctx = await analyzeFrame(engineerId, this.podId, jpeg); - this.contexts.set(engineerId, ctx); - await recordObservation(ctx); + const ctx = await analyzeFrame(engineerId, this.podId, jpeg); + this.contexts.set(engineerId, ctx); + await recordObservation(ctx); - // Fuse git ground truth: engineer_states written by scripts/podman-agent.mjs. - // Keyed by name (matches --name arg), same as LiveKit participant identity. - const gitStates = await getGitStates(this.podId); - for (const [id, c] of this.contexts) { - const git = gitStates.get(id); - if (git && git.changedFiles.length > 0) c.hasUnpushedChanges = true; - } - - const github = await getGithubState(); // cached - const collisions = detectCollisions([...this.contexts.values()], github); - for (const collision of collisions) await this.handle(collision); - } catch (err) { - console.warn(`[agent] frame from ${engineerId} skipped: ${(err as Error).message}`); + // Fuse git ground truth: engineer_states written by scripts/podman-agent.mjs. + // Keyed by name (matches --name arg), same as LiveKit participant identity. + const gitStates = await getGitStates(this.podId); + for (const [id, c] of this.contexts) { + const git = gitStates.get(id); + if (git && git.changedFiles.length > 0) c.hasUnpushedChanges = true; } + + const github = await getGithubState(); // cached + const collisions = detectCollisions([...this.contexts.values()], github); + for (const collision of collisions) await this.handle(collision); } private async handle(collision: Collision): Promise { diff --git a/backend/src/memory/db.ts b/backend/src/memory/db.ts index 19ad06e..5b64c73 100644 --- a/backend/src/memory/db.ts +++ b/backend/src/memory/db.ts @@ -69,21 +69,15 @@ export async function getGitStates(podId: string): Promise recentCommit?: string | null; gitUpdatedAt?: Date; }>('engineer_states'); + const docs = await col.find({ podId }).toArray(); const map = new Map(); - try { - const docs = await col.find({ podId }).toArray(); - for (const doc of docs) { - map.set(doc.name, { - changedFiles: doc.changedFiles ?? [], - branch: doc.branch ?? null, - recentCommit: doc.recentCommit ?? null, - gitUpdatedAt: doc.gitUpdatedAt ?? null, - }); - } - } catch (err) { - // Best-effort: a Mongo hiccup degrades git fusion, it must not crash the - // live agent loop. Detection falls back to vision-only signals. - console.warn(`[memory] getGitStates failed: ${(err as Error).message}`); + for (const doc of docs) { + map.set(doc.name, { + changedFiles: doc.changedFiles ?? [], + branch: doc.branch ?? null, + recentCommit: doc.recentCommit ?? null, + gitUpdatedAt: doc.gitUpdatedAt ?? null, + }); } return map; } diff --git a/backend/src/memory/store.ts b/backend/src/memory/store.ts index 30c9510..a314a17 100644 --- a/backend/src/memory/store.ts +++ b/backend/src/memory/store.ts @@ -10,14 +10,16 @@ import { enrichCollisionMemory } from './vectors.js'; /** * Continual-learning memory: persist observations, collisions, interventions, - * and outcomes to MongoDB so later sessions get sharper. Writes are best-effort - * — a Mongo hiccup logs a warning rather than crashing the agent/server. + * and outcomes to MongoDB so later sessions get sharper. MongoDB is mandatory — + * a failed write is surfaced loudly and rethrown, never silently swallowed, so + * a broken memory layer can never masquerade as a working one. */ async function persist(name: string, fn: () => Promise): Promise { try { await fn(); } catch (err) { - console.warn(`[memory] ${name} persist failed: ${(err as Error).message}`); + console.error(`[memory] ${name} persist FAILED: ${(err as Error).message}`); + throw err; } } diff --git a/backend/src/server.ts b/backend/src/server.ts index 6509f38..7f4dc59 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -177,7 +177,11 @@ http.listen(env.PORT, '0.0.0.0', () => { console.log(`[server] :${env.PORT}`); initMemory() .then(() => seedDefaultPods()) - .catch((e) => console.warn(`[memory] init failed: ${(e as Error).message}`)); + .catch((e) => { + // MongoDB is mandatory — do not run a half-dead API against a broken DB. + console.error(`[memory] init FAILED, exiting: ${(e as Error).message}`); + process.exit(1); + }); }); let shuttingDown = false;