Problem
The cockpit's live-progress table (issue #92/#114) shows a role/task/phase/timestamp per row, but nothing about how long a row has been sitting there relative to how long it's allowed to run. Diagnosing today's stall on #106/#107 required manually diffing the row's UTC timestamp against wall-clock time and cross-referencing loop-daemon.sh's LOOP_DRIVER_TIMEOUT (default 90m) and .claude/state/.loop-driver-out.*.json mtimes by hand — nothing in the UI flagged it.
Concretely: #106 sat at phase scoped for ~90 minutes (right at the driver timeout boundary) with no implementer ever spawned, and #107 produced zero events across two advance attempts. Both were only visible as "stale" after manually computing elapsed time.
Proposal
Add a per-row age/budget indicator to the live table, sourced from data the daemon already produces:
now - <row's timestamp> for elapsed time.
- The matching
.loop-driver-out.<id>.json file's mtime (while it still exists — it's deleted on driver exit) as the "still running" signal, since --output-format json only writes at process exit.
LOOP_DRIVER_TIMEOUT (or its default) as the budget, so a row can render e.g. "87m / 90m timeout" and visually flag (color/badge) once it crosses some fraction of budget (e.g. 80%).
Why this matters
The loop is autonomous and unattended for long stretches; the cockpit is the primary way to notice "this task has been quietly stuck" without grepping events.jsonl / loop-ticks.jsonl / loop-runs.log by hand and doing timezone math. A staleness badge turns a silent stall into something obvious at a glance.
Scope
.claude/scripts/cockpit.sh (row rendering) and cockpit-serve.sh (live SSE upsert path in the injected client script) both build the live table and would need the same badge logic kept in sync — same "one renderer" constraint the existing code already documents.
Problem
The cockpit's live-progress table (issue #92/#114) shows a role/task/phase/timestamp per row, but nothing about how long a row has been sitting there relative to how long it's allowed to run. Diagnosing today's stall on #106/#107 required manually diffing the row's UTC timestamp against wall-clock time and cross-referencing
loop-daemon.sh'sLOOP_DRIVER_TIMEOUT(default 90m) and.claude/state/.loop-driver-out.*.jsonmtimes by hand — nothing in the UI flagged it.Concretely:
#106sat at phasescopedfor ~90 minutes (right at the driver timeout boundary) with no implementer ever spawned, and#107produced zero events across two advance attempts. Both were only visible as "stale" after manually computing elapsed time.Proposal
Add a per-row age/budget indicator to the live table, sourced from data the daemon already produces:
now - <row's timestamp>for elapsed time..loop-driver-out.<id>.jsonfile's mtime (while it still exists — it's deleted on driver exit) as the "still running" signal, since--output-format jsononly writes at process exit.LOOP_DRIVER_TIMEOUT(or its default) as the budget, so a row can render e.g. "87m / 90m timeout" and visually flag (color/badge) once it crosses some fraction of budget (e.g. 80%).Why this matters
The loop is autonomous and unattended for long stretches; the cockpit is the primary way to notice "this task has been quietly stuck" without grepping
events.jsonl/loop-ticks.jsonl/loop-runs.logby hand and doing timezone math. A staleness badge turns a silent stall into something obvious at a glance.Scope
.claude/scripts/cockpit.sh(row rendering) andcockpit-serve.sh(live SSE upsert path in the injected client script) both build the live table and would need the same badge logic kept in sync — same "one renderer" constraint the existing code already documents.