feat: fuse engineer_states git truth into collision detector
Add getGitStates(podId) to db.ts — queries the engineer_states collection written by scripts/podman-agent.mjs and returns a map keyed by engineer name. In PodMan.onScreenFrame(), fuse before detectCollisions(): if an engineer has changedFiles in engineer_states, force hasUnpushedChanges=true on their context. This overrides Gemini vision's unreliable pixel-based guess with deterministic local git state — preventing the money-moment collision from silently not firing. Key assumption: LiveKit participant identity matches the --name arg used when running the git watcher (e.g. identity "alice" → --name alice). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaCFWMkYQmTcuPsxaaACft
This commit is contained in:
@@ -5,6 +5,7 @@ import { analyzeFrame } from '../vision/gemini.js';
|
||||
import { detectCollisions } from '../collision/detector.js';
|
||||
import { getGithubState } from '../github/client.js';
|
||||
import { recordObservation, recordCollision, recordIntervention } from '../memory/store.js';
|
||||
import { getGitStates } from '../memory/db.js';
|
||||
import { recallSimilar } from '../memory/vectors.js';
|
||||
import { shouldIntervene, preferredAction } from '../memory/policy.js';
|
||||
import { speak } from '../voice/live.js';
|
||||
@@ -36,6 +37,14 @@ export class PodMan {
|
||||
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);
|
||||
|
||||
@@ -43,6 +43,38 @@ export async function collections(): Promise<PodCollections> {
|
||||
};
|
||||
}
|
||||
|
||||
export interface GitState {
|
||||
changedFiles: string[];
|
||||
branch: string | null;
|
||||
recentCommit: string | null;
|
||||
gitUpdatedAt: Date | null;
|
||||
}
|
||||
|
||||
/** Fetch latest git state per engineer for a pod from the engineer_states collection.
|
||||
* Returns a map keyed by engineer name (matches --name arg used in podman-agent.mjs). */
|
||||
export async function getGitStates(podId: string): Promise<Map<string, GitState>> {
|
||||
const db = await getDb();
|
||||
const col = db.collection<{
|
||||
_id: string;
|
||||
name: string;
|
||||
changedFiles?: string[];
|
||||
branch?: string | null;
|
||||
recentCommit?: string | null;
|
||||
gitUpdatedAt?: Date;
|
||||
}>('engineer_states');
|
||||
const docs = await col.find({ podId }).toArray();
|
||||
const map = new Map<string, GitState>();
|
||||
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;
|
||||
}
|
||||
|
||||
/** Connect, verify reachability, and create helpful indexes. Call once on startup. */
|
||||
export async function initMemory(): Promise<void> {
|
||||
const db = await getDb();
|
||||
|
||||
Reference in New Issue
Block a user