Skip to content

feat: turn check — a finished turn that needs you reads as waiting - #7

Open
Stener1 wants to merge 8 commits into
cosscom:mainfrom
Stener1:turn-check
Open

Stener1 wants to merge 8 commits into
cosscom:mainfrom
Stener1:turn-check

Conversation

@Stener1

@Stener1 Stener1 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Closes #6. Refs #8 (its hook findings; the shared-daemon part of #8 is separate).

Split as asked: the Codex half is its own PR, #71. This branch is now on main without it, so the diff is turn check only.

Choose who checks turns (new, last commit)

Point 3 asked for no default vendor model. A model field alone wasn't a real choice, because AI Gateway's /v1/evaluate only serves Jev, so Settings → Agents now starts with a provider picker. Nothing is picked for you:

Provider Endpoint Key Model
Jev on AI Gateway https://ai-gateway.vercel.sh/v1/evaluate AI Gateway key typesafe-ai/jev
OpenAI Decisions https://api.openai.com/v1/decisions (public beta) OpenAI key gpt-6-luna
Laya, your own server the laya-serve URL you give (/v1/systemone, Jev's shape) none optional (english, multilingual, typed-decisions)
  • OpenAI gets the same instructions and criteria as one choice question over the turn as one text. Its answer is read for the probability of its choice, against the same 0.65 threshold, and a refusal leaves the turn finished.
  • Keys are kept per provider (turncheck.key, turncheck-openai.key) and sent only to that provider's own fixed URL. A url in turncheck.json never changes where a key goes, and Laya never gets one. A reference set for one provider is dropped when the provider changes, and switching provider turns the check off until you turn it on again.
  • Tests: TestTurnCheckAsksOpenAIDecisions, TestDecisionsVerdictReadsOnlyItsAnswer, TestTurnCheckKeepsAKeyPerProvider (a pasted AI Gateway key is never sent to OpenAI), TestTurnCheckSendsAKeyOnlyToItsProvider; e2e settings-turn-check covers both flows (OpenAI with a key, then switching; Laya with only an address).
  • Laya, live: I ran the official laya 0.4.6 (laya-serve, CPU) on a box, and berthd's turn check reached it and read its answers. Its quality on this task is weak, though. On 4 hand-made turns (a question, a finished fix, a running deploy, a blocked migration) it got 1–2 right per checkpoint and was never above 0.52 sure, so with the 0.65 threshold it left every turn Done: safe, but it changes nothing in practice. The Settings text doesn't oversell it.
  • OpenAI, not live: I had no OpenAI key here, so the Decisions request and answer are tested against a stand-in server built from OpenAI's documented shape.

Changes from the review

  1. Laptop-only settings, and a key only to its own provider. GET and PUT /v1/turncheck now refuse wire.IsLocal (403, "turn check is set from a paired laptop"), so an agent on the box can't read or change where turns go. A key, pasted or a secret reference, is sent only to its provider's own fixed URL (AI Gateway's or OpenAI's); url is used only for a Laya server, which never gets a key. PUT refuses a key with Laya and a url with a hosted provider. Since turncheck.json is still a file an agent on the box could edit, the check refuses too: a reference beside a Laya url sends nothing at all, and a url beside a hosted provider is ignored. TestTurnCheckIsSetFromALaptopOnly drives both routes over a real Unix socket; TestTurnCheckSendsAKeyOnlyToItsProvider writes your exact scenario into the file (own server, key: env://…, over http and https) and asserts that server is never asked.
  2. Capped and redacted. The prompt is capped at 4,000 characters (its start), the title at 300, and the reply stays at its last 12,000. Title, prompt and reply go through RedactSecrets first, on the full text before cutting: the same secretPatterns that refuse an artifact (artifactcheck.go), each match replaced with [redacted: <what>], plus a private key taken out whole, header to footer, since the artifact pattern only needs the header to refuse. If LooksLikeSecret still finds anything after that, the turn isn't sent. TestTurnCheckSendsNoSecretAndAShortPrompt covers an AWS key, a GitHub token, an Anthropic key in the title, an .env line and a PEM block.
  3. No default model. jevModel is gone. Turning it on needs a provider and, for the hosted ones, a model (400 otherwise), and a check with none sends nothing. See the provider picker above.

Docs (security.mdx, sessions-and-agents.mdx, app-api.mdx, config.mdx, events.mdx, changelog) are updated to match.

Rebased on main after #73–#77. Turn check reads a session's transcript through transcriptFile(ctx, …) like the other callers now. Structured Labs agents (the new Claude driver) aren't turn-checked yet: it only reads Claude Code's and Codex's own transcript files. Happy to add the driver's record if you want it.

After the rebase: go vet ./... and go test -race ./... pass, except TestAStructuredSessionGoesToItsTerminalAndBack ("the terminal's turn in the chat never happened"). That test fails the same way on a clean main here, so it isn't this branch. The turn-check e2e specs pass.

Tested before the rebase: go vet ./..., go test -race ./..., pnpm test, pnpm build, typecheck:plugins, check:titles, check:csp, check:themes, tsc -p e2e; e2e settings-turn-check, dashboard-turn-check and skills-notice (same Settings page), 66 of 66 over 3 repeats. Live: Laya as above. Not re-run live against Jev or OpenAI after these changes.


An opt-in turn check: when an agent's turn finishes, berthd asks a calibrated classifier what the turn's end means, and if it is confident, the session shows as Needs you (it asks you something or is blocked) or Working (its work goes on without it: a build, a deploy, a later time) instead of Done. Off by default; nothing is sent unless it is turned on.

What it does

  • On agent.finished (not error or interrupted) for Claude Code or Codex, berthd waits for the reply to reach the transcript (the notifier's settle), reads the turn's last prompt and reply, and asks the model you chose through Vercel AI Gateway's evaluate endpoint (POST /v1/evaluate, Jev's request and answer, prompt training disallowed). There is no default model.
  • With probability ≥ 0.65: needs_input or blocked publishes agent.waiting (Needs you); working publishes agent.ready, which keeps the turn finished and marks the session unfinished (shown as Working; see the last section), both with source: "turncheck", the choice and the finished event's seq. working after a finished turn means the agent stopped with its work unfinished: a build still compiling, something to happen first, work parked until a later day, or work it resumes by itself. The instructions sent to Jev say so explicitly; on 15 real finished turns that wording put every one in the column we would put it in by hand (it moved a "we'll wait until Monday" turn from Done to Idle and one "paused until the import finishes" turn from Needs you to Idle, and changed nothing else) (Codex does when a background terminal finishes, with no new prompt hook). That is neither done nor a question, and Idle ("agents open with nothing to do") was otherwise almost always empty. An earlier version sent it to Needs you, which put busy Codex sessions there. done, ready_to_ship and idle leave the turn Done. The phone app labels Idle "ready for a first prompt", which reads a little oddly for these; happy to adjust if you prefer a different column or wording.
  • No fallback. No key, an error, a timeout (10 s), an unusable or unsure answer: the session stays finished and berthd logs one line with the reason (never the prompt, reply or key).
  • A turn-check wait is soft. The dashboard, phone and notifications show Needs you, but sends are not held: idle() and the now/idle send gates treat it as finished, since the agent is at its prompt, not at an approval. (Otherwise a lead that ends with "teammates are on it" would hold its teammates' reports.)
  • Late answers are dropped. Both events carry the finished event's seq; the ledger applies them only if the session is still finished at that seq, inside Publish, so a prompt you send meanwhile always wins.

Settings → Agents → Turn check

Per box: paste an AI Gateway key and Save ("Key saved on ", Remove), then switch it on. The key is kept on the box in the state directory (turncheck.key, 0600), is never returned by the API, logged or put in an event, and goes only to the AI Gateway over https. Use a secret reference instead takes op://… or env://NAME. A Model field comes first; the switch stays off until one is saved. url in turncheck.json can point it at another server that answers the same request (e.g. a self-hosted laya-serve); that server never gets a key.

Privacy

This is the first feature that sends transcript text off the box, so it is opt-in per box and documented next to the existing promise in security.mdx and sessions-and-agents.mdx. Hooks stay content-free; TestTranslateNeverCopiesContent is unchanged.

Tested

  • go vet ./...; go test -race ./internal/box/ (incl. 12 turn-check tests: needs_input/blocked → waiting, working → idle, stays finished for done/low confidence/server errors/timeouts, no request when off, late answer dropped, soft wait lets idle and now sends through, request shape, key only over https and only to the gateway, stored key 0600 and never returned); pnpm -C app build, pnpm -C app test (160 pass).
  • Live on a Linux box (berthd from this branch, real Claude Code sessions, real Jev; the key first as an env:// reference, and for the last runs pasted in the new Settings screen with the environment variable removed): turns ending on a plain-text question went to Needs you 9 of 10 times (Jev 0.78–0.95); the one miss was on an earlier build of this branch, gave no log line, and did not recur in the 9 runs after it. Every turn that was really finished stayed Done (5 of 5); a send to a flipped session was delivered at once.
  • Not run: the Playwright e2e (its Chromium isn't installed here); the Settings section was clicked through in mock mode and against the live box instead.

Unrelated to this change, TestATeamSetupResumesAfterBerthdRestarts failed under -race on a clean main on one run here and passed on another.

working shows as Working, not Idle

Running the earlier version on a busy box, about 40% of finished turns (65 of ~170 over three days) went to Idle. Idle's own text is "Agents open with nothing to do", so finished work looked like it had disappeared from Done. Replaying all 146 verdicts through this branch's own transcript code showed Jev was mostly right: about 43 of 53 working turns really were waiting on a background job, CI, a deploy watch or a scheduled run. The problem was where they went.

  • Ledger: turn check's agent.ready no longer moves the session to idle. The turn stays finished, and the session gets Unfinished, which the next state change clears. So the turn still takes prompts, ends waits and delivers held prompts as done. The seq guard is the same as for the waits.
  • API: Session.unfinished (also in @berth/plugin's Session).
  • App: sessionState reads finished+unfinished as running, so the dashboard's Working column, the sidebar, counts and Home all agree. The card shows "Waiting on its work" with a tip saying it picks up by itself. Done holds only work that is done.
  • Review inbox: leaves an unfinished session out unless all is asked for.

Tested

  • TestTurnCheckMarksWorkStillGoingOnUnfinished (stays finished and ready for a prompt, enrich says unfinished, cleared by the next turn, a verdict on an older turn is ignored); the Review test extended, and it fails without the filter; derive.test.ts; new e2e/dashboard-turn-check.spec.ts (the card is in Working with the marker and not in Done; a done card stays in Done), 5 of 5 runs.
  • go vet ./..., go test -race ./...; typecheck:plugins, check:titles, check:csp, check:themes, pnpm test, pnpm build, tsc -p e2e, the docs build; e2e: rail, home-widgets, first-run, idle, pr-review and the new spec (26 passed).
  • TestReviewRoutesOverTheWire and TestPaneDeadOfASignalIsItsShellStatus failed once each in the full -race run and passed 10 of 10 alone, on this branch and on the commit before it. They're flaky under load and unrelated to this change.

What Jev is given

Jev was often judging a turn on its reply alone. user_request came from the last 60 transcript items, so a long turn, or one the agent resumed by itself when its background work reported back, sent it empty: 45 of 189 checked turns on a real box. session_objective was always empty.

  • The check looks back up to 10 pages for the turn's prompt (10 of 189 still have none) and sends the session's title as session_objective. Docs, security.mdx and the Settings text say the title is sent.
  • Instructions: a wrap-up, a handoff or "the queue is empty, send the next one" is done, even with follow-ups listed. A reply that asks, waits for the user's go-ahead or leaves something waiting on them is needs_input, even with work going on in the background. working needs the agent itself to be waiting on or going on with something specific.
  • Replayed against Jev on all 189 real turns (old input and instructions vs new): about 10 move to the right column ("Waiting for the go-ahead to continue": Working → Needs you; "The queue is empty": Working → Done; "Shall I check for a reply later today?": Done → Needs you) and about 3 to a worse one (e.g. "the four points still wait for you": Needs you → Done at 0.56). Many verdicts sit near the 0.65 threshold either way.
  • TestTurnCheckSendsTheLongTurnsPromptAndTheObjective (200 tool calls between the prompt and the reply; it fails without the lookback). go vet, go test -race ./... (only the known flake TestPaneDeadOfASignalIsItsShellStatus), pnpm test, pnpm build, the docs build.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V7UV6sY16AabsdHtcyxyjL

@vercel

vercel Bot commented Oct 7, 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 force-pushed the turn-check branch 4 times, most recently from 5e4a92c to 2e806be Compare October 9, 2026 14:38
@Stener1 Stener1 changed the title feat(box): turn check — a finished turn that needs you reads as waiting feat: turn check, and Codex turns that read right (only Stop ends them; the question box is a wait) Oct 9, 2026

@sean-brydon sean-brydon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is a really thorough PR. Could you split it in two?

Codex turns: happy to take this. notify firing per message explains the Codex turns we've seen showing Done while still working, and the fix looks right. Please open it on its own, rebased on main (it conflicts now).

Turn check: holding for now. The key handling is careful, but:

  1. It can send secrets out. PUT /v1/turncheck is also reachable on berthd's local socket, so any agent on the box can call it. It could point url at its own server with key: op://… and receive the secret plus later transcripts. It needs: laptop-only settings (refuse wire.IsLocal), and secret references only sent to the default gateway.
  2. Transcripts go out unredacted. Please cap the prompt and run both the prompt and the reply through the same secret detection artifacts use.
  3. I'd rather not default to one vendor's model for users' transcripts. Let the user choose the model.

Send the Codex half and I'll merge it quickly. Thanks again!

@Stener1

Stener1 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@sean-brydon Great! Apologies for the long time it took, I didn't realize a comment had come in, but I'm very happy to be contributing now that I saw it. Will fix straight away.

Stener1 added a commit to Stener1/berthd that referenced this pull request Oct 12, 2026
…picks no model

From the review on cosscom#7:

- GET and PUT /v1/turncheck refuse the box's own socket. Any agent on the
  box can reach it, and could otherwise point url at its own server with
  key: op://… and collect the secret and later transcripts.
- A key, pasted or a reference, goes only to the default gateway
  (AI Gateway's evaluate URL). Settings refuses a reference beside another
  url, and a check whose turncheck.json says so anyway sends nothing, so
  editing the file on the box can't collect one either.
- The prompt is capped at 4,000 characters and the title at 300, and the
  title, prompt and reply go through RedactSecrets first: the same
  patterns that refuse an artifact, with a private key taken out whole. A
  turn that still looks like it holds a secret isn't sent.
- No default model: turning it on needs one, and Settings → Agents asks
  for it before the switch can be turned on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7UV6sY16AabsdHtcyxyjL
@Stener1 Stener1 changed the title feat: turn check, and Codex turns that read right (only Stop ends them; the question box is a wait) feat: turn check — a finished turn that needs you reads as waiting Oct 12, 2026
@Stener1

Stener1 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@sean-brydon the Codex half is now its own PR: #71 (rebased on main, ready to merge). This PR is turn check only, with your three points addressed; see the description.

Stener1 added a commit to Stener1/berthd that referenced this pull request Oct 12, 2026
…picks no model

From the review on cosscom#7:

- GET and PUT /v1/turncheck refuse the box's own socket. Any agent on the
  box can reach it, and could otherwise point url at its own server with
  key: op://… and collect the secret and later transcripts.
- A key, pasted or a reference, goes only to the default gateway
  (AI Gateway's evaluate URL). Settings refuses a reference beside another
  url, and a check whose turncheck.json says so anyway sends nothing, so
  editing the file on the box can't collect one either.
- The prompt is capped at 4,000 characters and the title at 300, and the
  title, prompt and reply go through RedactSecrets first: the same
  patterns that refuse an artifact, with a private key taken out whole. A
  turn that still looks like it holds a secret isn't sent.
- No default model: turning it on needs one, and Settings → Agents asks
  for it before the switch can be turned on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7UV6sY16AabsdHtcyxyjL
@Stener1

Stener1 commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@sean-brydon updated: this is now turn check only, rebased straight on main (the Codex part is #71).

On point 3, I went further than a free-text model field, since AI Gateway's evaluate endpoint only serves Jev. Settings → Agents now asks who checks turns, with nothing picked by default: Jev on AI Gateway, OpenAI Decisions (gpt-6-luna), or Laya on a server you run (no key, and turns stay on your machines). Each provider's key is kept apart and only ever sent to that provider's own URL. Details, including how Laya actually did in a live run, are in the description.

@sean-brydon

Copy link
Copy Markdown
Collaborator

@sean-brydon updated: this is now turn check only, rebased straight on main (the Codex part is #71).

On point 3, I went further than a free-text model field, since AI Gateway's evaluate endpoint only serves Jev. Settings → Agents now asks who checks turns, with nothing picked by default: Jev on AI Gateway, OpenAI Decisions (gpt-6-luna), or Laya on a server you run (no key, and turns stay on your machines). Each provider's key is kept apart and only ever sent to that provider's own URL. Details, including how Laya actually did in a live run, are in the description.

Thanks man! Appreciate that - I'll look into reviewing this later today

Stener1 added a commit to Stener1/berthd that referenced this pull request Oct 13, 2026
…picks no model

From the review on cosscom#7:

- GET and PUT /v1/turncheck refuse the box's own socket. Any agent on the
  box can reach it, and could otherwise point url at its own server with
  key: op://… and collect the secret and later transcripts.
- A key, pasted or a reference, goes only to the default gateway
  (AI Gateway's evaluate URL). Settings refuses a reference beside another
  url, and a check whose turncheck.json says so anyway sends nothing, so
  editing the file on the box can't collect one either.
- The prompt is capped at 4,000 characters and the title at 300, and the
  title, prompt and reply go through RedactSecrets first: the same
  patterns that refuse an artifact, with a private key taken out whole. A
  turn that still looks like it holds a secret isn't sent.
- No default model: turning it on needs one, and Settings → Agents asks
  for it before the switch can be turned on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7UV6sY16AabsdHtcyxyjL
Stener1 and others added 5 commits October 13, 2026 14:07
An agent's hooks report a turn as finished even when it ends by asking
something or stops with its work unfinished. With turn check on for a box
(off by default), berthd reads a finished Claude Code or Codex turn's last
prompt and reply from its transcript and asks Jev on Vercel AI Gateway,
with prompt training disallowed. needs_input, blocked or working at 0.65
or more publishes agent.waiting (source "turncheck", the choice, and the
Seq of the turn's end); anything else, a failed, late or unsure answer
included, leaves the session finished, and no other model is asked.

The ledger takes the verdict only while the session is still finished at
that Seq, and marks the wait soft: it shows as waiting, but sends, held
prompts and the phone treat the agent as finished, so nothing deadlocks
on it.

The key is pasted in Settings → Agents and kept on the box
(turncheck.key in the state directory, 0600, never answered, sent only
to AI Gateway), or named by a secret reference (op:// or env://); the
last one set wins. A key goes only over https and never follows a
redirect. turncheck.json in ~/.berth holds the switch, the reference,
and optionally the url and model of another Jev-compatible server.
GET/PUT /v1/turncheck, before:turncheck.change, turncheck.changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, not Idle

A finished turn Jev read as work still going on without the agent (a build,
a deploy, a check, a later time) went to agent state idle, so the Agent
Dashboard put it in Idle ("Agents open with nothing to do"), beside agents
that had never been prompted. On a real box that was about 40% of finished
turns, and Done looked empty.

The ledger now keeps such a turn finished and marks the session unfinished
(cleared by its next state), so it still takes prompts, ends waits and
delivers held prompts as done. Sessions carry `unfinished`; the app shows a
finished, unfinished agent as running everywhere (dashboard Working column
with "Waiting on its work", sidebar, counts), and the Review inbox leaves it
out until it is done. Done holds only work that is done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ox8V6xEpeM41a6641PDPK
…le, and tells a wrap-up from work still going on

Jev was judging many turns on the reply alone: the prompt was read from
the last 60 transcript items, so a long turn (or one the agent resumed by
itself after its background work reported back) sent an empty
user_request, and session_objective was always empty. On a real box 45 of
189 checked turns went without a prompt. The check now looks back up to
ten pages for the prompt (10 of 189 still have none), and sends the
session's title as the objective.

The instructions now say that a wrap-up, a handoff or "the queue is
empty, send the next one" is done even with follow-ups listed, and that a
reply waiting for the user's go-ahead or leaving something waiting on
them needs input. Replayed against Jev on those 189 turns, about 10 move
to the right column (e.g. "Waiting for the go-ahead to continue" from
Working to Needs you, "The queue is empty" from Working to Done) and
about 3 to a worse one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ox8V6xEpeM41a6641PDPK
…picks no model

From the review on cosscom#7:

- GET and PUT /v1/turncheck refuse the box's own socket. Any agent on the
  box can reach it, and could otherwise point url at its own server with
  key: op://… and collect the secret and later transcripts.
- A key, pasted or a reference, goes only to the default gateway
  (AI Gateway's evaluate URL). Settings refuses a reference beside another
  url, and a check whose turncheck.json says so anyway sends nothing, so
  editing the file on the box can't collect one either.
- The prompt is capped at 4,000 characters and the title at 300, and the
  title, prompt and reply go through RedactSecrets first: the same
  patterns that refuse an artifact, with a private key taken out whole. A
  turn that still looks like it holds a secret isn't sent.
- No default model: turning it on needs one, and Settings → Agents asks
  for it before the switch can be turned on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7UV6sY16AabsdHtcyxyjL
…ecisions or your own Laya

There is no default provider or model any more: Settings → Agents asks who
checks turns first.

- gateway: Jev through Vercel AI Gateway's /v1/evaluate, as before.
- openai: OpenAI's Decisions API (POST /v1/decisions, gpt-6-luna), as one
  choice question over the turn as one text; its answer is read for the
  probability of its choice, and a refusal leaves the turn finished.
- laya: Laya's laya-serve, or another server that answers Jev's request,
  at a URL you give, with its model optional (Laya routes by itself).

A key is kept per provider (turncheck.key, turncheck-openai.key) and is
sent only to that provider's own fixed URL; a URL in turncheck.json never
changes where a key goes, and Laya gets none. A reference set for one
provider is dropped when the provider changes, and switching provider
turns the check off until it is turned on again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7UV6sY16AabsdHtcyxyjL
Stener1 and others added 3 commits October 13, 2026 15:13
A finished turn whose work is unfinished but needs nothing from you is now
one of two answers. working: something it started still runs (the agent, a
delegated task, a build, a deploy, a test run, a watcher), shown as Working
(unfinished: true). parked: nothing runs and it waits on someone else's
reply or a later time ("check again in 24 hours"), shown as Idle
(parked: true). Both stay finished, take prompts, never notify, and stay
out of Review. Jev, Laya and OpenAI Decisions are asked with both
definitions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQ7gUBM8j8uAh3PWTFSYQa
The "+N more" button under a Home widget's rows was 18px tall, under the
24px touch target axe asks for, so screens showing it (Home, the rail, the
skills notice) failed the accessibility check once enough agents were
listed. It keeps its look, with a 24px minimum height.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQ7gUBM8j8uAh3PWTFSYQa
A parked agent waits on a reply or a later time on purpose, and now sits in
Idle, which Clean up offers. It is listed, marked parked, and only stopped
when ticked by hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LQ7gUBM8j8uAh3PWTFSYQa
@Stener1

Stener1 commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

@sean-brydon a heads-up before you review: I rebased on main (only a changelog conflict) and added three commits on top.

  • 14b6156 turn check tells parked work from work still running. Using it, "working" turned out to cover two different things: something actually running (a deploy, a build, a watcher) and an agent paused until someone replies or a set time ("watching for an email, checking again in 24 h"). Both showed in Working. Now there's a separate parked answer: the turn stays finished, marked parked (like unfinished), and shows in Idle. It's out of Review and never notifies. The two real sessions that showed the problem are in turncheck_test.go as anonymised fixtures.
  • 0a0249b a Home widget's "more" button is a full-size touch target. The PR's demo agents made that button appear, and it failed axe target-size on 5 a11y screens. It's 24 px now. With this, the dark and light a11y runs fail only what main already fails (settings-boxes, fixed in fix(app): a box with no browsers says so outside the list #72, and database-review).
  • f1af652 Clean up never ticks a parked agent for you. Parked agents now sit in Idle, so the clean-up dialog would otherwise pre-tick an agent that's waiting on purpose.

Checks: Go build, vet and the turn-check and review tests pass; the app builds and its unit tests pass (392/392); dashboard-turn-check, settings-turn-check and home-widgets e2e pass. go test ./internal/box/ also fails three structured-agent tests and a flaky review-routes test, and those fail the same way on a clean main.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LQ7gUBM8j8uAh3PWTFSYQa

This branch has not been deployed

No deployments
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.

A turn that ends on a question, an offer or unfinished work shows as Done

2 participants