mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-13 16:04:58 +02:00
test: tighten plan-ceo-review smoke to require Step 0 fires first
Assertion narrows from ['asked', 'plan_ready'] to 'asked' only. Reaching
plan_ready first means the agent skipped Step 0 entirely and went
straight to ExitPlanMode — the regression we want to catch.
Why plan-ceo is special: unlike plan-eng / plan-design / plan-devex
(whose smokes legitimately reach plan_ready on certain branches without
asking), plan-ceo-review's template mandates Step 0A premise challenge
plus Step 0F mode selection BEFORE any plan write. There is no
legitimate path to plan_ready that does not first emit a skill-question
numbered prompt.
Failure message now branches on outcome (plan_ready vs timeout vs
silent_write) with a tailored diagnosis line per case. References the
skill template by section name ("Step 0 STOP rules", "One issue = one
AskUserQuestion call") instead of line numbers, so it survives template
edits.
Passes env: { QUESTION_TUNING: 'false', EXPLAIN_LEVEL: 'default' }
through the runner. Today this is advisory — gstack-config reads only
~/.gstack/config.yaml, not env vars — but the wiring is in place for a
future change. Documented honestly in the docstring.
Verified across 4 PTY runs: 3 pre-refactor + 1 post-refactor, all PASS.
This commit is contained in:
@@ -1,22 +1,34 @@
|
|||||||
/**
|
/**
|
||||||
* plan-ceo-review plan-mode smoke (gate, paid, real-PTY).
|
* plan-ceo-review plan-mode smoke (gate, paid, real-PTY).
|
||||||
*
|
*
|
||||||
* Asserts: when /plan-ceo-review is invoked in plan mode, the skill reaches
|
* Asserts: when /plan-ceo-review is invoked in plan mode, the FIRST terminal
|
||||||
* a terminal outcome that is either:
|
* outcome is 'asked' — a skill-question numbered list. Permission dialogs
|
||||||
* - 'asked' — skill emitted its Step 0 numbered prompt (scope mode
|
* (which also render numbered lists) are filtered out by `runPlanSkillObservation`
|
||||||
* selection, or the routing-injection prompt that runs
|
* via its `isPermissionDialogVisible(visible.slice(-1500))` short-circuit.
|
||||||
* before Step 0)
|
|
||||||
* - 'plan_ready' — skill ran end-to-end and surfaced claude's native
|
|
||||||
* "Ready to execute" confirmation
|
|
||||||
*
|
*
|
||||||
* FAIL conditions: silent Write/Edit before any prompt, claude crash,
|
* Reaching 'plan_ready' first IS the regression we want to catch: the agent
|
||||||
* timeout.
|
* skipped Step 0 entirely and went straight to ExitPlanMode. The original
|
||||||
|
* failure had the assistant read a diff, write a plan with two issues, and
|
||||||
|
* call ExitPlanMode without ever firing AskUserQuestion — the user had to
|
||||||
|
* manually call out the missing per-issue questions.
|
||||||
*
|
*
|
||||||
* Replaces the SDK-based test that never worked: the SDK's canUseTool
|
* Why this skill is special: unlike plan-eng-review / plan-design-review /
|
||||||
* interceptor on AskUserQuestion never fires in plan mode because plan
|
* plan-devex-review (whose smokes accept either 'asked' or 'plan_ready'),
|
||||||
* mode renders its native confirmation as TTY UI, not via the
|
* plan-ceo-review's template mandates Step 0A premise challenge (3 baked-in
|
||||||
* AskUserQuestion tool. The real PTY harness observes the rendered
|
* questions) AND Step 0F mode selection BEFORE any plan write. There is no
|
||||||
* terminal output directly.
|
* legitimate path to plan_ready that does not first emit a skill-question
|
||||||
|
* numbered prompt.
|
||||||
|
*
|
||||||
|
* Env passthrough: passes `QUESTION_TUNING=false` and `EXPLAIN_LEVEL=default`
|
||||||
|
* via the runner's env option. Today these are advisory — `gstack-config`
|
||||||
|
* reads `~/.gstack/config.yaml`, not env vars, so a contributor with
|
||||||
|
* `question_tuning: true` set in their YAML config can still see AUTO_DECIDE
|
||||||
|
* masking. The env passthrough is wired so a future gstack-config change to
|
||||||
|
* honor env overrides will make this test hermetic without further edits.
|
||||||
|
* Tracked as a post-merge follow-up.
|
||||||
|
*
|
||||||
|
* FAIL conditions: 'plan_ready' first, silent Write/Edit before any prompt,
|
||||||
|
* claude crash, timeout.
|
||||||
*
|
*
|
||||||
* See test/helpers/claude-pty-runner.ts for runner internals.
|
* See test/helpers/claude-pty-runner.ts for runner internals.
|
||||||
*/
|
*/
|
||||||
@@ -28,21 +40,33 @@ const shouldRun = !!process.env.EVALS && process.env.EVALS_TIER === 'gate';
|
|||||||
const describeE2E = shouldRun ? describe : describe.skip;
|
const describeE2E = shouldRun ? describe : describe.skip;
|
||||||
|
|
||||||
describeE2E('plan-ceo-review plan-mode smoke (gate)', () => {
|
describeE2E('plan-ceo-review plan-mode smoke (gate)', () => {
|
||||||
test('reaches a terminal outcome (asked or plan_ready) without silent writes', async () => {
|
test('first terminal outcome is asked (Step 0 fires before any plan write)', async () => {
|
||||||
const obs = await runPlanSkillObservation({
|
const obs = await runPlanSkillObservation({
|
||||||
skillName: 'plan-ceo-review',
|
skillName: 'plan-ceo-review',
|
||||||
inPlanMode: true,
|
inPlanMode: true,
|
||||||
timeoutMs: 300_000,
|
timeoutMs: 300_000,
|
||||||
|
env: { QUESTION_TUNING: 'false', EXPLAIN_LEVEL: 'default' },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (obs.outcome === 'silent_write' || obs.outcome === 'exited' || obs.outcome === 'timeout') {
|
if (obs.outcome !== 'asked') {
|
||||||
|
const diagnosis =
|
||||||
|
obs.outcome === 'plan_ready'
|
||||||
|
? `'plan_ready' first means the agent skipped Step 0 entirely and went straight to ExitPlanMode without asking.`
|
||||||
|
: obs.outcome === 'timeout'
|
||||||
|
? `Timeout means the agent neither asked nor completed within the budget — likely hung mid-question or stuck on a permission dialog.`
|
||||||
|
: obs.outcome === 'silent_write'
|
||||||
|
? `Silent Write/Edit fired to an unsanctioned path before any AskUserQuestion — also a Step 0 skip.`
|
||||||
|
: `Outcome '${obs.outcome}' is unexpected; investigate the evidence below.`;
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`plan-ceo-review plan-mode smoke FAILED: outcome=${obs.outcome}\n` +
|
`plan-ceo-review smoke FAILED: outcome=${obs.outcome}\n` +
|
||||||
|
`${diagnosis}\n` +
|
||||||
|
`Expected 'asked'. See plan-ceo-review/SKILL.md.tmpl: the Step 0 STOP rules ` +
|
||||||
|
`and the "One issue = one AskUserQuestion call" rule under "CRITICAL RULE — ` +
|
||||||
|
`How to ask questions".\n` +
|
||||||
`summary: ${obs.summary}\n` +
|
`summary: ${obs.summary}\n` +
|
||||||
`elapsed: ${obs.elapsedMs}ms\n` +
|
`elapsed: ${obs.elapsedMs}ms\n` +
|
||||||
`--- evidence (last 2KB visible) ---\n${obs.evidence}`,
|
`--- evidence (last 2KB visible) ---\n${obs.evidence}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
expect(['asked', 'plan_ready']).toContain(obs.outcome);
|
|
||||||
}, 360_000);
|
}, 360_000);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user