feat(replay): the trajectory bar — always on top, collapsible, synced with the turns; the loop-row diagram removed - #104
Merged
Merged
Conversation
…n top, collapsible The strip used to appear only after pressing replay; an overview that must be asked for is not an overview. It now draws in the session view at all times (lanes, clock ruler, idle folded, thread focus), grows on every landed pair, and touching it enters replay at that moment. Replay's own chrome (loop row, transport, veil, playhead, slice) still waits for body.replaying. A chevron in the clock gutter folds the lanes to the clock row, persisted; replay unfolds. Spec: replay-stage.md rev 3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n's turns Eric's verdict on the shipped flowchart after real use: remove it. The loop row, its data layer (loopAt/nowAt/stateAt) and both its animations are deleted — a three-state machine never changes shape, so the drawing restated what the strip, the beat and the convo already say. Motion returns to one owner, the playhead. In its place the bar becomes the session's MINIMAP: every turn carries data-ts, a faint reading marker tracks the topmost visible turn as the reader scrolls (hidden while replaying), and a click outside replay jumps the conversation to that instant instead of entering replay. Spec: replay-stage.md 'Revision 4'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… like idle Eric on rev 4 in real use: waiting eats the strip; replay is broken — sessions not focused, the bar not following. Root cause of the second: with replay active the session rebuilds as of the cursor, and showSession re-resolved the selection against that cursored subset — before the selected thread's first response it fell back to newestMainThread, OVERWROTE sessionSelKey, and the strip's axis (the selected thread's extent) re-ruled itself to the stranger on every tick. - The selection pins for the whole replay: resolved once against the full capture; a cursor before the thread's first response renders "nothing on this thread's wire yet" instead of switching threads - enterReplay (reading pages) parks at the selected thread's last pair; rpHome() (⏮ / Home / play-at-the-end) seeks its first — a merged capture's tape edges can sit hours inside other sessions. ⏭/End stay the tape's live edge - ←/→ step the selected thread's own pairs; shift+arrow keeps every wire boundary - threadExtent: waiting spans stretch the extent but leave the busy union — a call-less harness wait compresses like idle (the dsh Trajectory tab's duration projection folds every uncovered stretch; ours keeps a threshold), RP_IDLE_MS 5m -> 2m - rpQueueSyncRead falls back to a direct call in a hidden tab (rAF never fires there; the read marker was one frame stale on return) Verified end-to-end in Chrome on a real 668-pair merged trace: axis stable through restart+play, scoped stepping, empty-state convo, clean exit, zero console errors. bun test 876/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ens, a vertical facet rail The Context view's details open in ONE place now: a right panel beside the deck, opened by a pick (an icicle node, a stream record, an event row), closed by × / Esc, absent otherwise. The window deck's pane sat UNDER the icicle in the same scroll and pushed the graph off the sheet; the stream's inspector was always there, 40% of the deck spent on "pick a record"; events had no detail at all. Inside, a VERTICAL rail of facets — the questions the wire can answer about the pick, and only those (a tab that would say n/a is not listed): content (the block rendered with the detail panel's own renderers, the picked fold open), schema (the tool's declaration in the carrying request, its weight and rank among that request's tools), origin (the step it entered with and the CARRY — how many requests re-sent it and what that weighs; exact through the pinned step for an icicle item, counted to the next compaction boundary and saying so for a record), wire (the carrying request in brief, both links out). Events open too: the injected text, a compaction's before → after by category, a bump's cause with its TTL gap or failed previous status. Esc peels the inspector first. The panel repaints alone on a pick (never the overview under the cursor); its scroll survives a same-pick live repaint and resets when the pick or facet changes. Also: an image-bearing tool result (a browser screenshot) had no label under its icicle group — only string content was read. The label reads the array now, [image] for the picture. Data layer: ctxCarrySpan in src/context.ts, pure, inlined, unit-tested. Grammar tests cover the panel, the rail and the events picks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The bar's human lane drew a 2px point per prompt: a target nobody could hit, unnumbered below full depth, saying nothing about the turn it started. It is the TURNS lane now: one clickable block per working loop, from the instant the prompt hit the wire (the block's accent edge, exactly where the point was) to the loop's last reply, numbered the way the rail numbers it. The number shows at any zoom the block can hold it (the number IS the point); the prompt's words wait for full depth. Hover gives the turn's tally — steps, calls, agents spawned, clock and duration, the prompt, compactions carried and failed requests that fell inside it — all from the lanes already built. Click jumps the conversation to the turn's HEAD (a block carries data-rpj, the prompt's own instant; the seek time would land on the first reply); replaying, it seeks where the prompt became visible like every span. The block under the conversation's reading position lights as you scroll — the sync, on the block itself. A harness-started loop is a turn too, drawn faint and saying who started it; subagent loops stay on the agents lane. lanes.human stays as data: the thread extent and soFar read it. Data: sessionLanes().turns in src/replay.ts, unit-tested; the strip grammar tests name the new lane. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Items from Eric's 2026-08-31/09-01 batch: "make the session's trajectory bar always on top (collapsible of course); not only for the replay" — then, on the shipped 0.46.0 flowchart: "remove the diagram; it's shit and useless; figure it out like the trajectory bar should sync with the session turns."
Revision 3 — the bar is the overview (first commit)
#replay-baris FRAME in the session view at all times: the strip (five lanes over wall-clock, clock ruler, idle folded to ⧸⧸ breaks, thread focus, live open-request stub) draws without a replay running and grows on every landed pair.body.replaying.cctrace-traj-fold); replay overrides the fold. A sessionless trace hides the bar.Revision 4 — the diagram goes, the bar becomes the minimap (second commit)
#rp-now, its data layer (loopAt/nowAt/stateAt), the.rl-*CSS and both animations (−860 lines net). The autopsy is in the spec: a three-state machine never changes shape, so after one glance the drawing carried no information the strip, the beat and the conversation don't state better. The motion budget returns to ONE owner, the playhead.data-ts; a faint reading marker (#rp-read, the playhead's quiet twin, hidden while replaying) tracks the topmost visible turn as the reader scrolls (rAF-throttled); a click on the bar outside replay jumps the conversation to the turn at that instant. Clicking no longer enters replay — an overview click is navigation, not a mode switch; ⏵/Space/arrows still replay.Revision 5 — replay holds the reader's thread; waiting folds (third commit)
Eric on rev 4 in real use: waiting ate the strip; "replay is like broken now, sessions not focused, the nav bar not following".
newestMainThreadof whatever existed at the cursor, overwrotesessionSelKey, and the strip's axis re-ruled itself to the stranger on every tick. The intended thread now resolves ONCE against the full capture; a cursor before its first response renders "nothing on this thread's wire yet at this moment" instead of switching threads. The playhead crosses one stable ruler.threadExtent: waiting stretches the extent, never the busy union — nobody worked) andRP_IDLE_MSdrops 5m → 2m. On the real trace a ~45-minute wait went from holding half the strip to a ⧸⧸ break; the work now fills the frame. Borrowed from dsh's Trajectory tab duration projection, which compresses every uncovered stretch; ours keeps the threshold so short waits still read as time.Spec: "Revision 5: replay holds the reader's thread" in replay-stage.md. Verified end-to-end in Chrome on the 668-pair merged trace (axis stable through restart+play, scoped stepping, empty-state convo, clean exit, zero console errors); bun test 876/0.
Spec
docs/design/replay-stage.md — "Revision 3: the strip is the overview" + "Revision 4: the diagram goes, the bar becomes the minimap" (with the loop row's autopsy for the next diagram idea to read first).
Verification
bun test876/0 (loop-row tests removed with the feature; new: turns carry data-ts, the reading marker exists, a bar click outside replay does not enter replay, the fold survives re-renders, a landed pair grows the strip without replay).convoScrollTowith the right turn target and never entered replay (instrumented); ⏵ still enters replay with strip + transport and the stage — no loop row anywhere; zero console errors.🤖 Generated with Claude Code