Repository navigation
feat(conditions): AskUserQuestion condition + ghost-render fix (PR-4) - #329
Merged
Juliusolsson05 merged 2 commits intoJun 22, 2026
Merged
Conversation
…app) App-side half of PR-4. Pairs with claude-code-headless#26 (gitlink bumped to a0eeb74) which makes the AskUserQuestion picker a first-class, deduped condition in the unified snapshot. Three changes: 1. Ghost-render fix (foldEvent turn_stopped). An AskUserQuestion tool_use blocks the agent until a tool_result lands. If the user INTERRUPTS instead of answering, no result is ever emitted, so the block stayed `resultAt == null` forever and BlockRow kept routing it to the live, clickable picker — it "ghosted" in the feed many messages later (the reported bug). turn_stopped now stamps `resultAt` on pending AskUserQuestion blocks, the transcript-backed dismissal (NOT screen-derived, so it can't flicker). Scoped STRICTLY to `toolName === 'AskUserQuestion'`: every other pending tool can still resolve with a late result across a stop, so stamping them would drop real output; AskUserQuestion is unique in that it cannot resolve after an interrupt. 2. Shared-union entry: `claude.ask-user-question` (+ ClaudeAskUserQuestionState / Option), the app-side mirror of the headless parser's shape — the type the wire snapshot is validated against on this side of the submodule import wall. 3. Minimal renderer consumer: a 'QUESTION' dispatch attention badge when the live condition is present (selectors.ts). Purely informational — it cannot touch the inline picker's render gate (stays semantic, `!resultAt`) or its answering path, so it can't regress the working single-select flow. The answerability gate + multi-step answering driver are PR-5. Why no screen-snapshot shortcut: presence/dismissal of the inline row stays SEMANTIC (the tool block's resultAt); the screen-derived condition is a secondary signal only. That split is the point of the framework. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The agent-code PR must point at the merge commit that now exists on claude-code-headless main, not the pre-merge topic branch head. Keeping the gitlink on the integrated SHA avoids depending on a branch-only object after the submodule PR lands. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
PR-4 of the conditions-framework stack — integrates Claude's
AskUserQuestionpicker with the modular conditions system and fixes the interrupted-question ghost render. Targetsintegration/conditions-framework.Pairs with claude-code-headless#26 (gitlink bumped
9939c1c → a0eeb74), which makes the picker a first-class, deduped condition in the unified snapshot.1. Ghost-render fix — the reported bug
An
AskUserQuestiontool_use blocks the agent until atool_resultlands. If the user interrupts instead of answering (Esc / sends a new prompt), no result is ever emitted, so the block stayedresultAt == nullforever andBlockRowkept routing it to the live, clickable picker — it ghosted in the feed indefinitely.foldEvent.tsturn_stoppednow stampsresultAton pendingAskUserQuestionblocks. This is the transcript-backed dismissal — not screen-derived, so it can't flicker the way a screen-snapshot gate would. Scoped strictly totoolName === 'AskUserQuestion': every other pending tool can still resolve with a late result across a stop (stamping them would drop real output); AskUserQuestion is unique in that it cannot resolve after an interrupt.2. Shared-union entry
claude.ask-user-question(+ClaudeAskUserQuestionState/…Option) — the app-side mirror of the headless parser's shape, on this side of the submodule import wall.3. Minimal renderer consumer
A
'QUESTION'dispatch attention badge when the live condition is present (selectors.ts). Purely informational — it cannot touch the inline picker's render gate (stays semantic,!resultAt) or its answering path, so it can't regress the working single-select flow.Deliberately deferred to PR-5
The
sendThenReparseanswering driver (multi-select / free-text / multi-question) and the answerability gate that closes the documented "answered-via-terminal → stray digit" race. Those are coupled to the driver, not to this PR; building them here would half-wire the driver or risk regressing the working path.Why no screen-snapshot shortcut
Presence/dismissal of the inline row stays semantic (the tool block's
resultAt); the screen-derived condition is a secondary signal (badge now, answering affordances in PR-5). That split is the entire point of the framework over the oldsnap.askUserQuestiondirection.Verification
tsc -b(web + node): no new errors — 15 pre-existing failures in unrelated subsystems (dictation, pidusage, ghostJournal, …), all three touched files clean. Headlesstsc --noEmitclean. Vendor-sync of conditions-core: zero drift.🤖 Generated with Claude Code