UI composes from the ruixen registry (npx shadcn add ruixen.com/r/[component]) via @/components/ui/* + design tokens; only the SVG canvas is bespoke. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.5 KiB
Continual-Learning Graph Spec
Owner: graph data + visualization. Status: demo-backed (live
team_modelreads land later). Satisfies the documentation-first gate for thebackend/src/graph/*andfrontend/src/components/GraphView.tsxfiles.
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:
-
Embedded (served to the viz): the
team_modeldocument carries agraphfield:// team_model doc (one per pod, unique index { podId: 1 }) { podId, graph: PodGraph, updatedAt }GET /api/pods/:podId/graphreturnsteam_model.graph, or a demo graph when none exists yet. -
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_nodesGraphNodeDoc=PodGraphNode+podId{ podId: 1, id: 1 }uniquegraph_edgesGraphEdgeDoc=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):
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,GraphEdgeDocbackend/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 intoteam_model+ graph collections)frontend/src/lib/graph.ts—fetchPodGraph(podId)frontend/src/components/GraphView.tsx— shadcn-themed SVG graph (theme-aware; toggle fromApp.tsx)
Demo-first plan
- Serve
createDemoPodGraph()from the route (demo-stable, no DB dependency on the demo path). pnpm graph:seedwrites the same graph into Mongo so$graphLookupis real, not a mock.- Swap
loadPodGraphto read liveteam_model.graphonce the ingest pipeline populates it.
Component convention
UI is built from the shared shadcn / ruixen registry — add primitives with
npx shadcn@latest add "https://ruixen.com/r/[component]" and compose from
@/components/ui/* (Button, Badge, Card, …) using the design tokens
(var(--card) / --foreground / --border / …). Only the SVG node-link canvas
in GraphView.tsx is bespoke (3 SVG-only CSS rules); the chrome (header, toggles,
metric cards, detail panel, legend) is composed from the primitives + the app's
Tailwind utility patterns. No hand-rolled component stylesheets.