skip to content
Jobs

A Live Countdown for a Parked Run

Turning a usage-limit wait from a stale corner note into a card that counts down to the resume time.

Gantry sprints
Turning a usage-limit wait from a stale corner note into a card that counts down to the resume time.

When an agent hits a provider usage limit mid-run, the work can't continue until the limit resets — so the run sleeps and tries again at a specific wall-clock time. Gantry already computed how long to wait, but it showed that wait as a dim note tucked into the corner of the stage card that triggered it: a card that still looked busy, carrying a fixed duration that went stale the moment it was drawn. A person glancing at the screen couldn't tell whether the wait was nearly over or had barely begun.

This job turned that wait into something you can watch. A parked run now gets its own card in the activity feed, marked with a pause glyph, showing the local time it will resume and a remaining-time figure that decreases once a second until it reads "resuming…". The wait stopped being a footnote and became the clearest thing on the screen.

Build

The work was cut into three layers — the wake-time data, the card's state lifecycle, and the on-screen countdown — so each could be proven on its own test surface before the next depended on it.

The reset-wait already knew how long to sleep, but nothing downstream knew the absolute instant the run would wake — and a countdown needs a fixed point to count toward, or every frame reads a slightly different remaining time. The first piece added that instant as data and threaded it through the non-visual layers: the engine that decides the wait, the event it emits, the history round-trip, and a pure text formatter. Because none of that touches the terminal, it could be written and checked with plain assertions, no card in existence yet.

The second piece owned the terminal state only — creating one keyed card per wait, folding repeated retry events into it instead of spawning a card per tick, and closing it honestly whether the run resumed, was aborted, or exhausted its wait budget. Its brief made the reason for the split explicit: the lifecycle could be exercised against application state before any pixel was drawn. The third piece drew the card and made it move.

Where the difficulty actually lived was the local clock. The `time` crate refuses to read the host's local offset once the process has spawned threads, and quietly returns UTC instead — so a naive render-time lookup would show the resume time in the wrong zone. The plan confined that hazard to the render layer, along with its answer: capture the offset once at startup, on the main thread, before any worker exists, and pass it down. No gate sent a sprint back; a re-plan ran after each of the first two reviews and settled without forcing a sprint to be rebuilt, so the layered cut held under whatever those reviews learned.

Feature

Before, a usage-limit wait was a muted note on a card that still looked like it was working; now it is a first-class card that counts down to the resume time and closes with an honest summary.

Before this job, a parked run advertised itself as a stale duration wedged into the note of a card that still rendered as Running — the most important fact, that the run was asleep until a known time, was the least visible thing about it. Nothing in the terminal state could even express "parked": a card was working, ok, bad, or a note, and none of those fit a run that is neither busy nor done.

The design adds a distinct Waiting status and a small block of reset-wait metadata — the wake instant and the human wait phrase — that only a waiting card carries. A live card counts down to that instant each frame; when the moment passes it reads "resuming…" rather than a negative timer; a wait with no parseable reset time still gets the card but no fabricated clock; and a wait replayed from history renders already-closed rather than resurrecting as a phantom live timer.

Standing in the tree today, the whole design is still here and still documented: the Waiting status, the countdown formatter, the keyed one-card-per-wait lifecycle, and its resolve-on-resume, resolve-on-abort, and resolve-on-exhaustion paths. One piece has drifted — the startup offset capture no longer sits in the program's main entry point but in the TUI launch path, folded there when the offset helper moved into the domain module — yet it still fires before any worker thread is spawned, so the property the plan cared about survives the move.

unavailable sessions
unavailable context
63,409 in / 177,046 out tokens
$26.34 cost
0 x 3 milestones x sprints
unavailable edits
unavailable commands
53m duration
unavailable roles
0 x 0 fixes x replans
- harness