feat: plan-first turn for multi-part questions - #24
Merged
Conversation
Asked for revenue AND ticket counts per customer, the agent wrote one query joining both children of the same parent and silently multiplied the rows - returning 3405 for a customer whose revenue is 1702.50. The grain warning added in 6.2 was already in the context for that exact entity and did not prevent it, because the agent wrote one confident query in a single step. So the fix is a turn with NO tools offered, forcing it to commit to an approach in writing before it can act. The trigger is deliberately narrow. A false negative costs nothing; a false positive spends a turn on every question. A bare 'and' would fire on 'give the customer name and the amount' - one quantity in two columns, already answered correctly - so it keys on a genuine second quantity or instruction. Write-verb questions are excluded outright: measured, planning them turned a wrong number into a blank refusal, which is the over-refusal failure the verification pass exists to prevent. Their failure was never decomposition, so safety stays verification's job and planning stays decomposition's. Measured on the one case in either suite that triggers it: 1/8 runs without planning, 8/8 with (a repeat-5 run plus a full-suite repeat-3 run each way). Suite level, stated honestly: the hard A/B moved +30.6 to +33.3 (grounded 31/36 to 33/36). That is close to one noise unit, and necessarily so - planning fires on 1 of 12 cases, so the most it could ever move the aggregate is 8.3 points. The case-level result is strong, the suite-level result is weak, and those are different claims. Two implementation bugs the measurement caught, neither visible to the scripted tests: the transcript ended on an assistant turn, which the API rejects outright, and the agent finalized on the plan without running anything in 2 of 5 runs until the handoff message said plainly that the plan is not the answer. A test asserts a non-triggering question takes a byte-identical path, so the feature is provably free when it does not apply.
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.
Why
Asked for revenue and ticket counts per customer, the agent wrote one query joining both children of the same parent and silently multiplied the rows - returning 3,405 for a customer whose revenue is 1,702.50, because that customer has two tickets.
The grain warning added in ROADMAP 6.2 was already in the context for that exact entity and did not prevent it: the agent wrote one confident query in a single step. So a passive warning was demonstrably not enough, which is the argument for an active turn.
What
A planning turn with no tools offered, so the model must commit to an approach in writing before it can act. It fires only on questions asking for more than one thing.
The trigger is deliberately narrow - a false negative costs nothing, a false positive spends a turn on every question. A bare "and" would have fired on "give the customer name and the amount", which is one quantity in two columns and already answers correctly.
Write-verb questions are excluded outright. Measured: planning them turned a wrong number into a blank refusal - the over-refusal failure the verification pass exists to prevent. Their failure was never decomposition (the agent already separates the parts), so safety stays verification's job.
Measured
On the one case in either suite that triggers it:
(a
--repeat 5run plus a full-suite--repeat 3run each way)Suite level, stated honestly: the hard A/B moved +30.6 to +33.3 (grounded 31/36 to 33/36). That is close to one noise unit, and necessarily so - planning fires on 1 of 12 cases, so the most it could ever move the aggregate is 8.3 points. The case-level result is strong; the suite-level result is weak. Those are different claims and both are recorded.
Two bugs the measurement caught
Neither was visible to the scripted tests, because they do not validate message ordering or force the model to act:
Verification
npm run checkexit 0;npm run test:cli147 pass / 0 fail--no-plandisables it; the flag flows throughaskand both eval suites, and is recorded in the report configROADMAP step 7.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CKZ9UVgw2Mhiu6T5YAG7k6