feat(graph): continual-learning graph data model + dark-Bauhaus viz

Adds the team_model graph layer (the "it learned" view) and its visualization,
per docs/graph.md. Demo-backed first; built on origin/main conventions.

- shared: PodGraph / node / edge / metric types + GraphNodeDoc / GraphEdgeDoc
- backend/src/graph: demo graph data; Mongo store (loadPodGraph w/ demo
  fallback, seedGraph, reachFrom via $graphLookup); graph:seed script
- backend/src/server.ts: additive GET /api/pods/:id/graph + /graph/reach/:node
- frontend: dark-Bauhaus GraphView + fetch helper, reachable from a
  "Team memory" toggle in App.tsx
- docs/graph.md: spec (data model, $graphLookup, API, demo-first plan)

Demo-backed: the route serves a grounded demo graph when team_model has no
graph yet; graph:seed writes team_model + graph_nodes/graph_edges so the
$graphLookup traversal is real. Swap loadPodGraph to live team_model later.

Known follow-ups before merge: branch is based on 65a0791; origin/main is now
at 1294b84, so this needs a rebase + App.tsx conflict resolution (teammate
rewrote App.tsx with live room view / one-click join).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sb-iam
2026-06-27 17:34:02 -07:00
parent 1294b8428d
commit 546aeeed70
11 changed files with 892 additions and 7 deletions
+18
View File
@@ -18,6 +18,7 @@ import {
seedDefaultPods,
} from './pods/store.js';
import { getPresence } from './livekit/rooms.js';
import { loadPodGraph, reachFrom } from './graph/store.js';
import type { InterventionOutcome } from '@podman/shared';
const app = express();
@@ -137,6 +138,23 @@ app.delete('/api/pods/:id/members/:name', async (req, res) => {
res.json(pod);
});
// --- Continual-learning graph (team_model view) ---
app.get('/api/pods/:id/graph', async (req, res) => {
try {
res.json(await loadPodGraph(req.params.id));
} catch (e) {
res.status(500).json({ error: (e as Error).message });
}
});
app.get('/api/pods/:id/graph/reach/:node', async (req, res) => {
try {
res.json(await reachFrom(req.params.id, req.params.node));
} catch (e) {
res.status(500).json({ error: (e as Error).message });
}
});
const http = createServer(app);
// ws relay: the agent pushes collision/intervention JSON here; PWAs subscribed by pod receive it.