Skip to content

feat(cockpit): per-row age/timeout-budget badge on live-progress rows - #149

Merged
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-116-cockpit-staleness-badge
Jul 16, 2026
Merged

feat(cockpit): per-row age/timeout-budget badge on live-progress rows#149
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-116-cockpit-staleness-badge

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

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.

  • elapsed = now - <row's own timestamp> (the last-logged event for that
    worker).
  • budget = LOOP_DRIVER_TIMEOUT (or its 90m default — read from
    loop-daemon.sh's run_driver(), which passes it straight through to
    timeout/RuntimeMaxSec).
  • The badge escalates muted -> warn (>=80% of budget) -> bad (>=100%)
    only while a .loop-driver-out.*.json file still exists under the
    state dir — loop-daemon.sh deletes that file right after its driver
    exits, 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.sh gathers the "still running" signal in its bash prelude
    (globs the real state dir live, or reads a synthetic
    <fixtures>/loop-driver-running.json in --fixtures mode) and renders the
    badge server-side in renderLiveProgress().
  • cockpit-serve.sh's injected SSE client (upsertRow) duplicates the
    same parseDurationToSeconds/threshold logic in JS, recomputed fresh on
    every full-page load (clientScript() is called on every handleIndex()
    request), 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, because cockpit.test.sh hardcodes this table's column
count/colspan (<td colspan="7"> for the group-header row) and was out of
this change's module boundary (only cockpit.sh/cockpit-serve.sh were in
scope) — adding a column would have broken several existing tests.

Testing

  • GATES_FILE=.claude/self/gates.json bash .claude/scripts/gate.sh build — pass
  • GATES_FILE=.claude/self/gates.json bash .claude/scripts/gate.sh lint — pass
  • GATES_FILE=.claude/self/gates.json bash .claude/scripts/gate.sh test — pass (544 checks, incl. all 116 in cockpit.test.sh)
  • Manual fixture smoke checks confirming the muted/warn/bad thresholds and
    the LOOP_DRIVER_TIMEOUT override.

… (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>
@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

10 similar comments
@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot

Copy link
Copy Markdown
Collaborator Author

feat(cockpit): per-row age/timeout-budget badge on live-progress rows (not yet reviewed)

@robercano-ghbot
robercano-ghbot merged commit a99ffa3 into main Jul 16, 2026
9 checks passed
@robercano-ghbot
robercano-ghbot deleted the feat/issue-116-cockpit-staleness-badge branch July 16, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cockpit: surface driver age/timeout budget on live-progress rows

2 participants