garrytan / garrytan/gstack

Conductor + Claude: plan-tune PreToolUse hook breaks native AskUserQuestion (auto-installed in Conductor)

Open
#2,207 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

## Summary

In **Conductor** sessions running **Claude Code**, gstack's plan-tune AUQ hooks **break Conductor's native `AskUserQuestion`**. Every AUQ call returns `[Tool result missing due to internal error]` (or the input is echoed with no rendered options). The same Conductor workspace running **Codex/GPT works fine**, because Codex doesn't load `~/.claude/settings.json` hooks.

The important part: the premise behind the v1.58.1.0 "Conductor prose AskUserQuestion" feature — *"Conductor disables native AUQ and its MCP variant is flaky"* — appears to be **self-inflicted**. Conductor's AUQ is not flaky. gstack's own PreToolUse hook is what breaks it, and then the code attributes the failure to Conductor and routes to prose.

## Impact

- Claude + Conductor users lose clickable `AskUserQuestion` entirely (every decision silently degrades to prose, or errors).
- It's auto-installed with no opt-in: `setup` treats a Conductor workspace as an implicit opt-in for the plan-tune hooks (see below), so a routine `/gstack-upgrade` silently plants the breakage.

## Root cause

`AskUserQuestion` in Conductor + Claude Code is resolved through the Agent-SDK `canUseTool` elicitation bridge (`CLAUDE_CODE_ENTRYPOINT=sdk-ts`; the `mcp__conductor__AskUserQuestion` tool is injected by Conductor's harness, not present in `~/.claude.json`).

gstack registers a **PreToolUse hook** (`hosts/claude/hooks/question-preference-hook.ts`, source `plan-tune-cathedral`) on the matcher `(AskUserQuestion|mcp__.*__AskUserQuestion)`. When that hook participates in the permission flow — **even when it returns `permissionDecision: "defer"`** — it corrupts the `canUseTool` round-trip, and the tool result comes back missing.

`setup` auto-installs it in Conductor:

```sh
# setup, ~line 1389
if [ "$PT_DECISION" = "prompt" ] && { [ -n "${CONDUCTOR_WORKSPACE_PATH:-}" ] || [ -n "${CONDUCTOR_PORT:-}" ]; }; then
PT_DECISION="yes"
_PT_CONDUCTOR_AUTO=1
fi
```

Introduced in **v1.58.1.0** (`c7ae632`, "feat: hermetic local E2E + Conductor prose AskUserQuestion").

## Reproduction

1. Open a Conductor workspace, Claude Code agent, with gstack's plan-tune hooks installed (the Conductor default).
2. Trigger any `AskUserQuestion` / `mcp__conductor__AskUserQuestion` call.
3. Observe: no options render; result is `[Tool result missing due to internal error]`.
4. Remove the AUQ hooks from `~/.claude/settings.json` (or `gstack-config set plan_tune_hooks no`, delete the `hooks` block, restart).
5. Trigger AUQ again → it renders and works. (Verified live, 3×.)

Cross-check: the identical Conductor workspace on Codex/GPT renders AUQ fine throughout — Codex never reads `~/.claude/settings.json`, so the hook is never in the path. This isolates the hook as the cause.

Note: gating the `deny` behind a flag so the hook returns `defer` does **not** fix it — the hook has to be fully **unregistered**. Its mere presence in the permission flow is enough to break the bridge.

## Suggested fix

At minimum, stop auto-installing the AUQ **PreToolUse** hook in Conductor, since it's what breaks native AUQ there:

```diff
- # Conductor host reliability: ... Treat Conductor as an implicit opt-in ...
- if [ "$PT_DECISION" = "prompt" ] && { [ -n "${CONDUCTOR_WORKSPACE_PATH:-}" ] || [ -n "${CONDUCTOR_PORT:-}" ]; }; then
- PT_DECISION="yes"
- _PT_CONDUCTOR_AUTO=1
- fi
```

More fundamentally, it's worth re-validating the whole "Conductor prose AUQ" premise: native AUQ works in Conductor when this PreToolUse hook isn't registered, so the flakiness the feature works around looks like it's caused by the feature itself. If the PostToolUse capture/fallback hooks are confirmed harmless to the bridge (they fire after the result, so they likely are), the preference-enforcement value could be kept by only dropping the PreToolUse participation in Conductor — but that needs a test on real Conductor hardware.

## Workaround (for affected users)

```sh
# unregister the AUQ hooks and stop setup from re-adding them (even in Conductor)
gstack-config set plan_tune_hooks no
# then remove the plan-tune-cathedral / auq-error-fallback hooks from ~/.claude/settings.json
```

## Environment

- gstack `v1.58.5.0`
- Claude Code `2.1.201` via Agent SDK (`CLAUDE_CODE_ENTRYPOINT=sdk-ts`)
- Conductor (macOS, local mode)
- Regression introduced in gstack `v1.58.1.0` (`c7ae632`)

Happy to open a PR from a fork if the minimal `setup` change above looks right to you.

Contributor guide

Open the contributing guide

Research direction

Start in setup around line 1389 and inspect hosts/claude/hooks/question-preference-hook.ts, including the plan-tune-cathedral matcher. Reproduce in a Conductor workspace with Claude Code, then verify that setup no longer auto-registers the AUQ PreToolUse hook and that native AskUserQuestion renders successfully without it.

Written by the indexing model from the issue text.

Assessment

Tech stack
shell, typescript
Domain
cli, devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.