skip to content
Jobs

Rewatch Any Finished Gantry Run

A durable event tape and a replay verb that play a past run back through the live terminal view

Gantry milestones
A durable event tape and a replay verb that play a past run back through the live terminal view

Gantry shows a build as it happens: a live terminal view animates every event a run emits — agents starting, tests going red, streaming output — and `gantry --demo` plays a fictional run through that same view. But once a real run finished, that stream was gone. The rich, byte-for-byte record of events lived only on the run's attach socket and in memory, and it died with the process that produced it. What stayed on disk was a deliberately lossy journal: enough to resume a run, not enough to watch one.

This job made finished runs watchable again. It gives every run a durable event tape — an append-only file that records each event without losing detail — and adds a `gantry replay <name>` command that plays any finished, crashed, or merged run back through the unmodified live view, fast-forwarded so a multi-hour build takes a couple of minutes. When a run predates the tape, or its tape was deleted, replay falls back to the lossy journal rather than refusing, so the command always has something to show.

Build

Four sprints cut along one recorded event's path — write the tape, play it, expose the verb, fall back to the journal — each built against an interface that already existed. The stress landed at the boundary the plan expected to get for free: how an exhausted playback ends.

The decomposition follows the life of a recorded event. Sprint one persists the tape; sprint two builds a replay engine that reads it; sprint three exposes `gantry replay`; sprint four adds the journal fallback for runs with no tape. Ordering it that way gave each piece its input before it was built — the engine reuses the tape module's own decode helper, the verb drives the engine, and the fallback feeds the engine's pacing loop from a different source. Every brief also rests on the same reuse decision: the tape is the existing attach codec made durable rather than a second encoder, and the replay engine is one more implementation of the same `Engine` interface the demo's mock engine already satisfies, so the render path grows no playback-specific branch. That is what let a fresh agent build each piece against something already in the tree instead of inventing it.

The boundary that came under load was the ending. Sprint two's brief committed to a specific terminal behavior: when the tape runs out, idle with the event channel held open so the viewer can study the final frame. That held until sprint three wired the engine into the CLI, including the headless path that falls out of the engine being a real `Engine`. There the review learned the ending was wrong — a headless replay drains the channel until it disconnects, so an engine that idles with the channel open would hang `gantry replay --headless` forever. The review forced a re-plan, and the discipline was reversed: the playback thread now returns and drops the channel, and the view's own receive-and-keypress loop holds the resting frame.

That re-plan is where this run's difficulty actually lived. The bet that a replay engine is just another `Engine`, so headless composes at no cost, was mostly right — but "no cost" surfaced one behavior sprint two's tape-only view had no reason to anticipate, and only exercising the headless composition revealed it. Sprint four's brief was rewritten around the corrected ending before that sprint was built, so the fallback adopted the fix rather than repeating the bug. Everywhere else the cut held: the tape write rode the existing event pump, the delta-to-sleep math stayed an isolated pure function, and no other sprint was sent back.

Feature

Every run now writes a lossless per-run tape beside its journal, and `gantry replay <name>` plays any past run back through the untouched terminal view — dropping to the journal when no tape exists. The design still stands at HEAD, its code largely intact even as the pacing defaults were reworked afterward.

Before this job, a finished run could not be rewatched. The live detail died with the process, and the record left on disk kept only the lifecycle backbone — enough to resume, not enough to animate. Watching a build meant watching it once, in real time, as it ran.

The answer is three parts. The tape is an append-only `events.jsonl` sidecar, added to the checkout's git-exclude so it travels with the local run directory but never bloats the committed repo, and encoded with the same attach codec that already carried the live stream — one lossless encoding to maintain, not two. The replay engine reads a tape, or degraded the journal, and sends the recorded events down the same channel a live engine uses, paced by the timestamp gaps between them and clamped so a long idle stretch collapses to a moment. The verb resolves a run name the same forgiving way every run-management command does, finished runs included, then drives the engine through the ordinary front-end, so the TUI and the headless drain both work with no special casing.

Standing in the tree at today's HEAD, every file the job touched is still present, and both the tape and replay modules survive largely as written. The reuse held: the replay engine is still one more `Engine`, and the terminal discipline the re-plan installed is still in place, its comments still spelling out why dropping the channel is what keeps a headless replay from hanging. One thing has drifted — the pacing has been reworked since, so the default now plays at real time under a reshaped timeline instead of the original fixed fast-forward, and the README verb-table lines the job first wrote have been replaced in later documentation passes. The capability it delivered — a durable tape, a replay verb, a journal fallback — stands as built.

unavailable sessions
unavailable context
78,976 in / 198,255 out tokens
$29.54 cost
0 x 4 milestones x sprints
unavailable edits
unavailable commands
58m duration
unavailable roles
0 x 0 fixes x replans
- harness