Skip to content

fix(conductor): stop AskUserQuestion silently vanishing; force corral-question - #409

Merged
jrothrock merged 2 commits into
mainfrom
fix/conductor-askuserquestion
Sep 11, 2026
Merged

jrothrock merged 2 commits into
mainfrom
fix/conductor-askuserquestion

Conversation

@jrothrock

Copy link
Copy Markdown
Collaborator

Symptom

The global-chat conductor's questions to the user were intermittent — "sometimes it works, sometimes nothing comes through."

Root cause

The conductor is a headless claude -p process. It had two ways to ask a question:

  1. The native AskUserQuestion tool — auto-dismissed in headless mode (there's no interactive channel to answer it over the stream). The call returns an empty "dismissed" result and the user sees nothing.
  2. The corral-question fenced convention — plain text the UI renders as one-click chips. This is answerable and works.

The old guidance only suggested the convention ("you MAY ask with a fenced block"), so the model frequently reached for the native tool instead → question silently vanished. In conv 160 the conductor called AskUserQuestion twice (seq 388, 428); both were dismissed and it wrote "It looks like the question prompt was dismissed." When it happened to pick corral-question, the chips appeared — hence the intermittency.

Fix (both halves required — verified against claude 2.1.186)

  • Deny the native AskUserQuestion for the conductor via --disallowedTools, removing the silent-vanish path. (Only the global-chat conductor; project/worker chats unaffected.)
  • Rewrite chatQuestionGuidance from permissive to imperative: "you do NOT have an interactive question tool; the ONLY way to ask is a corral-question block." Denial alone isn't enough — without this the model can dead-end on the blocked tool instead of falling back. I tested this exact config (denied tool + imperative guidance) and the model produced a clean corral-question block, is_error: False.

buildClaudeArgs gains a disallowed []string param; callers updated.

Also in this PR

A separate, pre-existing test fix: TestCaptureSendRecordsAndPassesThrough asserted delivered==6 but the capturer legitimately injects a one-shot conv_meta frame (conv id/uuid → UI), so 7 frames reach the browser. The test predated that frame. Now asserts 6 sent + exactly one conv_meta. (CI runs only Playwright e2e, never go test, so it went unnoticed on main.)

Full internal/dashboard package passes.

🤖 Generated with Claude Code

jrothrock and others added 2 commits September 9, 2026 12:32
TestCaptureSendRecordsAndPassesThrough asserted delivered==6, but the
capturer legitimately injects a one-shot conv_meta frame (carrying the
conv id/uuid to the UI) when it first creates the conversation row, so
7 frames reach the browser. The test predates that frame. Assert the 6
sent frames + exactly one conv_meta. Pre-existing failure — CI only runs
Playwright e2e, never go test, so it went unnoticed on main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-question

The global-chat conductor is a headless `claude -p` process. It had two
ways to ask the user a question: the native AskUserQuestion tool and the
`corral-question` fenced convention. The old guidance only *suggested*
the convention ("you MAY"), so the model often reached for the native
tool instead — which is auto-DISMISSED in headless mode (no interactive
channel to answer over the stream). The question then silently vanished:
the user saw nothing and the model got an empty "dismissed" result. It
was intermittent because the model sometimes picked the answerable
convention (which worked) and sometimes the native tool (which didn't).

Fix, both halves needed (verified empirically against claude 2.1.186):
- Deny the native AskUserQuestion for the conductor via --disallowedTools,
  removing the silent-vanish path.
- Rewrite chatQuestionGuidance from permissive to imperative: "you do NOT
  have an interactive question tool; the ONLY way to ask is a
  corral-question block" — so the model routes to the answerable path
  instead of dead-ending on the denied tool.

buildClaudeArgs gains a `disallowed` param; only the conductor (global
chat) passes AskUserQuestion. Project/worker chats are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jrothrock
jrothrock merged commit 9daf71f into main Sep 11, 2026
1 check passed
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.

1 participant