Skip to content

PreToolUse:Agent/Task hooks resolve ~140ms after tool_dispatch_end — cannot block subagent dispatch even when the hook is correct #84

Description

@Tigon32

Summary

Even with route-dispatch.sh's PreToolUse:Agent gate working correctly and returning a blocking response, Claude Code's own harness resolves that hook's result after the subagent tool call has already dispatched and completed — making it structurally impossible for the hook to block anything. This is a Claude Code CLI (v2.1.220) timing behavior, not a bug in this plugin's scripts, but it directly undermines the enforcement route-dispatch.sh (and any other blocking PreToolUse:Agent/PreToolUse:Task hook) is meant to provide.

Related to #80 (which covers a separate, plugin-side bash bug in the same script) but distinct: even after that bug is fixed, this harness-timing issue independently means a correctly-computed block decision arrives too late to matter.

Evidence — real production timestamps, ~/.claude/debug/<session>.txt

Captured live via claude --debug hooks, one full Agent-tool dispatch cycle:

21:18:05.229Z  Registering async hook async_hook_34311 (PreToolUse:Agent) with timeout 5000ms
21:18:05.318Z  [Stall] tool_dispatch_start tool=Agent toolUseId=toolu_011vd36gY3jspxkT73nW5YrS
21:18:05.400Z  [API:timing] dispatching to firstParty model=copilot/standard
21:18:05.401Z  [API REQUEST] /v1/messages source=agent:builtin:general-purpose
21:18:08.606Z  Stream started - received first chunk (first byte after 3207ms)
21:18:08.691Z  [Stall] agent_completion ... durationMs=3370 turns=1 finalStopReason=end_turn
21:18:08.691Z  [Stall] tool_dispatch_end tool=Agent toolUseId=toolu_011vd36gY3jspxkT73nW5YrS outcome=ok durationMs=3373

21:18:08.830Z  (~139ms AFTER tool_dispatch_end) Checking hook async_hook_34311 (PreToolUse:Agent) - stdout length: 3
21:18:08.831Z  Found sync response from async_hook_34311: {}
21:18:08.831Z  Hooks: async_hook_34311 (PreToolUse:Agent) produced no response payload — skipping attachment

The subagent's model call was dispatched at 05.400Z and had already fully completed (tool_dispatch_end) at 08.691Zbefore Claude Code even checks async_hook_34311's stdout at 08.830Z. The hook's own registration timeout (5000ms) is irrelevant to this: the tool dispatch doesn't wait on the hook resolving at all; the hook is checked as a separate, later bookkeeping step.

Why this matters here specifically

route-dispatch.sh (this repo) is designed as a PreToolUse:Agent/PreToolUse:Task blocking gate — its own header describes it as "a wall, not advice," meant to exit 2 + stderr to block a subagent dispatch that omits an explicit model. Per the above timing, even a correctly-firing exit 2 cannot block dispatch — by the time Claude Code reads that exit code, the subagent has already run to completion against whatever model it inherited.

This means the entire class of "block a subagent tool call based on its declared inputs" hooks is currently unenforceable via PreToolUse:Agent/PreToolUse:Task on Claude Code 2.1.220, independent of anything in this plugin's own scripts.

Scope of this issue

This is very likely a Claude Code CLI issue rather than something fixable in ruvnet-brain's source — filing here to (a) document it for anyone else debugging why their blocking PreToolUse:Agent hooks appear to have no effect, and (b) track that it should also be reported to Anthropic directly, since the actual fix (checking hook results before or during dispatch, not ~140ms after completion) lives in their CLI.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions