b8b6b16531
Lands the dynamic Team-memory redesign on top of main's continual-learning pipeline. main already computes loop/activity in the API but its GraphView never rendered them and kept a static-column graph; this swaps in the dynamic graph and surfaces the rails, reusing main's richer PodLearningLoop / PodGraphActivity types. Frontend (new frontend/src/components/graph/*, composed into GraphView.tsx): - forceSim.ts: dependency-free force layout (charge, link springs, centroid recenter, 2-pass collision, bounds clamp, alpha anneal) — no new deps/lockfile churn. - GraphCanvas.tsx: SVG render from the sim — draggable + pinnable nodes, curved edges, weight-sized shapes, fade-in, animated learned_from dash, risk-path lighting / rest dimmed, label collision-avoidance. - MetricsRail / LearningLoop / ActivityStream / SelectedNodePanel / encoding.ts: the mock's rails + stream + detail panel in light shadcn. LearningLoop consumes main's PodLearningLoop (steps + activeStep); ActivityStream consumes PodGraphActivity (title + detail). SelectedNodePanel adds a Flow section that narrates the path through a clicked node (flowNarrative); default copy is mode-aware. Edge legend rounded out (editing/touches). - GraphView polls every 5s and diffs (positions preserved), + ws /api/events nudge. Stale selection (node gone across a poll) dropped so the canvas can't dim entirely. Backend (surgical — main's materializer + buildLoop kept): - live.ts: headline metric cards derived from the FINAL de-noised graph (Open risk paths = distinct collision files; Learned owners = distinct owner engineers) instead of raw collision-signature / accepted-outcome counts that inflate with test churn (50 -> 4 risk files, 16 -> 1 owner on live). buildLoop untouched. - demo.ts: metrics realigned to the demo graph (3 owners / 1 risk path / 100%). Verified: lint + -r typecheck + -r build pass; Playwright confirmed the dynamic graph (ticks on load, draggable), the loop rail (5 steps, ADAPT active) and activity stream rendering main's shapes, honest metrics (3/1/100%), and the flow narrative per node kind — zero page errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
320 lines
8.0 KiB
TypeScript
320 lines
8.0 KiB
TypeScript
import type { PodGraph } from '@podman/shared';
|
|
|
|
/**
|
|
* Demo continual-learning graph, grounded in the demo-pod crew. Used as the
|
|
* served graph until the ingest pipeline populates `team_model.graph`, and as
|
|
* the seed for the `graph_nodes` / `graph_edges` collections. The hero path —
|
|
* Karti + Yahya editing auth.ts -> collision -> sync PR -> *learned: Karti owns
|
|
* auth* — is the continual-learning story the demo lights up.
|
|
*/
|
|
export function createDemoPodGraph(podId: string): PodGraph {
|
|
return {
|
|
podId,
|
|
generatedAt: new Date().toISOString(),
|
|
// Kept consistent with the graph below (3 owner engineers, 1 collision file,
|
|
// 1 of 1 interventions accepted) so the numbers never contradict the picture.
|
|
metrics: [
|
|
{
|
|
label: 'Learned owners',
|
|
value: '3',
|
|
detail: 'Distinct owners retained from accepted interventions.',
|
|
},
|
|
{
|
|
label: 'Open risk paths',
|
|
value: '1',
|
|
detail: 'File with two or more converging editors.',
|
|
},
|
|
{
|
|
label: 'Accept rate',
|
|
value: '100%',
|
|
detail: 'Interventions accepted vs total this session.',
|
|
},
|
|
],
|
|
loop: {
|
|
activeStep: 'adapt',
|
|
steps: [
|
|
{
|
|
key: 'observe',
|
|
label: 'Observe',
|
|
value: '2',
|
|
detail: 'Screen context and local git state show two active editors.',
|
|
status: 'complete',
|
|
},
|
|
{
|
|
key: 'store',
|
|
label: 'Store',
|
|
value: '7',
|
|
detail: 'Observations, collisions, interventions, and outcomes are in MongoDB.',
|
|
status: 'complete',
|
|
},
|
|
{
|
|
key: 'predict',
|
|
label: 'Predict',
|
|
value: '2',
|
|
detail: 'Same-file risk paths are detected before push.',
|
|
status: 'complete',
|
|
},
|
|
{
|
|
key: 'outcome',
|
|
label: 'Outcome',
|
|
value: '6',
|
|
detail: 'Accepted and dismissed outcomes supervise future routing.',
|
|
status: 'complete',
|
|
},
|
|
{
|
|
key: 'adapt',
|
|
label: 'Adapt',
|
|
value: '1',
|
|
detail: 'Accepted real collision created a learned_from edge.',
|
|
status: 'complete',
|
|
},
|
|
],
|
|
},
|
|
activity: [
|
|
{
|
|
id: 'demo-learned-auth',
|
|
at: new Date().toISOString(),
|
|
kind: 'learned',
|
|
title: 'Learned Karti owns auth.ts',
|
|
detail: 'Accepted sync PR outcome created a durable learned_from path.',
|
|
nodeId: 'engineer:karti',
|
|
edgeId: 'e7',
|
|
},
|
|
{
|
|
id: 'demo-intervention-sync-pr',
|
|
at: new Date().toISOString(),
|
|
kind: 'intervention',
|
|
title: 'Intervention: sync PR',
|
|
detail: 'PodMan offered a small coordination card before voice.',
|
|
nodeId: 'intervention:sync-pr',
|
|
},
|
|
{
|
|
id: 'demo-collision-auth',
|
|
at: new Date().toISOString(),
|
|
kind: 'collision',
|
|
title: 'Collision risk on auth.ts',
|
|
detail: 'Karti and Yahya converged on unpushed work.',
|
|
nodeId: 'collision:auth',
|
|
},
|
|
],
|
|
nodes: [
|
|
{
|
|
id: 'engineer:shakthi',
|
|
kind: 'engineer',
|
|
label: 'Shakthi',
|
|
summary: 'Owns the team-memory graph + visualization.',
|
|
weight: 0.8,
|
|
status: 'active',
|
|
x: 78,
|
|
y: 70,
|
|
},
|
|
{
|
|
id: 'engineer:karti',
|
|
kind: 'engineer',
|
|
label: 'Karti',
|
|
summary: 'Learned owner of auth; backend + DB wiring.',
|
|
weight: 0.9,
|
|
status: 'learned',
|
|
x: 78,
|
|
y: 188,
|
|
},
|
|
{
|
|
id: 'engineer:yahya',
|
|
kind: 'engineer',
|
|
label: 'Yahya',
|
|
summary: 'Editing auth.ts with unpushed changes.',
|
|
weight: 0.82,
|
|
status: 'risk',
|
|
x: 78,
|
|
y: 300,
|
|
},
|
|
{
|
|
id: 'engineer:ramis',
|
|
kind: 'engineer',
|
|
label: 'Ramis',
|
|
summary: 'On the memory store + agent pipeline.',
|
|
weight: 0.66,
|
|
status: 'stable',
|
|
x: 78,
|
|
y: 404,
|
|
},
|
|
{
|
|
id: 'engineer:zander',
|
|
kind: 'engineer',
|
|
label: 'Zander',
|
|
summary: 'Owns the realtime pod + capture.',
|
|
weight: 0.6,
|
|
status: 'stable',
|
|
x: 214,
|
|
y: 440,
|
|
},
|
|
{
|
|
id: 'file:auth.ts',
|
|
kind: 'file',
|
|
label: 'auth.ts',
|
|
summary: 'Hot file — two live editors before push.',
|
|
weight: 0.92,
|
|
status: 'risk',
|
|
x: 300,
|
|
y: 150,
|
|
},
|
|
{
|
|
id: 'file:memory-store',
|
|
kind: 'file',
|
|
label: 'memory/store',
|
|
summary: 'Shared memory API surface.',
|
|
weight: 0.72,
|
|
status: 'active',
|
|
x: 250,
|
|
y: 330,
|
|
},
|
|
{
|
|
id: 'feature:continual-memory',
|
|
kind: 'feature',
|
|
label: 'continual memory',
|
|
summary: 'Atlas team_model + Voyage recall.',
|
|
weight: 1,
|
|
status: 'active',
|
|
x: 442,
|
|
y: 300,
|
|
},
|
|
{
|
|
id: 'feature:realtime-pod',
|
|
kind: 'feature',
|
|
label: 'realtime pod',
|
|
summary: 'LiveKit screen stream in.',
|
|
weight: 0.8,
|
|
status: 'active',
|
|
x: 360,
|
|
y: 418,
|
|
},
|
|
{
|
|
id: 'collision:auth',
|
|
kind: 'collision',
|
|
label: 'auth.ts overlap',
|
|
summary: 'Karti + Yahya editing auth.ts, unpushed — git cannot see it.',
|
|
weight: 0.95,
|
|
status: 'risk',
|
|
x: 440,
|
|
y: 118,
|
|
},
|
|
{
|
|
id: 'intervention:sync-pr',
|
|
kind: 'intervention',
|
|
label: 'sync PR',
|
|
summary: 'PodMan offered to open a draft sync PR.',
|
|
weight: 0.7,
|
|
status: 'learned',
|
|
x: 622,
|
|
y: 118,
|
|
},
|
|
],
|
|
edges: [
|
|
{
|
|
id: 'e1',
|
|
source: 'engineer:karti',
|
|
target: 'file:auth.ts',
|
|
kind: 'owns',
|
|
label: 'owns',
|
|
strength: 0.9,
|
|
},
|
|
{
|
|
id: 'e2',
|
|
source: 'engineer:yahya',
|
|
target: 'file:auth.ts',
|
|
kind: 'editing',
|
|
label: 'edits',
|
|
strength: 0.8,
|
|
},
|
|
{
|
|
id: 'e3',
|
|
source: 'engineer:karti',
|
|
target: 'collision:auth',
|
|
kind: 'collides',
|
|
label: 'in',
|
|
strength: 0.85,
|
|
},
|
|
{
|
|
id: 'e4',
|
|
source: 'engineer:yahya',
|
|
target: 'collision:auth',
|
|
kind: 'collides',
|
|
label: 'in',
|
|
strength: 0.85,
|
|
},
|
|
{
|
|
id: 'e5',
|
|
source: 'file:auth.ts',
|
|
target: 'collision:auth',
|
|
kind: 'touches',
|
|
label: 'hot',
|
|
strength: 0.7,
|
|
},
|
|
{
|
|
id: 'e6',
|
|
source: 'collision:auth',
|
|
target: 'intervention:sync-pr',
|
|
kind: 'warns',
|
|
label: 'routes',
|
|
strength: 0.9,
|
|
},
|
|
{
|
|
id: 'e7',
|
|
source: 'intervention:sync-pr',
|
|
target: 'engineer:karti',
|
|
kind: 'learned_from',
|
|
label: 'learned: owns auth',
|
|
strength: 0.6,
|
|
},
|
|
{
|
|
id: 'e8',
|
|
source: 'engineer:ramis',
|
|
target: 'file:memory-store',
|
|
kind: 'editing',
|
|
label: 'edits',
|
|
strength: 0.7,
|
|
},
|
|
{
|
|
id: 'e9',
|
|
source: 'engineer:karti',
|
|
target: 'file:memory-store',
|
|
kind: 'editing',
|
|
label: 'edits',
|
|
strength: 0.5,
|
|
},
|
|
{
|
|
id: 'e10',
|
|
source: 'file:memory-store',
|
|
target: 'feature:continual-memory',
|
|
kind: 'touches',
|
|
label: 'persists',
|
|
strength: 0.8,
|
|
},
|
|
{
|
|
id: 'e11',
|
|
source: 'engineer:shakthi',
|
|
target: 'feature:continual-memory',
|
|
kind: 'owns',
|
|
label: 'models',
|
|
strength: 0.85,
|
|
},
|
|
{
|
|
id: 'e12',
|
|
source: 'engineer:zander',
|
|
target: 'feature:realtime-pod',
|
|
kind: 'owns',
|
|
label: 'owns',
|
|
strength: 0.75,
|
|
},
|
|
{
|
|
id: 'e13',
|
|
source: 'feature:realtime-pod',
|
|
target: 'file:memory-store',
|
|
kind: 'touches',
|
|
label: 'feeds',
|
|
strength: 0.55,
|
|
},
|
|
],
|
|
};
|
|
}
|