Context
The cockpit today is server-rendered: cockpit.sh (~1.3k lines of bash emitting HTML) builds one static snapshot, and cockpit-serve.sh wraps it with an SSE refresh client and the worker-inspector drawer. Every new feature lands as more string-concatenated HTML inside cockpit.sh, which is at the limit of what it can absorb. The v0.5.0 theme — cockpit as the main interface — needs client-side interactivity (a milestone release board, an interactive dependency graph, a model-routing editor) that server-rendered snapshots can't reasonably deliver.
Constraint that STAYS: zero dependencies. No npm packages, no build step, no CDN/external assets — node built-ins on the server, vanilla JS/CSS/SVG in the browser, self-contained files shipped in the plugin.
Proposal
- Split data assembly from rendering in
cockpit.sh: a --json mode that emits the full state model as one JSON document — issues (with blocking edges via the existing --parse-blocking seam, labels, priority, milestone), PRs (review + CI state), live worker progress (the same derived task state the HTML panel computes: normalized identity, done-collapse, stale detection), loop health/tick history, needs-human items, model/skill routing, worktrees. One data assembly, two renderers during the transition.
cockpit-serve.sh: add GET /api/state returning that JSON, with the same short-lived cache + /api/refresh bypass semantics as today's HTML cache.
- New static SPA page (a single self-contained HTML file with inline JS/CSS, no framework) served at
/, rendering everything client-side from /api/state; the existing /events SSE stream triggers re-fetch. The current server-rendered HTML stays reachable at /legacy (and as cockpit.sh's no-server output) until the SPA reaches data parity; retiring it is a follow-up, not this issue.
- Keep the fixtures seam end-to-end:
--fixtures <dir> must drive --json and /api/state too, so cockpit.test.sh can assert on the JSON shape offline with no gh/network.
- Document the state-JSON contract (docs section or a
_README top-level key) so the board/graph/editor issues in this milestone build against a stable schema.
Acceptance criteria
cockpit.sh --json emits the full state model; schema covers everything the current HTML shows (issues + edges, PRs, workers, loop health, needs-human, routing, worktrees); --fixtures works with it.
GET /api/state serves it with caching matching today's HTML cache; /api/refresh still busts it.
/ serves the SPA shell rendering at least today's sections (issues by module, PRs, live workers, loop health, needs-you strip) from the JSON — data parity required, pixel parity not.
- No new runtime dependencies, no build step, no external network fetches from the page; degrades to placeholders (not crashes) when gh/network is unavailable, exactly like today.
- Existing tests keep passing; new fixture-driven tests cover
--json and /api/state.
Notes
Context
The cockpit today is server-rendered:
cockpit.sh(~1.3k lines of bash emitting HTML) builds one static snapshot, andcockpit-serve.shwraps it with an SSE refresh client and the worker-inspector drawer. Every new feature lands as more string-concatenated HTML insidecockpit.sh, which is at the limit of what it can absorb. The v0.5.0 theme — cockpit as the main interface — needs client-side interactivity (a milestone release board, an interactive dependency graph, a model-routing editor) that server-rendered snapshots can't reasonably deliver.Constraint that STAYS: zero dependencies. No npm packages, no build step, no CDN/external assets — node built-ins on the server, vanilla JS/CSS/SVG in the browser, self-contained files shipped in the plugin.
Proposal
cockpit.sh: a--jsonmode that emits the full state model as one JSON document — issues (with blocking edges via the existing--parse-blockingseam, labels, priority, milestone), PRs (review + CI state), live worker progress (the same derived task state the HTML panel computes: normalized identity, done-collapse, stale detection), loop health/tick history, needs-human items, model/skill routing, worktrees. One data assembly, two renderers during the transition.cockpit-serve.sh: addGET /api/statereturning that JSON, with the same short-lived cache +/api/refreshbypass semantics as today's HTML cache./, rendering everything client-side from/api/state; the existing/eventsSSE stream triggers re-fetch. The current server-rendered HTML stays reachable at/legacy(and ascockpit.sh's no-server output) until the SPA reaches data parity; retiring it is a follow-up, not this issue.--fixtures <dir>must drive--jsonand/api/statetoo, socockpit.test.shcan assert on the JSON shape offline with no gh/network._READMEtop-level key) so the board/graph/editor issues in this milestone build against a stable schema.Acceptance criteria
cockpit.sh --jsonemits the full state model; schema covers everything the current HTML shows (issues + edges, PRs, workers, loop health, needs-human, routing, worktrees);--fixturesworks with it.GET /api/stateserves it with caching matching today's HTML cache;/api/refreshstill busts it./serves the SPA shell rendering at least today's sections (issues by module, PRs, live workers, loop health, needs-you strip) from the JSON — data parity required, pixel parity not.--jsonand/api/state.Notes
/api/state.