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:
@@ -0,0 +1,88 @@
|
||||
# Continual-Learning Graph Spec
|
||||
|
||||
> Owner: graph data + visualization. Status: demo-backed (live `team_model` reads land later).
|
||||
> Satisfies the documentation-first gate for the `backend/src/graph/*` and
|
||||
> `frontend/src/components/GraphView.tsx` files.
|
||||
|
||||
## What this is (and is NOT)
|
||||
|
||||
PodMan's **visible "it learned" surface**. The graph is a render of the per-pod
|
||||
`team_model` — who **owns / edits** which files, where work **collides**, and what
|
||||
PodMan **learned** from accepted interventions (the `learned_from` edges). It is the
|
||||
continual-learning loop made legible in 10 seconds: _"PodMan now knows Karti owns auth."_
|
||||
|
||||
It is **not** a generic analytics dashboard (judges down-rank dashboard-as-product). It is a
|
||||
**secondary view** behind the pods list — opened from a header toggle — that exists to make
|
||||
the self-improving loop _visible_ during the demo. The landing surface stays the pods list.
|
||||
|
||||
## Data model — graph as a view of `team_model`
|
||||
|
||||
The graph lives in two places, both keyed by `podId`:
|
||||
|
||||
1. **Embedded (served to the viz):** the `team_model` document carries a `graph` field:
|
||||
|
||||
```ts
|
||||
// team_model doc (one per pod, unique index { podId: 1 })
|
||||
{ podId, graph: PodGraph, updatedAt }
|
||||
```
|
||||
|
||||
`GET /api/pods/:podId/graph` returns `team_model.graph`, or a demo graph when none exists yet.
|
||||
|
||||
2. **Normalized (for traversal):** the same nodes/edges are mirrored into two collections so
|
||||
the model can be walked with MongoDB `$graphLookup` (the graph-database pattern):
|
||||
|
||||
| Collection | Doc shape (`shared/src/graph.ts`) | Index |
|
||||
| ------------- | ----------------------------------------- | ---------------------------- |
|
||||
| `graph_nodes` | `GraphNodeDoc` = `PodGraphNode` + `podId` | `{ podId: 1, id: 1 }` unique |
|
||||
| `graph_edges` | `GraphEdgeDoc` = `PodGraphEdge` + `podId` | `{ podId: 1, source: 1 }` |
|
||||
|
||||
Node kinds: `engineer · feature · file · collision · intervention`.
|
||||
Edge kinds: `owns · editing · touches · collides · warns · learned_from`.
|
||||
The `learned_from` edges are the continual-learning signal — derived from accepted
|
||||
`interventions` / `outcomes` (ownership PodMan retains across sessions).
|
||||
|
||||
### Traversal (`$graphLookup`)
|
||||
|
||||
Walk the directed edge chain from any node (e.g. engineer → file → collision → intervention):
|
||||
|
||||
```ts
|
||||
db.collection('graph_edges').aggregate([
|
||||
{ $match: { podId, source: startNodeId } },
|
||||
{
|
||||
$graphLookup: {
|
||||
from: 'graph_edges',
|
||||
startWith: '$target',
|
||||
connectFromField: 'target',
|
||||
connectToField: 'source',
|
||||
as: 'reaches',
|
||||
restrictSearchWithMatch: { podId },
|
||||
},
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
This answers "what does this engineer's edit reach?" — the risk path PodMan lights up.
|
||||
|
||||
## API
|
||||
|
||||
| Method | Route | Returns |
|
||||
| ------ | ---------------------------------- | --------------------------------------------- |
|
||||
| `GET` | `/api/pods/:podId/graph` | `PodGraph` (live `team_model`, demo fallback) |
|
||||
| `GET` | `/api/pods/:podId/graph/reach/:id` | `$graphLookup` reachability from node `:id` |
|
||||
|
||||
Additive routes in `backend/src/server.ts` (shared file — additive only).
|
||||
|
||||
## Files
|
||||
|
||||
- `shared/src/graph.ts` — `PodGraph`, `PodGraphNode/Edge/Metric`, `GraphNodeDoc`, `GraphEdgeDoc`
|
||||
- `backend/src/graph/demo.ts` — `createDemoPodGraph(podId)` (grounded in the demo-pod crew)
|
||||
- `backend/src/graph/store.ts` — `loadPodGraph`, `seedGraph`, `reachFrom` (`$graphLookup`)
|
||||
- `backend/src/graph/seed.ts` — `pnpm graph:seed` (writes demo into `team_model` + graph collections)
|
||||
- `frontend/src/lib/graph.ts` — `fetchPodGraph(podId)`
|
||||
- `frontend/src/components/GraphView.tsx` — dark-Bauhaus SVG graph (toggle from `App.tsx`)
|
||||
|
||||
## Demo-first plan
|
||||
|
||||
1. Serve `createDemoPodGraph()` from the route (demo-stable, no DB dependency on the demo path).
|
||||
2. `pnpm graph:seed` writes the same graph into Mongo so `$graphLookup` is real, not a mock.
|
||||
3. Swap `loadPodGraph` to read live `team_model.graph` once the ingest pipeline populates it.
|
||||
Reference in New Issue
Block a user