Skip to content

fix(claude): resolve the trust dialog from the observed highlight - #707

Merged
Juliusolsson05 merged 2 commits into
mainfrom
fix/claude-trust-dialog-accept
Aug 31, 2026
Merged

Juliusolsson05 merged 2 commits into
mainfrom
fix/claude-trust-dialog-accept

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

Problem

Claude Code 2.1.251 re-ordered the folder-trust dialog and pre-highlights "No, exit". Our entire accept contract was a bare '\r' written on the old "Yes is pre-selected" assumption — so clicking the app's own trust this folder button confirmed No, exit, terminated the CLI ~300ms later, never persisted trust, and brought the dialog back on every start. Every later send into the pane then failed with Cannot deliver prompt: <id> is not a live agent session. Recorded end to end in debug bundle 2026-08-30T23-51-06-471-9bd68e14 (three identical kill loops across sessions 1cc95cb1/4ac948fd/9bd68e14).

Fixes #705.

Implemented behavior

Accept stops being a keystroke and becomes intent, resolved against the live screen:

  • claude-code-headless (submodule bump to fix(conditions): accept the trust dialog from the observed highlight claude-code-headless#51): TrustDialogParser now extracts the real on-screen option order and which row carries the ❯ pointer; the new driveTrustDialogAccept driver walks the highlight onto "Yes, I trust this folder" with per-press verification before pressing Enter, and fails closed (writes nothing) when the screen cannot prove where the pointer is. The claude.trust-dialog condition's accept action is now { kind: 'custom', name: 'claude.trust-dialog.accept' }; decline is Esc (replacing the numbered-list '2\r'). Handles both the 2.1.251 layout and the legacy numbered layout.
  • TrustDialogModal / views.tsx: the modal expresses intent (onAccept/onDecline); the view dispatches the condition actions through the existing outlet → session:resolveCondition → headless resolver path (the same plumbing AskUserQuestion uses). No raw bytes originate in the renderer anymore.
  • providerConditions.ts: ClaudeTrustDialogState.options gains the optional highlighted field the parser now reports.

Design decisions

  • No constant keystroke can be correct: upstream inverted the default once already; the old code even chose bare Enter specifically to survive a highlight change, and that reasoning inverted with it. Acting on the observed pointer is the only contract that holds long-term.
  • Fail-closed everywhere: a wrong Enter either kills the session ("No, exit") or submits composer content if the dialog vanished a frame earlier — so an unprovable layout refuses to write and leaves the dialog up for the user.
  • The renderer dispatches literals, not wire actions: a stale pre-fix snapshot could still carry the old '\r' pty accept; dispatching the pinned custom action is deterministic, and an old headless answers it with a structured no-resolver failure instead of a fatal byte.

Tests

  • Package (claude-code-headless#51): parser pinned against the real recorded 2.1.251 screen from the incident bundle plus the legacy numbered layout; driver tests assert the write sequence (never Enter while "No, exit" holds the pointer; verified arrow-then-Enter; zero writes on unprovable layouts; structured timeouts). 70/70 package suite.
  • Parent: new trustDialogView.renderer.test.tsx pins that accept dispatches the custom action and that no pty payload containing '\r' leaves the view, and that cancel dispatches Esc. Conditions-adjacent renderer suites green (28 tests).
  • Verification: tsc node + web clean; targeted suites green under NODE_ENV=development.

Known limitations / follow-ups

🤖 Generated with Claude Code

https://claude.ai/code/session_01E9UW81kpTBiDPhZFpcpjf6

Juliusolsson05 and others added 2 commits August 30, 2026 17:03
Claude Code 2.1.251 flipped the trust dialog's default highlight to
'No, exit'; every accept path we own writes a bare Enter that now
confirms the exit option and kills the session. The plan replaces the
static keystroke contract with an observe-verify driver.

Refs #705

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9UW81kpTBiDPhZFpcpjf6
Claude Code 2.1.251 pre-highlights 'No, exit' in the folder-trust
dialog, so the bare Enter this modal dispatched for accept confirmed the
exit option and terminated the CLI — and because trust never persisted,
the dialog returned on every start. Accept now leaves the renderer as
the claude.trust-dialog.accept custom action, resolved headless-side by
the new highlight-aware driver (claude-code-headless#51): it walks the
pointer onto 'Yes, I trust this folder' with per-press verification and
refuses to write when the screen cannot prove the layout. Decline
becomes Esc, whose meaning does not depend on the highlight.

Fixes #705

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9UW81kpTBiDPhZFpcpjf6
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Pre-merge review: 2-agent adversarial orchestrated panel (run trust-wake-pr-review), both MERGE-SAFE, zero blockers. CI green.

Verified: full accept chain traced (modal → outlet custom dispatch → session:resolveCondition → SessionManager → ClaudeSession → headless evaluator → trust resolver), types clean across all three tsconfig projects; failures resolve as structured ok:false with the modal staying up (fail-closed end to end); old snapshots without highlighted and pre-#51 headless (no-resolver) degrade gracefully; generic condition consumers read only visible/kind, unaffected; remote client improves for free (its decline now matches the offered-action guard); prompt gate/#596-#597 wake-kill class not re-opened; gitlink equals claude-code-headless#51 HEAD and the untouched lockfile is correct (package consumed via path aliases, not a file: dep).

Recorded non-blocking follow-ups: desktop discards the DriveResult — a failed accept shows no toast (phone surfaces failedAtStep); onResolveCustom bypasses sendConditionKey's quarantine fence for driver-synthesized keystrokes; views.tsx duplicates the resolver-name literal (deliberate — package barrel is node-only — but package comments claiming the constant is imported should be corrected); plan doc's lockfile-resync note is wrong for this path-alias package; the two-step gitlink pattern implies a later chore: re-point at merged main.

@Juliusolsson05
Juliusolsson05 merged commit 4a0bd17 into main Aug 31, 2026
2 checks passed
@Juliusolsson05
Juliusolsson05 deleted the fix/claude-trust-dialog-accept branch August 31, 2026 00:43
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.

bug(claude): trust dialog accept confirms "No, exit" — new default highlight kills the session

1 participant