Skip to content

feat(cockpit): worker inspector drawer — timeline, worktree forensics, breadcrumbs (closes #70) - #74

Merged
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-70-worker-inspector
Jul 7, 2026
Merged

feat(cockpit): worker inspector drawer — timeline, worktree forensics, breadcrumbs (closes #70)#74
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-70-worker-inspector

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

What

Cockpit 3b — worker inspector drawer. Clicking a worker row in the served dashboard opens an inspector drawer with three sections, backed by a new GET /api/worker/<role>/<task> JSON endpoint in cockpit-serve.sh:

  1. Event timeline — every .claude/state/events.jsonl record for that role+task (phases + --detail breadcrumbs), newest first.
  2. Worktree forensics (zero agent tokens) — computed live by the server via git: current branch, git status --short, last 5 commits, and diffstat vs merge-base with main. Located by .claude/worktrees/issue-<task> dir or feat/issue-<task>-* branch. Degrades gracefully to a "no worktree found" marker when absent.
  3. Latest breadcrumbs — the most recent --detail values, surfaced prominently.

Endpoint stays 127.0.0.1-only, node built-ins only, all dynamic content escaped (drawer renders via textContent, not innerHTML).

Why

Part 2 of 3 of the cockpit design (tracking #68), building on 3a (serve mode + SSE, #73). Gives a zero-token forensic view of what each worker is doing.

Agent prompt nudges

Additive --detail breadcrumb convention added to the existing log-event.sh paragraphs in orchestrator.md, implementer.md, reviewer.md (one terse sentence per phase transition; never blocks work if log-event fails). Dogfooded during this run.

3a follow-up fixes (reviewer-flagged in #73)

  • Temp-HTML leak: removed the dead bash EXIT trap after exec node (never fired) — cleanup now runs via node's own process.on("exit") across normal, shutdown, and error paths.
  • SSE offset staleness: when events.jsonl is rotated (file size < current read offset), the SSE offset resets to 0 so tailing resumes correctly.

Hardening (from correctness review)

  • Malformed percent-escapes in the worker path no longer crash the server — decodeURIComponent is wrapped in try/catch returning 400 JSON.
  • role/task containing path separators, .., or NUL are rejected up front with 400 (defense-in-depth; git calls already use execFileSync arg arrays).

Testing

Extended .claude/scripts/cockpit.test.sh with fully offline drawer coverage: a synthetic temp git repo/worktree under $TMPDIR (mirroring smoke-fanout.sh), asserting the endpoint returns timeline + forensics fields (branch/status/commits/diffstat), newest-first ordering, breadcrumbs, the found:false degrade path, both 3a fixes, and the malformed/traversal → 400 + server-stays-up regression. No network/gh in tests. Self gates green (build / lint / test).

Reviewed through correctness and tests lenses (consensus all).

Closes #70.

🤖 Generated with Claude Code

robercano and others added 2 commits July 7, 2026 17:48
Adds GET /api/worker/<role>/<task> to cockpit-serve.sh: an event timeline
(newest first) from events.jsonl, the latest --detail breadcrumbs surfaced
separately, and live worktree forensics (branch, status --short, last 5
commits, diffstat vs main's merge-base) computed by shelling out to git —
zero agent tokens. The worker's worktree is located by conventional
directory name (.claude/worktrees/issue-<task>) or, failing that, by
scanning `git worktree list` for a branch matching feat/issue-<task>-*;
no match degrades to a clear found:false marker rather than erroring.

Wires a click-to-open inspector drawer into the served dashboard's live-
progress table (static one-shot cockpit.sh output is unchanged), rendering
the three sections above with every dynamic value via textContent only.

Also lands the two PR #73-flagged 3a followups:
 - cockpit-serve.sh's bash EXIT trap for its mktemp'd HTML file never fired
   (exec node replaces the shell), leaking one temp file per invocation;
   cleanup now happens in node's own 'exit' handler instead.
 - /events' SSE tailer went stale forever once log-event.sh's rotation
   shrank events.jsonl below the subscriber's read offset; pump() now
   resets the offset to 0 whenever the file has shrunk.

Extends the log-event.sh paragraphs in orchestrator/implementer/reviewer.md
so every phase-transition call also passes a terse --detail breadcrumb,
and extends cockpit.test.sh with an offline worker-inspector smoke case
(synthetic temp git repo/worktree under $TMPDIR, mirroring
.claude/self/smoke-fanout.sh's own git-init/worktree-add pattern) plus
regression checks for both fixes.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NTZEsfFT4mrZpv8CY67Frd
…in role/task

Correctness review (BLOCKING): a malformed percent-escape in
/api/worker/<role>/<task> reached decodeURIComponent() outside
handleWorkerInspector's try/catch, throwing an uncaught URIError that
killed the whole node process for every client (no
process.on("uncaughtException") handler exists). The route now passes
the raw matched segments through, and handleWorkerInspector decodes
them itself inside a try/catch, responding 400 JSON on failure instead
of crashing.

Hardening (both reviewers flagged): role/task are interpolated into
path.join(WORKTREES_ROOT, ...) and a RegExp branch scan. Reject any
decoded value containing a path separator, "..", or NUL up front with
400 JSON, closing the traversal note (git calls already use
execFileSync with arg arrays, so this is defense-in-depth).

Adds an offline regression test hitting the endpoint with a malformed
percent-escape and a traversal-shaped task, asserting both return 400
and that the server is still serving valid requests afterward --
proving the crash is fixed rather than just changing a status code.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NTZEsfFT4mrZpv8CY67Frd
@robercano-ghbot
robercano-ghbot merged commit 3a46728 into main Jul 7, 2026
9 checks passed
@robercano-ghbot
robercano-ghbot deleted the feat/issue-70-worker-inspector branch July 7, 2026 16:22
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 3b: worker inspector drawer (timeline + worktree forensics + breadcrumbs)

2 participants