fix(hook-kit): repair agent-spawn model injection and gate high-tier spawns - #316
Merged
Conversation
Gate A emitted `updatedInput: { model: "sonnet" }`, but updatedInput REPLACES
the tool input wholesale rather than being merged into it. Every spawn without
an explicit model therefore lost description / prompt / subagent_type and died
on schema validation with "The required parameter `description` is missing".
The gate was meant to make model-less spawns cheap, not impossible — it turned
them into a hard failure, and the only workaround was to always pass `model`
explicitly, which defeats the default.
Echo the caller's own tool_input back with just `model` added.
Gate A only fires when `model` is absent, so it says nothing about a spawn that explicitly asks for an expensive tier. Choosing WHAT work to run (a next-action option) and approving WHICH TIER runs it are separate axes; collapsing them is what produced an unapproved fable spawn after the user merely picked a "Delegate to Fable" work item and the selection was read as tier consent. Add Gate C: a spawn with model opus/fable is blocked unless the prompt carries [tier-approved], or an AskUserQuestion tool_use appears in the current turn (evidence the user was actually consulted). The turn window is computed the same way edit-guard.sh does it, and an AskUserQuestion answer arrives as a tool_result rather than a user string, so the ask stays inside the window. sonnet/haiku spawns and the Gate A default path are unaffected.
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.
Two fixes to
block-agent-spawn-without-model.sh, split so the bug fix is cherry-pickable independently of the new policy gate.Changes
fix(hook-kit)— preserve tool input when injecting the default modelGate A emitted
updatedInput: { model: "sonnet" }, butupdatedInputREPLACES the tool input wholesale rather than being merged into it. Every spawn without an explicit model therefore lostdescription/prompt/subagent_typeand died on schema validation with "The required parameterdescriptionis missing".The gate was meant to make model-less spawns cheap, not impossible — it turned them into a hard failure, and the only workaround was to always pass
modelexplicitly, which defeats the default. Now echoes the caller's owntool_inputback with justmodeladded.fix(hook-kit)— gate opus/fable spawns on explicit user approvalGate A only fires when
modelis absent, so it said nothing about a spawn that explicitly asks for an expensive tier. Choosing WHAT work to run and approving WHICH TIER runs it are separate axes; collapsing them is what produced an unapproved high-tier spawn after a work item was selected and the selection was read as tier consent.Gate C blocks
opus/fableunless the prompt carries[tier-approved], or anAskUserQuestiontool_use appears in the current turn. The turn window is computed the same wayedit-guard.shdoes it, and anAskUserQuestionanswer arrives as a tool_result rather than a user string, so the ask stays inside the window.sonnet/haikuspawns and the Gate A default path are unaffected.Test plan
model: sonnet(was: schema-validation failure)modelstill emits nothing (no injection)run_in_backgroundstill exits 2opusandfablewithout approval exit 2;[tier-approved]passes; an in-turn ask passes;sonnetunaffectedbash -nclean; full pre-push suite green (24 tests) on push