feat(cockpit): per-row age/timeout-budget badge on live-progress rows - #149
Conversation
… (issue #116) Adds an "<elapsed>m / <budget>m timeout" badge into each live-progress row's Updated cell: elapsed = now - the row's own timestamp, budget = LOOP_DRIVER_TIMEOUT (loop-daemon.sh's run_driver() wall-clock cap, default 90m). The badge escalates muted -> warn (>=80% of budget) -> bad (>=100%) only while a .loop-driver-out.*.json file still exists in the state dir (loop-daemon.sh deletes it right after its driver exits, so existence is the "still running" signal) -- once the driver has exited the badge stays muted, since that row's clock has already stopped ticking against any enforced ceiling. cockpit.sh gathers the "still running" signal in its bash prelude (globs the real state dir, or reads a synthetic fixtures/loop-driver-running.json in --fixtures mode) and renders the badge server-side. cockpit-serve.sh's injected SSE client duplicates the SAME parseDurationToSeconds/threshold logic in JS (recomputed fresh on every full-page load) so a row upserted live via SSE never disagrees with a row rendered by the next full cockpit.sh re-render. The badge is folded into the existing "Updated" cell rather than added as a new table column, since cockpit.test.sh hardcodes this table's column count/colspan and is out of this change's module boundary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
) The tests reviewer rejected the #116 badge feature (duration parse, 80%/100% thresholds, driver-running-vs-exited override, LOOP_DRIVER_TIMEOUT override) for shipping with zero automated coverage. Add a new section 9 to cockpit.test.sh following the existing fixture-driven pattern: <80% budget badges muted, just-over-80% warns, just-over-100% badges bad, the same >100%-elapsed row mutes once the driver has exited, LOOP_DRIVER_TIMEOUT overrides both the label and the threshold math, and a regression guard that the badge stays nested inside the existing Updated <td> (colspan-preserving design). Case 8 (SSE client-path cross-check) is documented as skipped: the client-side formula is anchored to the browser's real Date.now() with no COCKPIT_NOW override seam, so testing it would either require a headless browser (a new dependency, against the documented no-dependency cockpit contract) or break this suite's established real-clock-free determinism. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
10 similar comments
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
|
feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed) |
Closes #116
What changed
Adds a per-row age/timeout-budget indicator to the cockpit's live-progress
table:
<elapsed>m / <budget>m timeout, appended into each row's existing"Updated" cell.
now - <row's own timestamp>(the last-logged event for thatworker).
LOOP_DRIVER_TIMEOUT(or its90mdefault — read fromloop-daemon.sh'srun_driver(), which passes it straight through totimeout/RuntimeMaxSec).only while a
.loop-driver-out.*.jsonfile still exists under thestate dir —
loop-daemon.shdeletes that file right after its driverexits, so its mere existence is the "a driver is still running" signal.
Once the driver has exited, the badge stays muted regardless of elapsed,
since that row's clock has already stopped ticking against any enforced
ceiling (a separate pre-existing "stale" badge already flags long-abandoned
rows).
Keeping both renderers in sync
cockpit.shgathers the "still running" signal in its bash prelude(globs the real state dir live, or reads a synthetic
<fixtures>/loop-driver-running.jsonin--fixturesmode) and renders thebadge server-side in
renderLiveProgress().cockpit-serve.sh's injected SSE client (upsertRow) duplicates thesame
parseDurationToSeconds/threshold logic in JS, recomputed fresh onevery full-page load (
clientScript()is called on everyhandleIndex()request), so a row upserted live via SSE never disagrees with a row
rendered by the next full
cockpit.shre-render.The badge is folded into the existing "Updated" cell rather than added as a
new table column, because
cockpit.test.shhardcodes this table's columncount/colspan (
<td colspan="7">for the group-header row) and was out ofthis change's module boundary (only
cockpit.sh/cockpit-serve.shwere inscope) — adding a column would have broken several existing tests.
Testing
GATES_FILE=.claude/self/gates.json bash .claude/scripts/gate.sh build— passGATES_FILE=.claude/self/gates.json bash .claude/scripts/gate.sh lint— passGATES_FILE=.claude/self/gates.json bash .claude/scripts/gate.sh test— pass (544 checks, incl. all 116 incockpit.test.sh)the
LOOP_DRIVER_TIMEOUToverride.