Skip to content

fix: a handoff to a worker reads as done, and work an agent waits on shows an hourglass, not a live turn's spinner - #183

Merged
sean-brydon merged 4 commits into
cosscom:mainfrom
Stener1:turn-check-handoff-done
Oct 23, 2026
Merged

sean-brydon merged 4 commits into
cosscom:mainfrom
Stener1:turn-check-handoff-done

Conversation

@Stener1

@Stener1 Stener1 commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Summary

A session whose agent handed its work to a separate worker ("I've started a worker to fix it, so you can close this session… I turned off its notifications to this session") kept showing as working. Two causes:

  1. Turn check read "a delegated task" as working even when this agent won't follow it.
  2. Sidebar and tab drew a turn-check "working" verdict (agent_state: finished + unfinished) with the same spinner as a live turn. Only the dashboard card said "Waiting on its work".
 working: something runs now that the agent waits on or acts on when it reports back:
-  the agent itself, a delegated task, a build, …
+  the agent itself, a delegated task it waits on or will act on when it reports back, a build, …
+  A task handed to a separate worker or session that this agent won't follow is a handoff:
+  "you can close this session", "followed elsewhere", "turned off its notifications to this
+  session" → done, even while the worker still runs.
 sessionState(s)                     // unchanged: still "running" for counts, the board, grouping
+onItsWork(s)  = finished && unfinished
+allOnItsWork  = every "running" session in a row is onItsWork
 <TaskGlyph state onWork>            // sidebar rows, folded headers and forks, Labs columns
 <StateGlyph state session noTip>    // tabs, a pane's header, the rail's card
-  running → spinner
+  running && onItsWork → hourglass (WorkGlyph), with "Its turn ended while its work runs on…"
+                         in its tip, or in the tab's / pane's own tooltip (noTip)

A new turn clears unfinished on the box (turns.go), so the spinner comes back while it runs.

Evidence

Turn check on the real model (TestTurnCheckLive, new, skipped unless BERTH_TURNCHECK_LIVE_KEY_FILE and BERTH_TURNCHECK_LIVE_MODEL are set; CI never calls a provider). Same fixtures, typesafe-ai/jev on AI Gateway:

Turn Before (main's prompt), 3 runs After, 5 runs
handoff to a worker it won't follow working 0.76 / 0.77 / 0.80 ❌ done 0.78–0.85 ✅
delegated task it waits on working 1.00 working 0.99–1.00
deploy running working 1.00 working 1.00
background re-check working 0.92–0.95 working 0.95–0.97
check-in scheduled for tomorrow parked 0.88–0.92 parked 0.85–0.90

On a real box, the real turn (.claude/skills/verify-berthd/scripts/turn-check-replay.sh, new: a throwaway location, a stand-in claude session, the turn as its transcript, the UserPromptSubmit and Stop hooks through berthd hook). Before, on the installed v0.3.30 build, 2 of 2 runs:

replay-cp9-handoff        agent_state=finished unfinished=True parked=False   ← shows as working
replay-delegated-waiting  agent_state=finished unfinished=True parked=False

The after run on a build with this branch is in a comment below.

App (drive.mjs turn-check-indicator, mock fixtures, no console errors; e2e/sidebar-turn-check.spec.ts asserts the same):

Sidebar: hourglass and its tip (order-export), spinner for a live turn (qa-deck) Tab: hourglass, tooltip says why Live turn's tab keeps the spinner
sidebar tab live

Before, the sidebar's order-export row and its tab drew the blue spinner.

Checks (local; fork PRs need a maintainer for CI): go vet ./... ✅; go test -race ./... ✅ except internal/leadline/api TestDetach, which failed once under the full suite's load and passes 5/5 alone (a package this PR doesn't touch). pnpm typecheck:plugins, check:titles, check:type, check:grid, check:csp, check:themes, test (incl. new derive.test.ts cases), build, tsc -p e2e ✅. e2e/sidebar-turn-check.spec.ts + e2e/dashboard-turn-check.spec.ts ✅. Docs build ✅. Codex review and adversarial review: no findings.

Merge Danger

Door: two-way

Prompt text and a UI indicator; no data or schema. Revert to undo.

Blast Radius: classification

Every box's turn check uses the new wording, so a borderline delegated turn could now read as done (it then shows as done instead of working; nothing is lost and no prompt is held). The live replay keeps a delegated-and-waiting turn at working 0.99–1.00. In the app, sessionState() is unchanged, so counts, the dashboard's columns and notifications are untouched; only the glyph changes.

🤖 Generated with Claude Code

Stener1 and others added 4 commits October 22, 2026 09:33
…one, not working

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… sidebar and its tab, not a live turn's spinner

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… line as the sidebar's

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 22, 2026

Copy link
Copy Markdown

@Stener1 is attempting to deploy a commit to the seanbrydonio's projects Team on Vercel.

A member of the Team first needs to authorize it.

@Stener1

Stener1 commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

I can't add the run-ci label from this fork (no permission), so CI waits for a maintainer to add it. The checks AGENTS.md lists were run locally; results are in the description.

@Stener1

Stener1 commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

After, on a real box. I built berthd from this branch merged onto v0.3.30 and installed it on my Linux box (a hand-built binary; the app's box upgrade replaces it). Then I replayed the same real turn through the box's own turn check (`turn-check-replay.sh`: a stand-in `claude` session, the turn as its transcript, and the UserPromptSubmit and Stop hooks through `berthd hook`). Provider `typesafe-ai/jev` on AI Gateway, 3 runs each:

Turn Before (v0.3.30 build) After (this branch)
handoff: "I've started a worker to fix it, so you can close this session… I turned off its notifications to this session" `unfinished=True` (working), 2/2 `unfinished=False` (done), 3/3; no "stays finished" error logged
delegated, waiting: "I've started the release build in a worker… I'll check the result when it reports back" `unfinished=True` (working) `unfinished=True` (working), 3/3

The UI scenario (`drive.mjs turn-check-indicator`) and `e2e/sidebar-turn-check.spec.ts` + `e2e/dashboard-turn-check.spec.ts` also pass on that merged tree.

@sean-brydon
sean-brydon merged commit 83b84a5 into cosscom:main Oct 23, 2026
0 of 2 checks passed
Stener1 added a commit to Stener1/berthd that referenced this pull request Oct 23, 2026
Main's handoff wording (cosscom#183) changed the request these six replays were
recorded for. Recorded again against the real provider; every verdict is
the one it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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