Repository navigation
fix(idle): hold idle while the workspace trust dialog is on screen - #634
Merged
Merged
Conversation
The dialog's own prompt glyph armed idle confirmation and the quiet dialog passed it, so a fresh case reported idle about 4 s in. An agent that waited for idle and then prompted typed into the dialog, and its Enter confirmed No, exit and killed the pane.
Ark0N
pushed a commit
that referenced
this pull request
Oct 16, 2026
feat(codex): raise codex approval, question and trust dialogs as BLOCKED Conflict resolved against #634 in src/session.ts _probePaneWorking(): #624's assignment (a dialog on screen is never work) is kept, and #634's trust-dialog verdict block (log the hold once, remember the reading) follows it, before _readWatching(). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner
|
Merged and shipped in 1.42.0. Thanks @JDProfresh! The before/after numbers on brand-new folders made this easy to trust, and every guard has a test that goes red without it, the restarted-pane case included. I re-ran it with Claude Code 2.1.301 in fresh folders: on master an idle wait answered while the dialog was still up in 4 of 6 launches, with this PR it held every time. Together with #635 the dialog is usually gone before the hold even matters, so idle lands about as fast as in a trusted folder. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Claude Code's workspace-trust dialog puts its
❯onNo, exit, and that glyph arms idle confirmation like the composer does. The dialog is quiet and has no working line, so a fresh case was concluded idle while the dialog was still up.Problem
On a brand-new directory,
GET /api/sessions/:id/wait?until=idleanswersidleabout 3.8 s after launch while the pane still shows the trust dialog with the cursor onNo, exit. An agent that waits for idle and then sends its first prompt types into the dialog, and the prompt's Enter confirmsNo, exit. The CLI quits, the pane dies (Pane is dead (status 1)), and the send-and-wait reportsexit. I hit this twice on 2026-10-06 with fresh/tmpdirs on Claude Code 2.1.280, and it reproduces on 2.1.294 (numbers below).Cause
_detectInteractiveActivity()arms_confirmIdle()on any chunk that carries the CLI'spromptGlyph. For Claude that is❯, and the trust dialog draws one as its selection marker._confirmIdle()then checks that the pane went quiet and that_probePaneWorking()finds no working line. The dialog passes both, so_concludeIdle()runs: the status flips toidleand theidleevent fires.currentSignalFor()and the wait registry read those two, so every idle consumer (wait, send-and-wait, the defaultstop,idle,exitset) sees the same false idle. The auto-accept usually answers the dialog a second or two later, which is why this is a race, and why it is a dead pane every time when the accept is slow or never lands.Fix
_probePaneWorking()already captures the pane at the moment idle is decided. It now also records whether that capture shows the dialog, using the existingisTrustDialogScreen()two-marker match. A failed capture keeps the last answer, since it proves nothing either way._confirmIdle()re-checks onPANE_PROBE_RECHECK_MSwhile the dialog is on screen, instead of concluding idle. This holds only inside the auto-accept's own startup window (TRUST_DIALOG_WINDOW_MS, 90 s), so a later screen that merely quotes the dialog (an agent readingsession-trust-dialog.ts) cannot hold a session busy.busyfrom spawn), not marked working: nothing is running. Sowait?until=idlekeeps waiting until the dialog is answered and the composer is up, anduntil=workingdoes not fire on it.startInteractive()clears the recorded verdict, so a restarted pane does not inherit the last pane's answer. This matters when the new pane's probe cannot replace that answer: the direct-PTY fallback has no pane to capture, and a failed capture keeps the last answer. Without the reset, a restart there after the dialog was seen would hold the new composer busy for the whole 90 s window.Because
_concludeIdle()is the only place a Claude pane becomes idle (the launch settle in_settlePaneStartup()does not apply to a CLI that declaresworkDetect, andidle_promptnever fires on the dialog), gating_confirmIdle()covers every idle path.Tests
test/session-trust-dialog.test.ts, new block "Session idle detection while the trust dialog is up", driving the real_detectInteractiveActivity()and_confirmIdle()against a scripted pane (the verbatim 2.1.252 capture already in the file):❯arms the check, and after 30 s the session is stillbusywith noidleevent;idle;startInteractive()(pty.spawn stubbed with a harmless process) after the flag was set by an earlier pane, and the new pane's❯still goes idle. The direct-PTY fallback has no pane to capture, so nothing would ever overwrite the stale verdict there (a failed capture keeps it the same way), and without the reset the new pane would read busy for its whole 90 s window.I removed each guard and watched its test go red: the
_confirmIdle()hold (first two red), the null-capture rule (third red), the window bound (fourth red), thestartInteractive()reset (fifth red,expected 'busy' to be 'idle'). Full gate: 567 files passed, 11262 tests passed, 12 skipped, on the tree before the restart test was added. With it, the trust, activity and awaiting-workers suites pass, 99 of 99, three runs.Verified
Claude Code 2.1.294, a throwaway Codeman instance from this branch's base (
CODEMAN_INSTANCE=w8trust, port 5187), one freshmktemp -d /tmp/...directory per launch,POST /api/sessions+POST .../interactive, thenGET .../wait?until=idlewhile samplingtmux capture-paneonce a second.Before (upstream master), observed:
idleat 3.8 s with the pane still on❯ No, exit. In 2 of those the dialog was never answered at all (a separate auto-accept bug, sent in its own PR), so the session readidlefor its whole life while parked on the dialog.idleat 3.8 s on❯ No, exit. The prompt's send-and-wait then resolvedexit3 s later, and the pane readPane is dead (status 1). The fourth happened to go idle after the accept and survived.After (this branch), observed:
Holding idle while the workspace trust dialog is on screen, the wait answeredidleat 7.8 to 8.8 s with no dialog on screen, and all 5 panes were alive afterwards (pane_dead=0, composer up).Inferred, not measured: idle now lands up to one
PANE_PROBE_RECHECK_MS(5 s) after the dialog is answered, because the hold re-checks on that cadence. On a directory that is already trusted nothing changes, since the dialog never appears.Docs
The idle-detection paragraph in CLAUDE.md and
docs/architecture-invariants.md#idle-detection-composer-glyph-and-working-linedescribe the hold.docs/api-reference.md,docs/extending-codeman.mdanddocs/wiki/Driving-Codeman-From-An-Agent.mdsaid a prompt sent into the dialog just sits there while the startup idle resolves the wait. Measured, it confirmsNo, exitand kills the pane, so I corrected those sentences. They still say idle is not a readiness signal.Invariants touched: idle-detection-composer-glyph-and-working-line, workspace-trust-dialog-auto-accept (reuses its window and two-marker match; the three auto-accept guards are untouched), agent-wait-primitives (behaviour only, no route change).
Note
The companion PR #635 (re-reading a throttled trust-dialog scan) touches neighbouring lines in
src/session.ts, the same test file and the trust paragraph in CLAUDE.md. A trial merge of the two branches is clean, and the merged test file passes.