feat(cockpit): serve mode with SSE auto-update, dark theme, clickable dashboard (closes #69) - #73
Merged
Conversation
…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
approved these changes
Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
.claude/scripts/cockpit-serve.sh(invoke:bash .claude/scripts/cockpit-serve.sh [port] [--fixtures <dir>]). Node built-inhttponly, zero new deps, binds 127.0.0.1 only, default port 8090. Sourcesresolve-roots.shand 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 tocockpit.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. ClientEventSourceupdates 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_REFRESHoverride).cockpit.sh) via CSS custom properties, with a light-theme toggle persisted inlocalStorage. Self-contained inline CSS/JS — no CDNs/fonts/external assets. All dynamic content stays escaped (serveresc(); client usestextContentfor event data). Marker:data-theme="dark"on<html>.data-modulerows); live-worker rows carry stabledata-role/data-taskstructure 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
--fixturesseam, exit codes, existing assertions). The dark restyle applies to both modes, as the issue requires.cockpit.test.shgains 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 fixturesevents.jsonland asserts it arrives on/eventswithin a bounded timeout, then kills the server via an EXIT trap. Hermetic — no network, no gh..claude/self/checks.shtestcase now actually executescockpit.test.sh+log-event.test.sh(previously only build+lint ran), with exit codes propagating (a failing test blocks the gate).Reviews
Known non-blocking follow-ups (reviewer notes)
cockpit-serve.shleaves a per-invocation temp HTML file in$TMPDIR(the bash EXIT trap is dead afterexec node); unlink it from the nodeprocess.on("exit").log-event.shlog rotation the offset goes stale (self-heals within ~60s via the periodic reload + reconnect). Harden by resetting offset to 0 whensize < offset.🤖 Generated with Claude Code