feat(graph): dynamic force-directed Team-memory graph + honest metrics (on main's pipeline)
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>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import type { PodLearningLoop } from '@podman/shared';
|
||||
import { BLUE } from './encoding.js';
|
||||
|
||||
/**
|
||||
* The continual-learning loop rail: observe → store → predict → outcome → adapt.
|
||||
* The active stage (most-recent activity) gets a pulsing accent bar + ring.
|
||||
*/
|
||||
export function LearningLoop({ loop }: { loop: PodLearningLoop }) {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
||||
Learning loop
|
||||
</p>
|
||||
{loop.steps.map((s, i) => {
|
||||
const active = s.status === 'active' || s.key === loop.activeStep;
|
||||
return (
|
||||
<div key={s.key}>
|
||||
<div
|
||||
className="relative overflow-hidden rounded-lg border bg-card py-2 pl-3.5 pr-3 shadow-sm transition-colors data-[active=true]:bg-accent/40"
|
||||
data-active={active}
|
||||
style={active ? { boxShadow: `inset 0 0 0 1px ${BLUE}55` } : undefined}
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
className={`absolute inset-y-0 left-0 w-1 ${active ? 'pm-pulse' : ''}`}
|
||||
style={{ background: active ? BLUE : 'var(--border)' }}
|
||||
/>
|
||||
<div className="flex items-baseline justify-between gap-2">
|
||||
<p className="text-[0.7rem] font-medium uppercase tracking-wide text-muted-foreground">
|
||||
<span className="tabular-nums">{String(i + 1).padStart(2, '0')}</span> {s.label}
|
||||
</p>
|
||||
<p className="font-heading text-sm font-semibold tabular-nums">{s.value}</p>
|
||||
</div>
|
||||
<p className="mt-0.5 text-xs leading-snug text-muted-foreground">{s.detail}</p>
|
||||
</div>
|
||||
{i < loop.steps.length - 1 && (
|
||||
<p
|
||||
aria-hidden
|
||||
className="py-0.5 text-center text-xs leading-none text-muted-foreground/60"
|
||||
>
|
||||
↓
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user