Skip to content

feat: plan-first turn for multi-part questions - #24

Merged
kiyeonjeon21 merged 1 commit into
mainfrom
feat/planning-pass
Jul 25, 2026
Merged

feat: plan-first turn for multi-part questions#24
kiyeonjeon21 merged 1 commit into
mainfrom
feat/planning-pass

Conversation

@kiyeonjeon21

Copy link
Copy Markdown
Owner

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:

passing runs
without planning 1/8
with planning 8/8

(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. 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:

  1. The transcript ended on an assistant turn, which the API rejects outright - both target cases errored on the first real run.
  2. 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.

Verification

  • npm run check exit 0; npm run test:cli 147 pass / 0 fail
  • A test asserts a non-triggering question takes a byte-identical path, so the feature is provably free when it does not apply
  • --no-plan disables it; the flag flows through ask and both eval suites, and is recorded in the report config

ROADMAP step 7.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CKZ9UVgw2Mhiu6T5YAG7k6

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.
@kiyeonjeon21
kiyeonjeon21 merged commit 3461a83 into main Jul 25, 2026
1 check passed
@kiyeonjeon21
kiyeonjeon21 deleted the feat/planning-pass branch July 25, 2026 17:43
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