Skip to content

feat(cockpit): serve mode with SSE auto-update, dark theme, clickable dashboard (closes #69) - #73

Merged
robercano-ghbot merged 1 commit into
mainfrom
feat/issue-69-cockpit-serve-sse
Jul 7, 2026
Merged

feat(cockpit): serve mode with SSE auto-update, dark theme, clickable dashboard (closes #69)#73
robercano-ghbot merged 1 commit into
mainfrom
feat/issue-69-cockpit-serve-sse

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

Implements #69 (Cockpit Phase 3a from tracking issue #68): turns the one-shot static dashboard into a live, clickable cockpit — additive and opt-in, static mode contract preserved.

What

  • Serve mode — new .claude/scripts/cockpit-serve.sh (invoke: bash .claude/scripts/cockpit-serve.sh [port] [--fixtures <dir>]). Node built-in http only, zero new deps, binds 127.0.0.1 only, default port 8090. Sources resolve-roots.sh and references siblings via $script_dir (issue scripts break when run from the plugin install cache (CLAUDE_PLUGIN_ROOT): root derived as ../.. conflates plugin root with consumer project root #63 convention).
    • GET / — reuses the EXISTING renderer (shells out to cockpit.sh, no forked render) and injects the SSE/refresh client JS before </body>.
    • GET /events — SSE (text/event-stream), fs.watch + 1s polling fallback on the events file, pushes each newly appended JSONL line, 15s heartbeat. Client EventSource updates the live-workers panel in place, auto-reconnects, and shows a "stale since" badge on drop.
    • GET /api/refresh — forces a re-render; gh-backed sections also refresh on a timer (default 60s, COCKPIT_GH_REFRESH override).
  • Dark theme by default in the shared renderer (cockpit.sh) via CSS custom properties, with a light-theme toggle persisted in localStorage. Self-contained inline CSS/JS — no CDNs/fonts/external assets. All dynamic content stays escaped (server esc(); client uses textContent for event data). Marker: data-theme="dark" on <html>.
  • Clickability — issues/PRs link to GitHub (verified); module group headers filter the issue list client-side (data-module rows); live-worker rows carry stable data-role/data-task structure for the future 3b inspector (no drawer yet).

Why

Auto-updating live cockpit you leave open while the loop runs — no watch + manual browser reload. The render/serve/SSE path contains no LLM calls (0 tokens to view/refresh/stream).

Testing

  • Static one-shot mode keeps its behavioral/contract identity (arg parsing, --fixtures seam, exit codes, existing assertions). The dark restyle applies to both modes, as the issue requires.
  • cockpit.test.sh gains a serve smoke case: random free ephemeral port (net.createServer().listen(0)), readiness poll on the startup line, GET / asserts HTTP 200 + dashboard marker + dark-theme marker, appends a line to the fixtures events.jsonl and asserts it arrives on /events within a bounded timeout, then kills the server via an EXIT trap. Hermetic — no network, no gh.
  • .claude/self/checks.sh test case now actually executes cockpit.test.sh + log-event.test.sh (previously only build+lint ran), with exit codes propagating (a failing test blocks the gate).
  • Self gates green: build / lint / test all pass.

Reviews

  • Correctness (opus): APPROVE
  • Tests (sonnet): APPROVE (45/45 cockpit checks; verified exit-code propagation)

Known non-blocking follow-ups (reviewer notes)

  • cockpit-serve.sh leaves a per-invocation temp HTML file in $TMPDIR (the bash EXIT trap is dead after exec node); unlink it from the node process.on("exit").
  • SSE byte-offset assumes append-only; on log-event.sh log rotation the offset goes stale (self-heals within ~60s via the periodic reload + reconnect). Harden by resetting offset to 0 when size < offset.

🤖 Generated with Claude Code

…ty (#69)

Cockpit Phase 3a. Adds cockpit-serve.sh, a zero-dependency node http server
(127.0.0.1 only) that reuses cockpit.sh's renderer unchanged: GET / regenerates
the dashboard on a short TTL cache and injects an SSE/refresh client script
before </body>; GET /events streams the live-progress event log (replay +
fs.watch/poll, 15s heartbeat, auto-reconnect via EventSource); GET /api/refresh
forces an immediate re-render for the client's timer/button. This lets workers'
progress update live instead of requiring a manual re-run of cockpit.sh.

cockpit.sh itself is restyled dark-by-default (CSS custom properties, with a
localStorage-persisted light-theme toggle) and gains two small client-side
hooks: a stable `data-theme="dark"` marker, and a module-header click filter
over the issue list (data-module on each <li>). Live-progress rows also grow a
hidden data-role/data-task cell for a future worker inspector (issue 3b) —
appended after every column the existing tests exact-match, so none of them
needed to change.

cockpit.test.sh gains a serve-mode smoke case (random free port, readiness
poll, SSE delivery of a freshly appended events.jsonl line) plus assertions
for the new dark-theme/filter markup, still fully offline against fixtures.
Wired cockpit.test.sh + log-event.test.sh into .claude/self/checks.sh's `test`
case (previously neither ran as part of the self-host test gate) and fixed a
latent GATES_FILE leakage bug that surfaced once cockpit.test.sh actually ran
under the self-adapter's environment.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NTZEsfFT4mrZpv8CY67Frd
@robercano-ghbot
robercano-ghbot merged commit 9177112 into main Jul 7, 2026
9 checks passed
@robercano-ghbot
robercano-ghbot deleted the feat/issue-69-cockpit-serve-sse branch July 7, 2026 15:34
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.

2 participants