PLAN.md
c24. Synthetic Live Session Replay Plan
Goal
Simulate one "live" agent session in qmap over 10 seconds using synthetic data first, then preserve the same playback system so real session data can drop in later with minimal UI churn.
Canonical demo:
- agent initializes
- research phase touches 24 HTTP URLs
- execution phase uses 4 MCP servers
- edit phase touches 6 files
This should feel live, not like a static fixture swap. Actors and flows should accumulate over time, phase changes should read clearly, and playback should be deterministic enough for repeated visual tuning.
Recommended Product Shape
Add a third source mode wherever this replay lands:
synthetic— current stable fixturesreplay— synthetic live session over 10 secondslive— real session feed when available
The replay mode should not hard-code animation inside the Vue view. Instead, build a small playback stack with three layers.
Three-Layer Architecture
1. Scenario Spec
Human-authored semantic intent. This is where we describe the story we want to see.
Suggested shape:
{
id: "agent-research-edit-10s",
durationMs: 10_000,
seed: 24,
actors: {
user: { id: "user-mark", type: "user", label: "Mark" },
agent: { id: "agent-codex", type: "agent", label: "Codex" },
mcpServers: [
{ id: "mcp-brave", type: "mcp-server", label: "Brave Search MCP" },
{ id: "mcp-github", type: "mcp-server", label: "GitHub MCP" },
{ id: "mcp-filesystem", type: "mcp-server", label: "Filesystem MCP" },
{ id: "mcp-docs", type: "mcp-server", label: "Docs MCP" }
]
},
phases: [
{ id: "init", startMs: 0, endMs: 1000 },
{ id: "research", startMs: 1000, endMs: 4500, urls: 24 },
{ id: "edit", startMs: 4500, endMs: 9000, mcpServers: 4, files: 6 },
{ id: "finalize", startMs: 9000, endMs: 10_000 }
]
}
2. Timeline Compiler
Transforms the scenario spec into timestamped replay events and snapshot checkpoints.
Suggested event types:
actor.revealactor.stateflow.bumpphase.startphase.endfocus.setmeta.update
Suggested emitted shape:
{
atMs: 3125,
kind: "flow.bump",
payload: {
origin: "mcp-brave",
destination: "ext-openai-docs",
amount: 1
}
}
The compiler should also produce snapshots on a fixed cadence, for example every 100ms, so playback is smooth but deterministic.
3. Playback Source
Consumes the compiled timeline and exposes the same kind of session object the view already understands.
Suggested interface:
const replay = createSessionReplay({
scenario,
snapshotMs: 100,
});
replay.play();
replay.pause();
replay.seek(4500);
replay.subscribe(({ nowMs, session, phase, delta }) => {});
The key requirement is that the playback source emits normal qmap session data, not special rendering commands.
Canonical 10-Second Storyboard
Phase 1: Init (0ms - 1000ms)
- reveal
UserandAI Agent - show initial invoke flow from user to agent
- keep MCP/file/host rugs empty at first
- optionally pre-register hidden actors so later reveals do not cause violent layout churn
Phase 2: Research (1000ms - 4500ms)
- reveal two research MCPs first:
Brave Search MCP,Docs MCP - fan out to 24 URL touches across roughly
8-12distinct hosts - spread touches across the window so flows visibly accumulate instead of arriving in one burst
- let the research hosts appear lazily as first touched
Phase 3: Edit (4500ms - 9000ms)
- reveal remaining MCPs:
Filesystem MCP,GitHub MCP - reveal 6 files gradually rather than all at once
- start with reads, then transition a subset to writes so the file-access grouping remains meaningful
- accumulate MCP -> file and MCP -> host flows alongside agent -> MCP flows
Phase 4: Finalize (9000ms - 10_000ms)
- stop introducing new actors
- continue a few final flow bumps
- settle on a readable end state with the hottest MCPs and touched files clearly visible
- expose summary metadata such as total URLs, total files, total MCPs used
Tricky Details To Work Out Early
Layout Churn
- keep actor ids stable for the whole replay
- optionally pre-seed hidden actors from frame 0
- distinguish
revealedfrompresentso layout slots can remain stable
Flow Identity
- compile one deterministic flow id per logical connection
- update
volumeover time - optionally track
lastTouchedAtMsfor pulse styling
Arrow Persistence During Replay
- selection-owned arrows currently rebuild on every replay snapshot, even when the logical flow still exists
- this creates a visible blink in
c24because replay emits frequent session snapshots and re-selects the root agent after transitions - treat selected-flow arrows as a reconciled set keyed by logical flow identity, not as disposable per-refresh decoration
Investigation: Why The Arrows Blink
c24drivesQMapAgentSessionwith a continuously updatingsessionplusdataflows, and re-applies root-agent selection on ready/transition end so the agent-centered arrows remain visibleuseSceneReactivity()updatescurrentDataflowsand then always callsrefreshSelection()after each reactive data change, including the fast path where there are no actor/rug layout changesrefreshSelection()callsapplySelection(), which callscreateFlowsForActor()createFlowsForActor()begins withdisposeFlows(), removing every current arrow mesh before recreating all connected flows fromcurrentDataflows- result: any new replay snapshot tears down all animating arrows first, so even unchanged flows disappear for a frame and then reappear
Targeted Plan: Only Remove Arrows Missing From The Next Map
1. Lock Selection Flow Identity
- define a stable selection-flow key using
dataflow.idwhen present, with a deterministic fallback likeorigin|destination|protocol - use that key consistently for the selection overlay so we can compare the current rendered set against the next snapshot
2. Replace Full Rebuild With Reconciliation
- refactor
useActorSelection()so selection flows are stored in a keyed map instead of a single disposable array - when selection refreshes, compute
nextConnectedFlowsfor the selected actor and diff them against the rendered flow map - remove only flows whose key is absent from the next set
- create flows only for newly added keys
- keep existing flow instances alive for persisted keys
3. Update Persisted Flows In Place
- for a persisted flow, update positions from current actor meshes instead of recreating the
LineFlow - if replay changes mutable flow attributes such as
volume,status, orprotocol, either:- add a small
updateFlowData()path onLineFlow, or - recreate only that one changed flow while leaving the rest untouched
- add a small
- continue to apply
flowConfigwatcher updates to surviving flows so tuning changes still propagate live
4. Keep Full Disposal For Real Removals
- preserve the existing
disposeFlows()behavior for deselection, actor removal, or full scene clear - if the selected actor disappears from the next snapshot, clear all selection flows exactly as today
5. Verify Against Replay Cases
- confirm that a snapshot containing the same logical agent-connected flows no longer causes arrow blink
- confirm that adding one new flow introduces only that arrow
- confirm that removing one flow disposes only that arrow
- confirm that actor exits during transitions still clean up affected flows safely
Timing Determinism
- deterministic seed for host ordering and event spacing
- wall-clock playback driven by
requestAnimationFrame - snapshot generation based on virtual clock, not render FPS
Real-Data Compatibility
- keep replay inputs event-oriented
- keep replay outputs session-shaped
- write adapters rather than mixing reducer logic into the Vue component
Suggested File-Level Split
shared/replay/sessionReplayScenarios.jsshared/replay/compileSessionReplay.jsshared/replay/createSessionReplay.jsshared/replay/replaySessionAdapters.js
Implementation Order
Phase A: Data Contract
- lock the scenario and replay event shapes
- author the canonical 10-second scenario
- decide whether hidden pre-seeded actors are part of the contract
Phase B: Deterministic Compiler
- compile the scenario into timed events
- emit snapshots every
100ms - confirm the last snapshot is identical on every run
Phase C: UI Playback Controls
- add
replayas a source mode - add play, pause, restart, scrub, and
1x/2xspeed controls - show active phase and elapsed time in the header
Phase D: Integration and Recommendation
- run the replay against one experiment surface
- verify phase readability, layout behavior, and flow accumulation
- decide whether any replay seam deserves promotion into
src/