feat(sidebar): expandable worker tree under each thread #49

Merged
chad merged 2 commits from feat/sidebar-worker-tree into feat/status-tokens 2026-11-06 20:05:29 -05:00
Collaborator

Each root thread in the sidebar gets a chevron that expands to every worker it spawned (Coder, Codex, Claude Code, child threads), sweatshop-style.

Stacked on #48 (uses its status tokens). Merge #48 first, then retarget this to main.

Behaviour

  • Rows: the worker's mark in its own colour (Claude always orange), its name, and the first line of its task.
  • Status: shown after the label. A spinner while it runs (muted while queued or waiting), a red dot when it failed, and nothing once it finished.
  • Loading: a thread's workers load only when you expand it, so a collapsed thread costs nothing. A thread is open by default while it's running. Your open/closed choices are saved in localStorage.
  • Click: a worker row opens the thread at that worker's card (#worker-<toolCallId>). The card opens and scrolls into view.
  • Child threads now live in the tree instead of as flat indented rows, so nothing shows twice. Rename, archive and delete on root threads are unchanged.

Data

No new tables or server code. The list comes from the thread's saved transcript (GET /api/harness/transcript), parsed with the delegation card's own parseDelegationResult / delegationOutcome (pure, in lib/worker-tree.ts, unit-tested). Liveness comes from the snapshot's active agent operations, matched to calls with the card's existing matchLiveAgents. An expanded, running thread is re-read every 5 s.

Verification

  • pnpm typecheck and pnpm test pass (1295 passed, 5 new worker-tree tests, sidebar-threads test rewritten).
  • Real data: I ran workerRowsFromTranscript on three real transcripts from the live instance (Codex, Coder ×5, Claude Code). It gave the same outcomes the cards show: interrupted runs failed, finished runs done.
  • Browser: I fed those transcripts to the branch's sidebar (one Coder call changed to a live background run to show the spinner). Every row and status rendered as expected, and Claude's mark computed to #d97757 even when failed.
  • Hash: checked on a 40-card scratch page, both on a cold load and on in-app navigation. The targeted card opens and scrolls into view, and no other card opens.

Known limits

  • A worker waiting on an approval shows the running spinner in the tree, because the snapshot's operations don't carry waiting. Its card still says waiting.
  • Clicking the same worker twice in a row doesn't re-scroll, because the hash doesn't change.
  • Not yet seen against a live start_thread child; the unit tests cover its shape.

Screenshots

Light:

Worker tree, light

Dark:

Worker tree, dark

Each root thread in the sidebar gets a chevron that expands to every worker it spawned (Coder, Codex, Claude Code, child threads), sweatshop-style. **Stacked on #48** (uses its status tokens). Merge #48 first, then retarget this to `main`. ## Behaviour - **Rows:** the worker's mark in its own colour (Claude always orange), its name, and the first line of its task. - **Status:** shown after the label. A spinner while it runs (muted while queued or waiting), a red dot when it failed, and nothing once it finished. - **Loading:** a thread's workers load only when you expand it, so a collapsed thread costs nothing. A thread is open by default while it's running. Your open/closed choices are saved in localStorage. - **Click:** a worker row opens the thread at that worker's card (`#worker-<toolCallId>`). The card opens and scrolls into view. - **Child threads** now live in the tree instead of as flat indented rows, so nothing shows twice. Rename, archive and delete on root threads are unchanged. ## Data No new tables or server code. The list comes from the thread's saved transcript (`GET /api/harness/transcript`), parsed with the delegation card's own `parseDelegationResult` / `delegationOutcome` (pure, in `lib/worker-tree.ts`, unit-tested). Liveness comes from the snapshot's active `agent` operations, matched to calls with the card's existing `matchLiveAgents`. An expanded, running thread is re-read every 5 s. ## Verification - `pnpm typecheck` and `pnpm test` pass (1295 passed, 5 new worker-tree tests, sidebar-threads test rewritten). - **Real data:** I ran `workerRowsFromTranscript` on three real transcripts from the live instance (Codex, Coder ×5, Claude Code). It gave the same outcomes the cards show: interrupted runs failed, finished runs done. - **Browser:** I fed those transcripts to the branch's sidebar (one Coder call changed to a live background run to show the spinner). Every row and status rendered as expected, and Claude's mark computed to `#d97757` even when failed. - **Hash:** checked on a 40-card scratch page, both on a cold load and on in-app navigation. The targeted card opens and scrolls into view, and no other card opens. ## Known limits - A worker waiting on an approval shows the running spinner in the tree, because the snapshot's operations don't carry `waiting`. Its card still says waiting. - Clicking the same worker twice in a row doesn't re-scroll, because the hash doesn't change. - Not yet seen against a live `start_thread` child; the unit tests cover its shape. ## Screenshots Light: ![Worker tree, light](./worker-tree-light.png) Dark: ![Worker tree, dark](./worker-tree-dark.png)
Workers are the delegation calls in a thread's saved transcript, paired
with their results; liveness from the snapshot's agent operations. Fetched
only when a row is expanded. start_thread children no longer nest as flat
rows; a worker row links to its delegation card by hash.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fix(sidebar): keep worker marks in their own colour, status as its own glyph
All checks were successful
build / build-and-test (pull_request) Successful in 2m40s
build / build-sandbox-image (pull_request) Successful in 1s
build / deploy (pull_request) Has been skipped
bee0c0eb88
chad merged commit 856100ef9e into feat/status-tokens 2026-11-06 20:05:29 -05:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chad/tansweat!49
No description provided.