From 89b673e40df6ee99c9e9878ee425ebc4813db80e Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Sun, 26 Jul 2026 14:29:02 -0400 Subject: [PATCH] feat(#2631): planner emits estimate and plan-checker surfaces the over-budget flag (#2670) * test(#2631): failing-first planner estimate emission and over-budget surfacing * feat(#2631): emit plan estimate and surface the over-budget split recommendation * fix(#2631): extract sizing prose to references to fit planner and plan-phase caps * fix(#2631): move estimate check to plan-checker; fix template regex and caps * fix(#2631): restore ALWAYS split literal and keep gsd_run after the launcher preamble * fix(#2631): invoke estimate-check after the launcher preamble in plan-checker * fix(#2631): stop double-applying calibration; repair COMMANDS table and stale reference * chore(#2631): backfill changeset pr to 2670 * chore(#2631): backfill changeset pr to 2670 --- .changeset/noble-ravens-howl.md | 5 + agents/gsd-plan-checker.md | 20 +++ agents/gsd-planner.md | 35 ++-- docs/COMMANDS.md | 2 + docs/reference/plan-md.md | 1 + gsd-core/references/context-budget.md | 40 +++++ src/estimate-cli.cts | 19 +- tests/agent-size-baseline.json | 4 +- .../golden-install-parity/antigravity.json | 6 +- .../golden-install-parity/augment.json | 6 +- .../golden-install-parity/claude-local.json | 6 +- .../golden-install-parity/claude.json | 6 +- .../fixtures/golden-install-parity/cline.json | 6 +- .../golden-install-parity/codebuddy.json | 6 +- .../fixtures/golden-install-parity/codex.json | 10 +- .../golden-install-parity/copilot.json | 6 +- .../golden-install-parity/cursor.json | 6 +- .../golden-install-parity/hermes.json | 6 +- .../fixtures/golden-install-parity/kilo.json | 6 +- .../golden-install-parity/kimi-code.json | 6 +- .../fixtures/golden-install-parity/kimi.json | 6 +- .../golden-install-parity/opencode.json | 6 +- tests/fixtures/golden-install-parity/pi.json | 2 +- .../fixtures/golden-install-parity/qwen.json | 6 +- .../fixtures/golden-install-parity/trae.json | 6 +- .../golden-install-parity/windsurf.json | 6 +- .../fixtures/golden-install-parity/zcode.json | 6 +- tests/phase-estimation.test.cjs | 54 ++++++ tests/planner-estimate-emission.test.cjs | 162 ++++++++++++++++++ 29 files changed, 371 insertions(+), 85 deletions(-) create mode 100644 .changeset/noble-ravens-howl.md create mode 100644 tests/planner-estimate-emission.test.cjs diff --git a/.changeset/noble-ravens-howl.md b/.changeset/noble-ravens-howl.md new file mode 100644 index 000000000..2cc477536 --- /dev/null +++ b/.changeset/noble-ravens-howl.md @@ -0,0 +1,5 @@ +--- +type: Added +pr: 2670 +--- +**Plans now carry a calibrated effort estimate** — every generated PLAN.md includes an `estimate` block, and `/gsd-plan-phase` flags a phase projected to exceed the smart-zone budget with a concrete split recommendation. Advisory only; it never blocks planning. (#2631) diff --git a/agents/gsd-plan-checker.md b/agents/gsd-plan-checker.md index 3ab9bde7d..7be4dcf24 100644 --- a/agents/gsd-plan-checker.md +++ b/agents/gsd-plan-checker.md @@ -252,6 +252,19 @@ issue: 1. Count tasks per plan 2. Estimate files modified per plan 3. Check against thresholds +4. **Smart-zone estimate check (#2631, ADR-2629).** For each plan carrying an `estimate` block, run the + `estimate-check --calibrated` verb against its `estimate.tokens` (the `--calibrated` flag is required — + the plan's figure already has the factor applied, and omitting it would square the correction) (invoked in Step 1 below, after the launcher + preamble). The verb reads `workflow.smart_zone_tokens` and applies the project's calibration. Report + one line per plan: plan id, estimated tokens, the budget, and — when `over_budget` is true — the + returned `recommendation`, which names how many slices the phase should become. + + **Over budget is a WARNING, never a blocker** (ADR-2629 Decision 5). Recommend re-slicing into a tracer + plus expansion slices; never fail the check on it. Report `estimate.confidence` alongside: `low` means + fewer than 3 completed phases carry actuals, so the figure is not yet calibrated for this project — say + so rather than presenting it as precise, and weigh the task/file thresholds above more heavily. + + A plan with no `estimate` block is not a defect; the field is optional and additive. **Thresholds:** | Metric | Target | Warning | Blocker | @@ -706,6 +719,13 @@ gsd_run query phase.list-plans "$phase_number" gsd_run query phase.list-artifacts "$phase_number" --type research gsd_run query roadmap.get-phase "$phase_number" gsd_run query phase.list-artifacts "$phase_number" --type summary + +# Smart-zone estimate check (#2631) — advisory, never fails the check. +for plan in "${phase_dir:-$PHASE_DIR}"/*-PLAN.md; do + [ -f "$plan" ] || continue # unmatched glob leaves the literal pattern — skip it + EST=$(sed -n '/^estimate:/,/^[a-z_]*:/p' "$plan" | grep -o 'tokens: *[0-9]*' | head -1 | grep -o '[0-9]*') + [ -n "$EST" ] && gsd_run query estimate-check --tokens "$EST" --calibrated 2>/dev/null || true +done ``` **Extract:** Phase goal, requirements (decompose goal), locked decisions, deferred ideas. diff --git a/agents/gsd-planner.md b/agents/gsd-planner.md index 040c2acc2..a6a6b320b 100644 --- a/agents/gsd-planner.md +++ b/agents/gsd-planner.md @@ -288,30 +288,15 @@ See @~/.claude/gsd-core/references/planner-guidance.md for dependency graph buil -## Context Budget Rules +## Sizing and the Estimate Block -Plans should complete within ~50% context (not 80%). No context anxiety, quality maintained start to finish, room for unexpected complexity. +Full rules: @~/.claude/gsd-core/references/context-budget.md (Phase Sizing). Read before sizing. -**Each plan: 2-3 tasks maximum.** - -| Context Weight | Tasks/Plan | Context/Task | Total | -|----------------|------------|--------------|-------| -| Light (CRUD, config) | 3 | ~10-15% | ~30-45% | -| Medium (auth, payments) | 2 | ~20-30% | ~40-50% | -| Heavy (migrations, multi-subsystem) | 1-2 | ~30-40% | ~30-50% | - -## Split Signals - -**ALWAYS split if:** -- More than 3 tasks -- Multiple subsystems (DB + API + UI = separate plans) -- Any task with >5 file modifications -- Checkpoint + implementation in same plan -- Discovery + implementation in same plan - -**CONSIDER splitting:** >5 files total, natural semantic boundaries, context cost estimate exceeds 40% for a single plan. See `` for prohibited split reasons. - -See @~/.claude/gsd-core/references/planner-guidance.md for Granularity Calibration table (Coarse/Standard/Fine plans-per-phase). +- **2-3 tasks per plan.** **ALWAYS split if:** >3 tasks, multiple subsystems, or any task touching >5 files. +- **Emit `estimate`** in PLAN.md frontmatter: run the `estimate-calibration` query, multiply your raw + token projection by its calibration factor, and copy its `confidence` verbatim — derived from the + sample count, never self-rated. +- **Over the smart-zone budget?** Re-slice: tracer + expansion slices. Advisory, never a block. @@ -331,6 +316,11 @@ autonomous: true # false if plan has checkpoints requirements: [] # REQUIRED — Requirement IDs from ROADMAP this plan addresses. MUST NOT be empty. user_setup: [] # Human-required setup (omit if empty) +estimate: # Projected execution cost (see Estimate Emission) + tokens: 60000 # calibrated projection + tasks: 3 # task count the projection assumes + confidence: low # low | med | high — DERIVED from sample count, never self-rated + must_haves: truths: [] # Observable behaviors artifacts: [] # Files that must exist @@ -412,6 +402,7 @@ Create `.planning/phases/XX-name/{padded_phase}-{plan}-SUMMARY.md` when done | `autonomous` | Yes | `true` if no checkpoints | | `requirements` | Yes | **MUST** list requirement IDs from ROADMAP. Every roadmap requirement ID MUST appear in at least one plan. | | `user_setup` | No | Human-required setup items | +| `estimate` | No | Projected cost `{tokens, tasks, confidence}`. See Estimate Emission. | | `must_haves` | Yes | Goal-backward verification criteria | Wave numbers are pre-computed during planning. Execute-phase reads `wave` directly from frontmatter. diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 54e91e9de..a5798ca05 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -216,6 +216,8 @@ Research, plan, and verify a phase. | `--tdd` | TDD mode — planner applies `type: tdd` to eligible behavior-adding tasks so each begins with a failing test. Composable with `--mvp`: `--mvp --tdd` produces vertical slices where every behavior-adding task starts red-green. The leading `tracer` task also starts red under `--tdd`. | | `--granularity ` | Override the planning granularity for this invocation, ignoring config. Valid values: `coarse`, `standard`, `fine`. Takes precedence over `granularities.planning`, top-level `granularity`, and `planning.granularity` config. | +**Smart-zone estimate report (#2631).** Every generated PLAN.md carries an optional `estimate` block (`{tokens, tasks, confidence}`). During the plan-check pass, `gsd-plan-checker` runs each plan's `estimate.tokens` through `estimate-check` against the configurable `workflow.smart_zone_tokens` budget (default `100000`) and reports the result; a plan above budget gets a concrete split recommendation. The report is **advisory and never blocks planning**, and it is skipped with `--skip-verify` since it runs inside the verification pass. `confidence` is derived from how many completed phases carry recorded actuals — `low` means fewer than three, so the figure is not yet calibrated for your project. See [ADR-2629](adr/2629-phase-effort-estimation-calibration.md). + **Prerequisites:** `.planning/ROADMAP.md` exists **Produces:** `{phase}-RESEARCH.md`, `{phase}-{N}-PLAN.md`, `{phase}-VALIDATION.md`; `{phase}/SKELETON.md` when Walking Skeleton mode fires diff --git a/docs/reference/plan-md.md b/docs/reference/plan-md.md index 9655d0a5a..3a3da716c 100644 --- a/docs/reference/plan-md.md +++ b/docs/reference/plan-md.md @@ -73,6 +73,7 @@ must_haves: | `requirements` | Yes | array of IDs | Requirement IDs from ROADMAP.md that this plan addresses. Every phase requirement ID must appear in at least one plan's `requirements` field. Empty arrays are a BLOCKER. | | `user_setup` | No | array of objects | External-service setup steps that Claude cannot automate (account creation, secret retrieval, dashboard configuration). When present, execute-phase generates a `USER-SETUP.md` checklist for the developer. | | `status` | No | `superseded` | Marks a plan that was deliberately reassigned or abandoned mid-phase and will never be executed. A `status: superseded` plan is excluded from the phase's plan and summary counts, so it never holds the phase below 100%. See [Superseded plans](#superseded-plans). Any other value (or the field's absence) has no effect on counting. | +| `estimate` | No | object | Projected execution cost: `{tokens, tasks, confidence}` (#2631, [ADR-2629](../adr/2629-phase-effort-estimation-calibration.md)). `tokens` is an `estimateTokens`-scale projection with the project's calibration factor **already applied** (which is why the plan-checker passes `--calibrated` to `estimate-check` — re-applying it would square the correction); `confidence` (`low`/`med`/`high`) is **derived from the calibration sample count, never self-rated**. Additive and optional — a plan without it behaves exactly as before. A plan estimated above `workflow.smart_zone_tokens` is flagged with a split recommendation at plan time; the flag is advisory and never blocks. | | `must_haves` | Yes | object | Goal-backward verification criteria. See below. | ### Superseded plans diff --git a/gsd-core/references/context-budget.md b/gsd-core/references/context-budget.md index 7223976d0..32434bf2e 100644 --- a/gsd-core/references/context-budget.md +++ b/gsd-core/references/context-budget.md @@ -83,3 +83,43 @@ Either list works — `enabledMcpjsonServers` is an explicit allow-list, `disabl ### Composition with model_profile Trimming MCPs and tuning `model_profile` are independent levers that **compound**. Disabling a 25k-token MCP saves 25k per turn whether you're running `quality` (opus everywhere) or `budget` (sonnet/haiku); the savings are additive, not in lieu of model tuning. Don't pick one — do both, and audit MCPs first because the per-turn savings show up immediately and stack across every subagent the orchestrator spawns. + +--- + +# Phase Sizing (gsd-planner) + +## Estimate Emission (#2631, ADR-2629) + +Every plan carries an `estimate` block. It is the quantitative reason a phase must be sliced — tracer-first says *slice thin*, the estimate says *how thin, for this codebase*. + +**Compute it:** +1. Sum `estimateTokens`-scale cost across the plan: implementation + the files each task reads + verification output. Roughly chars/4 over what the executor will actually touch. +2. Run `estimate-calibration` and **multiply your raw figure by its `factor`.** It is the measured estimate-vs-actual ratio for THIS project — a factor of 1 means there is not yet enough history to correct. +3. `confidence` is **derived, not judged**: it is the `confidence` value from the same calibration query, keyed to the sample count (`low` <3, `med` 3–5, `high` ≥6). **Do not rate your own certainty.** Self-rated confidence was measured in this project and found weak (`references/honest-verifier.md:25-29`); every signal here routes on measured history instead. + +**Over budget?** The plan-checker flags a plan whose estimate exceeds `workflow.smart_zone_tokens`. This is advisory — it never blocks. When flagged, re-slice: a tracer plus expansion slices, each inside the budget. Prefer more, smaller plans over one that spends the agent's best early-context tokens and finishes degraded. + +## Context Budget Rules + +Plans should complete within ~50% context (not 80%). No context anxiety, quality maintained start to finish, room for unexpected complexity. + +**Each plan: 2-3 tasks maximum.** + +| Context Weight | Tasks/Plan | Context/Task | Total | +|----------------|------------|--------------|-------| +| Light (CRUD, config) | 3 | ~10-15% | ~30-45% | +| Medium (auth, payments) | 2 | ~20-30% | ~40-50% | +| Heavy (migrations, multi-subsystem) | 1-2 | ~30-40% | ~30-50% | + +## Split Signals + +**ALWAYS split if:** +- More than 3 tasks +- Multiple subsystems (DB + API + UI = separate plans) +- Any task with >5 file modifications +- Checkpoint + implementation in same plan +- Discovery + implementation in same plan + +**CONSIDER splitting:** >5 files total, natural semantic boundaries, context cost estimate exceeds 40% for a single plan. See `` for prohibited split reasons. + +See @~/.claude/gsd-core/references/planner-guidance.md for Granularity Calibration table (Coarse/Standard/Fine plans-per-phase). diff --git a/src/estimate-cli.cts b/src/estimate-cli.cts index a1c5e78f1..73406c17a 100644 --- a/src/estimate-cli.cts +++ b/src/estimate-cli.cts @@ -89,7 +89,7 @@ export function readCalibrationSamples(cwd: string): ReturnType', ERROR_REASON.USAGE); + error('Usage: estimate-check --tokens [--calibrated]', ERROR_REASON.USAGE); } const value = args[idx + 1]; @@ -119,15 +119,26 @@ export function parseTokensFlag(args: string[]): number { * in the payload is the signal. */ export function cmdEstimateCheck(cwd: string, args: string[], raw: boolean): void { - const rawTokens = parseTokensFlag(args); + const inputTokens = parseTokensFlag(args); + const preCalibrated = args.includes('--calibrated'); const budget = readSmartZoneBudget(cwd); const calibration = estimation.computeCalibration(readCalibrationSamples(cwd)); - const calibratedTokens = estimation.applyCalibration(rawTokens, calibration.factor); + // `--calibrated` says the caller already applied the factor. Without it we + // would apply the correction a SECOND time and compare factor^2 against the + // budget — with the [0.5, 3.0] clamp that is anywhere from 4x under to 9x + // over, and it is invisible until a project reaches 3 samples (below that + // factor === 1, and 1^2 === 1). A plan's recorded `estimate.tokens` is + // calibrated at emission time per ADR-2629 Decision 1, so the plan-checker + // MUST pass this flag. + const calibratedTokens = preCalibrated + ? inputTokens + : estimation.applyCalibration(inputTokens, calibration.factor); const classification = estimation.classifyAgainstBudget(calibratedTokens, budget); output({ - raw_tokens: rawTokens, + raw_tokens: inputTokens, calibrated_tokens: calibratedTokens, + pre_calibrated: preCalibrated, budget, over_budget: classification.overBudget, budget_valid: classification.budgetValid, diff --git a/tests/agent-size-baseline.json b/tests/agent-size-baseline.json index afb6f5fa5..78d09ea4c 100644 --- a/tests/agent-size-baseline.json +++ b/tests/agent-size-baseline.json @@ -22,8 +22,8 @@ "gsd-nyquist-auditor.md": 7345, "gsd-pattern-mapper.md": 12487, "gsd-phase-researcher.md": 40866, - "gsd-plan-checker.md": 44780, - "gsd-planner.md": 49327, + "gsd-plan-checker.md": 46363, + "gsd-planner.md": 49251, "gsd-project-researcher.md": 22242, "gsd-research-synthesizer.md": 13847, "gsd-roadmapper.md": 22273, diff --git a/tests/fixtures/golden-install-parity/antigravity.json b/tests/fixtures/golden-install-parity/antigravity.json index 4253c8bcc..f3827ef42 100644 --- a/tests/fixtures/golden-install-parity/antigravity.json +++ b/tests/fixtures/golden-install-parity/antigravity.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "805a1404d03629f1", "agents/gsd-pattern-mapper.md": "e62ee90d39084802", "agents/gsd-phase-researcher.md": "cff1196c8e8bb4fa", - "agents/gsd-plan-checker.md": "dd1e7cdc837d8f3e", - "agents/gsd-planner.md": "c7ba1adfe2b1975d", + "agents/gsd-plan-checker.md": "893e88036b4ad419", + "agents/gsd-planner.md": "8af42f4321c34bc9", "agents/gsd-project-researcher.md": "85de7f562872ee9b", "agents/gsd-research-synthesizer.md": "18a2e1b30ff7ae3a", "agents/gsd-roadmapper.md": "7a8465ac6d4dd29e", @@ -58,7 +58,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "efd80aca449032ad", "gsd-core/references/checkpoints.md": "130bb6ef705fc065", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "177520ead2ae3a23", + "gsd-core/references/context-budget.md": "0c8a4b3dee1f6666", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "4c13da220b6a4261", "gsd-core/references/debugger-fix-acceptance.md": "87d367e4e864910e", diff --git a/tests/fixtures/golden-install-parity/augment.json b/tests/fixtures/golden-install-parity/augment.json index cc0f460c2..96f5968f5 100644 --- a/tests/fixtures/golden-install-parity/augment.json +++ b/tests/fixtures/golden-install-parity/augment.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "230e6667411ccf2f", "agents/gsd-pattern-mapper.md": "43c6021cf7caabfa", "agents/gsd-phase-researcher.md": "f1f6fd6a3e67c7a8", - "agents/gsd-plan-checker.md": "bf1a4e636f2390de", - "agents/gsd-planner.md": "f0195f37cafb303b", + "agents/gsd-plan-checker.md": "d9aca5f75d649fc0", + "agents/gsd-planner.md": "5b018b0a2df93e37", "agents/gsd-project-researcher.md": "4531b7cc8f5e5f7d", "agents/gsd-research-synthesizer.md": "4a4f68e6c75b133a", "agents/gsd-roadmapper.md": "bb2f57695dbab32c", @@ -129,7 +129,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "c2fe89c42ca88349", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "f1ce57bf418824af", + "gsd-core/references/context-budget.md": "6749084395889c6a", "gsd-core/references/continuation-format.md": "ce73e57d053ab8a7", "gsd-core/references/debugger-bug-taxonomy.md": "78fb8c1711acc971", "gsd-core/references/debugger-fix-acceptance.md": "5616622f33e00436", diff --git a/tests/fixtures/golden-install-parity/claude-local.json b/tests/fixtures/golden-install-parity/claude-local.json index 5021f5d2d..e508c8acf 100644 --- a/tests/fixtures/golden-install-parity/claude-local.json +++ b/tests/fixtures/golden-install-parity/claude-local.json @@ -23,8 +23,8 @@ "agents/gsd-nyquist-auditor.md": "f86a28f5c164a0d3", "agents/gsd-pattern-mapper.md": "b45b5e106775bec1", "agents/gsd-phase-researcher.md": "4772d9eada32e8bd", - "agents/gsd-plan-checker.md": "75851b147f35354a", - "agents/gsd-planner.md": "352243b5b6c87c36", + "agents/gsd-plan-checker.md": "b3f510f1257ff383", + "agents/gsd-planner.md": "831977f45c361561", "agents/gsd-project-researcher.md": "d7f355894519f9fe", "agents/gsd-research-synthesizer.md": "1c738df9932d325a", "agents/gsd-roadmapper.md": "453e9471ad27c7ea", @@ -128,7 +128,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "c2fe89c42ca88349", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "6bfac08025ea3106", + "gsd-core/references/context-budget.md": "3565d2150c2124e1", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "78fb8c1711acc971", "gsd-core/references/debugger-fix-acceptance.md": "5616622f33e00436", diff --git a/tests/fixtures/golden-install-parity/claude.json b/tests/fixtures/golden-install-parity/claude.json index ac584464f..c5e737b05 100644 --- a/tests/fixtures/golden-install-parity/claude.json +++ b/tests/fixtures/golden-install-parity/claude.json @@ -23,8 +23,8 @@ "agents/gsd-nyquist-auditor.md": "0bc0cdad7b3a0013", "agents/gsd-pattern-mapper.md": "b45b5e106775bec1", "agents/gsd-phase-researcher.md": "85217c69c1ed2ac6", - "agents/gsd-plan-checker.md": "c70134c61b969589", - "agents/gsd-planner.md": "1ded63b1c2b85729", + "agents/gsd-plan-checker.md": "670e4ad7132a66ca", + "agents/gsd-planner.md": "0f5d0199fbce83f8", "agents/gsd-project-researcher.md": "f468e96f8339d1e0", "agents/gsd-research-synthesizer.md": "7be02e47f4fd901b", "agents/gsd-roadmapper.md": "8a7f1f1256a6aed5", @@ -57,7 +57,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "c2fe89c42ca88349", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "533eae480bfc4bb8", + "gsd-core/references/context-budget.md": "b2c7b1703642a342", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "78fb8c1711acc971", "gsd-core/references/debugger-fix-acceptance.md": "5616622f33e00436", diff --git a/tests/fixtures/golden-install-parity/cline.json b/tests/fixtures/golden-install-parity/cline.json index e1f9c0a7a..b7d9fc472 100644 --- a/tests/fixtures/golden-install-parity/cline.json +++ b/tests/fixtures/golden-install-parity/cline.json @@ -27,8 +27,8 @@ "agents/gsd-nyquist-auditor.md": "4f9bba7b9762835d", "agents/gsd-pattern-mapper.md": "b526065fd2efa19c", "agents/gsd-phase-researcher.md": "c507db2ba66038f4", - "agents/gsd-plan-checker.md": "a609245dbdc4ef2b", - "agents/gsd-planner.md": "5249eb5808e67a6a", + "agents/gsd-plan-checker.md": "70ccc072a52cc4ce", + "agents/gsd-planner.md": "aab8d4d2db4c3d93", "agents/gsd-project-researcher.md": "049f816c6caa4316", "agents/gsd-research-synthesizer.md": "2f7dcbff50371d4c", "agents/gsd-roadmapper.md": "bbb23d3097911516", @@ -61,7 +61,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "f66fb7d5b435682d", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "c154ba00dbbf4477", + "gsd-core/references/context-budget.md": "20caa988a2bca07a", "gsd-core/references/continuation-format.md": "ce73e57d053ab8a7", "gsd-core/references/debugger-bug-taxonomy.md": "63521aea0206d4ac", "gsd-core/references/debugger-fix-acceptance.md": "1057d70f7f2d654a", diff --git a/tests/fixtures/golden-install-parity/codebuddy.json b/tests/fixtures/golden-install-parity/codebuddy.json index d0476ea99..a9df20ed4 100644 --- a/tests/fixtures/golden-install-parity/codebuddy.json +++ b/tests/fixtures/golden-install-parity/codebuddy.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "42676eea063d2ec5", "agents/gsd-pattern-mapper.md": "92cfa2e6c2a06bf3", "agents/gsd-phase-researcher.md": "6338474da1a5d65e", - "agents/gsd-plan-checker.md": "e704c083b02e8c35", - "agents/gsd-planner.md": "4b0e9f592f31bc1d", + "agents/gsd-plan-checker.md": "3fdccb7353c69340", + "agents/gsd-planner.md": "55445462761df7b3", "agents/gsd-project-researcher.md": "e43c59f7f1f2f37a", "agents/gsd-research-synthesizer.md": "87955470c3c129b2", "agents/gsd-roadmapper.md": "20b69eff61a7a9fa", @@ -129,7 +129,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "c2fe89c42ca88349", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "f1ce57bf418824af", + "gsd-core/references/context-budget.md": "6749084395889c6a", "gsd-core/references/continuation-format.md": "ce73e57d053ab8a7", "gsd-core/references/debugger-bug-taxonomy.md": "78fb8c1711acc971", "gsd-core/references/debugger-fix-acceptance.md": "5616622f33e00436", diff --git a/tests/fixtures/golden-install-parity/codex.json b/tests/fixtures/golden-install-parity/codex.json index f35e7fc39..1f6199621 100644 --- a/tests/fixtures/golden-install-parity/codex.json +++ b/tests/fixtures/golden-install-parity/codex.json @@ -118,10 +118,10 @@ "agents/gsd-pattern-mapper.toml": "48a17baa3d7d142c", "agents/gsd-phase-researcher.md": "87d74324e7708ea0", "agents/gsd-phase-researcher.toml": "44a3d510cd0ce3bd", - "agents/gsd-plan-checker.md": "e7f02c10ea788aee", - "agents/gsd-plan-checker.toml": "6f8ceb421d0ad721", - "agents/gsd-planner.md": "ef417468fecb841f", - "agents/gsd-planner.toml": "4c9feaf9e76d6655", + "agents/gsd-plan-checker.md": "9ba8ea7643f0a7f5", + "agents/gsd-plan-checker.toml": "03488397d892ea09", + "agents/gsd-planner.md": "88283bc1d8da011e", + "agents/gsd-planner.toml": "1b24b3f19b8afecc", "agents/gsd-project-researcher.md": "959f2e57c3d69ed8", "agents/gsd-project-researcher.toml": "f395e8e8c4baf1ed", "agents/gsd-research-synthesizer.md": "497f85adf53259ef", @@ -164,7 +164,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "4156025334411073", "gsd-core/references/checkpoints.md": "d52116f59e92ca43", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "0b67ea1d5db4bc08", + "gsd-core/references/context-budget.md": "8bde1f8516046fa7", "gsd-core/references/continuation-format.md": "e64c0da2b3d0f2f0", "gsd-core/references/debugger-bug-taxonomy.md": "49f94fe75cabba52", "gsd-core/references/debugger-fix-acceptance.md": "b2732420bdbcadcd", diff --git a/tests/fixtures/golden-install-parity/copilot.json b/tests/fixtures/golden-install-parity/copilot.json index b1be9f6d9..4bb5d3df9 100644 --- a/tests/fixtures/golden-install-parity/copilot.json +++ b/tests/fixtures/golden-install-parity/copilot.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.agent.md": "6463a50d90ada125", "agents/gsd-pattern-mapper.agent.md": "b1f488b0fa6a2395", "agents/gsd-phase-researcher.agent.md": "03cfb510a766fe93", - "agents/gsd-plan-checker.agent.md": "c50a5b008ddcbfad", - "agents/gsd-planner.agent.md": "defa418d75a9b3e9", + "agents/gsd-plan-checker.agent.md": "0d68eb258b85e24e", + "agents/gsd-planner.agent.md": "0c222d93778eb371", "agents/gsd-project-researcher.agent.md": "d73bdbe986ffa8a6", "agents/gsd-research-synthesizer.agent.md": "f03eed4aa89e47c5", "agents/gsd-roadmapper.agent.md": "322048cf8ddcb4e5", @@ -59,7 +59,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "efd80aca449032ad", "gsd-core/references/checkpoints.md": "d26b11ab5ece9e61", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "435474d5e10be65a", + "gsd-core/references/context-budget.md": "371da06bb42df63b", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "ddfb2bbc1f3de50a", "gsd-core/references/debugger-fix-acceptance.md": "812922e2b5db9410", diff --git a/tests/fixtures/golden-install-parity/cursor.json b/tests/fixtures/golden-install-parity/cursor.json index d715726f7..76a4cfe71 100644 --- a/tests/fixtures/golden-install-parity/cursor.json +++ b/tests/fixtures/golden-install-parity/cursor.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "e18b0209cea56a2e", "agents/gsd-pattern-mapper.md": "1229c215677f740d", "agents/gsd-phase-researcher.md": "982d59921bed463d", - "agents/gsd-plan-checker.md": "ba51999876d40cf2", - "agents/gsd-planner.md": "a39e021ed91064bc", + "agents/gsd-plan-checker.md": "30bc89279d6586b7", + "agents/gsd-planner.md": "61d89d16ac357320", "agents/gsd-project-researcher.md": "beeac940d3a10e76", "agents/gsd-research-synthesizer.md": "6315f016d55176f4", "agents/gsd-roadmapper.md": "d28e7d4bac46dde2", @@ -129,7 +129,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "273b371c5751f35a", "gsd-core/references/checkpoints.md": "a44e66095240c46e", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "304dcdab82a27623", + "gsd-core/references/context-budget.md": "1aada627aab75f46", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "63c03b8afbd21b8a", "gsd-core/references/debugger-fix-acceptance.md": "86b7cbde0171fafd", diff --git a/tests/fixtures/golden-install-parity/hermes.json b/tests/fixtures/golden-install-parity/hermes.json index 3b35d6288..94805fff5 100644 --- a/tests/fixtures/golden-install-parity/hermes.json +++ b/tests/fixtures/golden-install-parity/hermes.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "8bfde4a9a36318aa", "agents/gsd-pattern-mapper.md": "cea092600aeb3978", "agents/gsd-phase-researcher.md": "2bd0402f33d757ca", - "agents/gsd-plan-checker.md": "4b4e2b475bf5b5c3", - "agents/gsd-planner.md": "07bd1760e8861253", + "agents/gsd-plan-checker.md": "c31dc063b5506aaf", + "agents/gsd-planner.md": "d1fe1e2653cb12de", "agents/gsd-project-researcher.md": "425a7df7f37a5c06", "agents/gsd-research-synthesizer.md": "9d31c87fc2c87ffa", "agents/gsd-roadmapper.md": "64dce5d5f9fa5654", @@ -58,7 +58,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "6e4b08a89c485c71", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "b93e9f47aa1b1753", + "gsd-core/references/context-budget.md": "6e9dbeb47e90493f", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "fd9b10fffb3bd052", "gsd-core/references/debugger-fix-acceptance.md": "b04b913d5ea0ac90", diff --git a/tests/fixtures/golden-install-parity/kilo.json b/tests/fixtures/golden-install-parity/kilo.json index 36660b43b..d6f947e8b 100644 --- a/tests/fixtures/golden-install-parity/kilo.json +++ b/tests/fixtures/golden-install-parity/kilo.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "7bc3cc159c7be987", "agents/gsd-pattern-mapper.md": "6a5408fd11d70391", "agents/gsd-phase-researcher.md": "94818f28c498bb26", - "agents/gsd-plan-checker.md": "56164206242c8caf", - "agents/gsd-planner.md": "739c1b66005be087", + "agents/gsd-plan-checker.md": "2546e311f7e8e0e8", + "agents/gsd-planner.md": "72996d1f61ad7b0f", "agents/gsd-project-researcher.md": "60573a38d3dfd9fe", "agents/gsd-research-synthesizer.md": "1f7cd286c5783c86", "agents/gsd-roadmapper.md": "277e0a3252553ab7", @@ -129,7 +129,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "3986d58011bf9006", "gsd-core/references/checkpoints.md": "d52116f59e92ca43", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "533eae480bfc4bb8", + "gsd-core/references/context-budget.md": "b2c7b1703642a342", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "49f94fe75cabba52", "gsd-core/references/debugger-fix-acceptance.md": "b2732420bdbcadcd", diff --git a/tests/fixtures/golden-install-parity/kimi-code.json b/tests/fixtures/golden-install-parity/kimi-code.json index b790b1f49..82d95d75d 100644 --- a/tests/fixtures/golden-install-parity/kimi-code.json +++ b/tests/fixtures/golden-install-parity/kimi-code.json @@ -52,8 +52,8 @@ "agents/gsd-nyquist-auditor.md": "f2232a1be0835709", "agents/gsd-pattern-mapper.md": "68ecefd60811a669", "agents/gsd-phase-researcher.md": "2235f61764d8e969", - "agents/gsd-plan-checker.md": "bb38f345d3d41edc", - "agents/gsd-planner.md": "4b7405292d3866ad", + "agents/gsd-plan-checker.md": "f3caf89525709445", + "agents/gsd-planner.md": "bf4304ea220f3874", "agents/gsd-project-researcher.md": "f572892f138734ff", "agents/gsd-research-synthesizer.md": "29949bf3f049a8f1", "agents/gsd-roadmapper.md": "840ac933e3b094f9", @@ -86,7 +86,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "c2fe89c42ca88349", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "f1ce57bf418824af", + "gsd-core/references/context-budget.md": "6749084395889c6a", "gsd-core/references/continuation-format.md": "ce73e57d053ab8a7", "gsd-core/references/debugger-bug-taxonomy.md": "78fb8c1711acc971", "gsd-core/references/debugger-fix-acceptance.md": "5616622f33e00436", diff --git a/tests/fixtures/golden-install-parity/kimi.json b/tests/fixtures/golden-install-parity/kimi.json index 595012cfe..20edb907e 100644 --- a/tests/fixtures/golden-install-parity/kimi.json +++ b/tests/fixtures/golden-install-parity/kimi.json @@ -77,9 +77,9 @@ "agents/subagents/gsd-pattern-mapper.yaml": "d1e537e77f953fd4", "agents/subagents/gsd-phase-researcher.md": "897080d9383bd176", "agents/subagents/gsd-phase-researcher.yaml": "7633c8e82617e7cc", - "agents/subagents/gsd-plan-checker.md": "bd302afc01ed40f0", + "agents/subagents/gsd-plan-checker.md": "410639f4c4f16e7a", "agents/subagents/gsd-plan-checker.yaml": "8295181071121db8", - "agents/subagents/gsd-planner.md": "28172805a3e78163", + "agents/subagents/gsd-planner.md": "0384e53e5b5b50e5", "agents/subagents/gsd-planner.yaml": "2e83ee194bcd7fbd", "agents/subagents/gsd-project-researcher.md": "39bc2ec5a8b18283", "agents/subagents/gsd-project-researcher.yaml": "ce12586b0347e2dc", @@ -122,7 +122,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "c2fe89c42ca88349", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "f1ce57bf418824af", + "gsd-core/references/context-budget.md": "6749084395889c6a", "gsd-core/references/continuation-format.md": "ce73e57d053ab8a7", "gsd-core/references/debugger-bug-taxonomy.md": "78fb8c1711acc971", "gsd-core/references/debugger-fix-acceptance.md": "5616622f33e00436", diff --git a/tests/fixtures/golden-install-parity/opencode.json b/tests/fixtures/golden-install-parity/opencode.json index c9101c645..4bf370b70 100644 --- a/tests/fixtures/golden-install-parity/opencode.json +++ b/tests/fixtures/golden-install-parity/opencode.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "7a7e0d1cbafd6b63", "agents/gsd-pattern-mapper.md": "7c6d1d9817a9c1e7", "agents/gsd-phase-researcher.md": "9874110700b41f48", - "agents/gsd-plan-checker.md": "28ca3dc43669894f", - "agents/gsd-planner.md": "b2ea0c457376ac3e", + "agents/gsd-plan-checker.md": "f39aca1ea261d720", + "agents/gsd-planner.md": "15c78c3192e53c9b", "agents/gsd-project-researcher.md": "dae210ae0b3c6e2b", "agents/gsd-research-synthesizer.md": "e02c6ad5d1b74171", "agents/gsd-roadmapper.md": "1658a40b20d8b575", @@ -129,7 +129,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "3986d58011bf9006", "gsd-core/references/checkpoints.md": "d52116f59e92ca43", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "355826e667f9ccd1", + "gsd-core/references/context-budget.md": "876da77377424383", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "49f94fe75cabba52", "gsd-core/references/debugger-fix-acceptance.md": "b2732420bdbcadcd", diff --git a/tests/fixtures/golden-install-parity/pi.json b/tests/fixtures/golden-install-parity/pi.json index 312e69a47..ec1246631 100644 --- a/tests/fixtures/golden-install-parity/pi.json +++ b/tests/fixtures/golden-install-parity/pi.json @@ -25,7 +25,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "c2fe89c42ca88349", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "f1ce57bf418824af", + "gsd-core/references/context-budget.md": "6749084395889c6a", "gsd-core/references/continuation-format.md": "ce73e57d053ab8a7", "gsd-core/references/debugger-bug-taxonomy.md": "78fb8c1711acc971", "gsd-core/references/debugger-fix-acceptance.md": "5616622f33e00436", diff --git a/tests/fixtures/golden-install-parity/qwen.json b/tests/fixtures/golden-install-parity/qwen.json index d16e80d8e..72e11227e 100644 --- a/tests/fixtures/golden-install-parity/qwen.json +++ b/tests/fixtures/golden-install-parity/qwen.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "2bf7126001ba3e70", "agents/gsd-pattern-mapper.md": "83c66c7722e8b165", "agents/gsd-phase-researcher.md": "284e55a86ae46d7f", - "agents/gsd-plan-checker.md": "c8a8fcc8ed38eff0", - "agents/gsd-planner.md": "e85ed2535f6a7624", + "agents/gsd-plan-checker.md": "e80e6d51017be405", + "agents/gsd-planner.md": "f3c8b934ee07020c", "agents/gsd-project-researcher.md": "b5baac64a15c85e2", "agents/gsd-research-synthesizer.md": "6cd9b501dc97bd50", "agents/gsd-roadmapper.md": "c357a77ab919e9e5", @@ -58,7 +58,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "5e2d923303f16667", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "a1351dd40ef8691f", + "gsd-core/references/context-budget.md": "b862a6cda7a5f216", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "47e7929972c06378", "gsd-core/references/debugger-fix-acceptance.md": "ab6a1cce253944f4", diff --git a/tests/fixtures/golden-install-parity/trae.json b/tests/fixtures/golden-install-parity/trae.json index cb8998036..ac49b0e59 100644 --- a/tests/fixtures/golden-install-parity/trae.json +++ b/tests/fixtures/golden-install-parity/trae.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "58efbed3b81b5ede", "agents/gsd-pattern-mapper.md": "b5d7a4abb1baecb9", "agents/gsd-phase-researcher.md": "2256f1f82212c757", - "agents/gsd-plan-checker.md": "523119bd5d6599fe", - "agents/gsd-planner.md": "dbec071e8a52ebd4", + "agents/gsd-plan-checker.md": "aaa9e928de1abba9", + "agents/gsd-planner.md": "ca48b09c8688e70d", "agents/gsd-project-researcher.md": "ddf7794e81300032", "agents/gsd-research-synthesizer.md": "a124b00271748d07", "agents/gsd-roadmapper.md": "493ef92b42b12cf4", @@ -58,7 +58,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "4d24ce110ed6c746", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "b7e9640063775c98", + "gsd-core/references/context-budget.md": "bdff0159e58a8568", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "d0458c690c4487c9", "gsd-core/references/debugger-fix-acceptance.md": "134f83886c343733", diff --git a/tests/fixtures/golden-install-parity/windsurf.json b/tests/fixtures/golden-install-parity/windsurf.json index 7915d0bbc..695c3f9c8 100644 --- a/tests/fixtures/golden-install-parity/windsurf.json +++ b/tests/fixtures/golden-install-parity/windsurf.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "0ef8609fd897e399", "agents/gsd-pattern-mapper.md": "ada0c169daa2f0ec", "agents/gsd-phase-researcher.md": "2a45ebde829555ec", - "agents/gsd-plan-checker.md": "33fbf70b7b24eb1e", - "agents/gsd-planner.md": "8b59834f86f84d31", + "agents/gsd-plan-checker.md": "c68b9bd6382a22a7", + "agents/gsd-planner.md": "b63b7203e0bc7beb", "agents/gsd-project-researcher.md": "f6697b316b5995ba", "agents/gsd-research-synthesizer.md": "04036f38c1d373ea", "agents/gsd-roadmapper.md": "fb62e1e3de84b5f9", @@ -58,7 +58,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "273b371c5751f35a", "gsd-core/references/checkpoints.md": "900530adaf675e38", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "ff843cd6139c8564", + "gsd-core/references/context-budget.md": "7a53f9d14e306e3a", "gsd-core/references/continuation-format.md": "580287399ad3ba68", "gsd-core/references/debugger-bug-taxonomy.md": "5d8c0a266a6d61f8", "gsd-core/references/debugger-fix-acceptance.md": "ddd00cbd78dd3fdf", diff --git a/tests/fixtures/golden-install-parity/zcode.json b/tests/fixtures/golden-install-parity/zcode.json index 0793eb61b..c9a67bce2 100644 --- a/tests/fixtures/golden-install-parity/zcode.json +++ b/tests/fixtures/golden-install-parity/zcode.json @@ -24,8 +24,8 @@ "agents/gsd-nyquist-auditor.md": "f2232a1be0835709", "agents/gsd-pattern-mapper.md": "68ecefd60811a669", "agents/gsd-phase-researcher.md": "2235f61764d8e969", - "agents/gsd-plan-checker.md": "bb38f345d3d41edc", - "agents/gsd-planner.md": "4b7405292d3866ad", + "agents/gsd-plan-checker.md": "f3caf89525709445", + "agents/gsd-planner.md": "bf4304ea220f3874", "agents/gsd-project-researcher.md": "f572892f138734ff", "agents/gsd-research-synthesizer.md": "29949bf3f049a8f1", "agents/gsd-roadmapper.md": "840ac933e3b094f9", @@ -129,7 +129,7 @@ "gsd-core/references/autonomous-smart-discuss.md": "2fc710cde0ec7785", "gsd-core/references/checkpoints.md": "c2fe89c42ca88349", "gsd-core/references/common-bug-patterns.md": "a4cfea8954dede29", - "gsd-core/references/context-budget.md": "f1ce57bf418824af", + "gsd-core/references/context-budget.md": "6749084395889c6a", "gsd-core/references/continuation-format.md": "ce73e57d053ab8a7", "gsd-core/references/debugger-bug-taxonomy.md": "78fb8c1711acc971", "gsd-core/references/debugger-fix-acceptance.md": "5616622f33e00436", diff --git a/tests/phase-estimation.test.cjs b/tests/phase-estimation.test.cjs index dfd632871..2c43742ad 100644 --- a/tests/phase-estimation.test.cjs +++ b/tests/phase-estimation.test.cjs @@ -679,3 +679,57 @@ describe('query estimate-calibration', () => { assert.equal(calibration.factor, 1); }); }); + +// ─── double-calibration guard (#2631) ────────────────────────────────────── + +describe('estimate-check --calibrated', () => { + // A plan's recorded `estimate.tokens` already has the factor applied at + // emission time (ADR-2629 Decision 1). Without --calibrated, estimate-check + // applies it a SECOND time and compares factor^2 against the budget. With the + // [0.5, 3.0] clamp that ranges from 4x under to 9x over — and it is invisible + // until a project reaches 3 samples, because below that factor === 1 and + // 1^2 === 1. These tests pin both modes at a factor where they diverge. + const withHistory = (t) => { + const tmpDir = createTempProject(); + t.after(() => cleanup(tmpDir)); + fs.writeFileSync( + path.join(tmpDir, '.planning', 'estimation-calibration.json'), + est.renderCalibrationDocument([sample(100, 200), sample(100, 200), sample(100, 200)]), + ); + return tmpDir; + }; + + test('without the flag, a raw projection IS corrected', (t) => { + const tmpDir = withHistory(t); + const out = JSON.parse(runGsdTools('query estimate-check --tokens 50000', tmpDir).output); + assert.equal(out.calibration_factor, 2, 'fixture must produce factor 2'); + assert.equal(out.calibrated_tokens, 100000, 'a raw projection must be multiplied by the factor'); + assert.equal(out.pre_calibrated, false); + }); + + test('with the flag, an already-calibrated figure is NOT corrected again', (t) => { + const tmpDir = withHistory(t); + const out = JSON.parse(runGsdTools('query estimate-check --tokens 50000 --calibrated', tmpDir).output); + assert.equal(out.calibration_factor, 2, 'the factor is still reported'); + assert.equal(out.calibrated_tokens, 50000, + 'a pre-calibrated figure must pass through untouched — re-applying squares the correction'); + assert.equal(out.pre_calibrated, true); + }); + + test('the two modes diverge by exactly the factor', (t) => { + const tmpDir = withHistory(t); + const raw = JSON.parse(runGsdTools('query estimate-check --tokens 40000', tmpDir).output); + const pre = JSON.parse(runGsdTools('query estimate-check --tokens 40000 --calibrated', tmpDir).output); + assert.equal(raw.calibrated_tokens, pre.calibrated_tokens * raw.calibration_factor); + }); + + test('the flag changes the over-budget verdict at the boundary', (t) => { + const tmpDir = withHistory(t); + runGsdTools('config-set workflow.smart_zone_tokens 60000', tmpDir); + // 50000 raw -> 100000 calibrated -> over 60000. Same value pre-calibrated -> under. + const raw = JSON.parse(runGsdTools('query estimate-check --tokens 50000', tmpDir).output); + const pre = JSON.parse(runGsdTools('query estimate-check --tokens 50000 --calibrated', tmpDir).output); + assert.equal(raw.over_budget, true, 'double-applied correction reports a false over-budget'); + assert.equal(pre.over_budget, false, 'the honest figure is under budget'); + }); +}); diff --git a/tests/planner-estimate-emission.test.cjs b/tests/planner-estimate-emission.test.cjs new file mode 100644 index 000000000..efc937e71 --- /dev/null +++ b/tests/planner-estimate-emission.test.cjs @@ -0,0 +1,162 @@ +// allow-test-rule: source-text-is-the-product see #2631 +// agents/gsd-planner.md, agents/gsd-plan-checker.md and docs/reference/plan-md.md — their text IS what the runtime loads and what +// the planner emits against. Per CONTRIBUTING.md exception matrix. + +/** + * Planner estimate emission + over-budget surfacing. + * + * Epic #1952 Phase 2 (#2631). Design lock: docs/adr/2629-phase-effort-estimation-calibration.md. + * + * Phase 1 (#2630) landed the estimation module and its CLI verbs deliberately + * unconsumed. This phase wires them: the planner emits `estimate` into PLAN.md + * frontmatter, and plan-phase surfaces the over-budget warning. These tests pin + * the wiring so the module cannot silently go back to being dead code. + * + * The parity test at the bottom is the load-bearing one: the confidence + * vocabulary appears in BOTH agent prose and the module's frozen enum, which is + * exactly the "generative fix divergence" shape CLAUDE.md requires a parity + * assertion for. + */ + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const ROOT = path.join(__dirname, '..'); +const read = (p) => fs.readFileSync(path.join(ROOT, p), 'utf8'); + +const PLANNER = 'agents/gsd-planner.md'; +const PLAN_CHECKER = 'agents/gsd-plan-checker.md'; +const PLAN_MD_REF = 'docs/reference/plan-md.md'; + +/** + * Extract the PLAN.md frontmatter template the planner tells agents to emit. + * + * The template is a bare `---`-delimited YAML block, NOT a fenced ```yaml + * region — an earlier version of this helper looked for a fence and silently + * matched nothing, which made every assertion below fail for the wrong reason. + */ +function plannerFrontmatterTemplate(src) { + for (const m of src.matchAll(/^---\r?\n([\s\S]*?)^---\r?$/gm)) { + const body = m[1]; + if (/^phase:/m.test(body) && /^must_haves:/m.test(body)) return body; + } + return null; +} + +describe('planner emits an estimate block (AC1)', () => { + const src = read(PLANNER); + + test('the PLAN.md frontmatter template carries an estimate block', () => { + const tmpl = plannerFrontmatterTemplate(src); + assert.ok(tmpl, 'could not locate the PLAN.md frontmatter template in the planner'); + assert.match(tmpl, /^estimate:/m, 'template must declare an `estimate:` block'); + for (const field of ['tokens', 'tasks', 'confidence']) { + assert.match(tmpl, new RegExp(`^\\s+${field}:`, 'm'), `estimate block must carry \`${field}\``); + } + }); + + test('the frontmatter field table documents estimate', () => { + assert.match(src, /\|\s*`estimate`\s*\|/, 'field reference table must have an `estimate` row'); + }); + + test('the planner is told to apply the calibration factor, not invent confidence', () => { + assert.match(src, /estimate-calibration|calibration factor/i, + 'planner must consume the calibration surface Phase 1 exposed'); + assert.match(src, /derived|sample count/i, + 'planner must be told confidence is derived from sample count, not self-rated'); + }); +}); + +describe('the over-budget flag is surfaced (AC2)', () => { + // Surfaced by gsd-plan-checker, not plan-phase.md: that workflow sits ~74 + // bytes under the phase-6 capstone ratchet (94519) and cannot take new + // content without an unrelated extraction. Dimension 5 already owns scope + // sanity, so the estimate check belongs there. + const src = read(PLAN_CHECKER); + + test('the checker resolves the configured smart-zone budget', () => { + assert.match(src, /workflow\.smart_zone_tokens/, + 'must read the configured budget, not hardcode one'); + }); + + test('the checker invokes the estimate-check verb', () => { + assert.match(src, /estimate-check/, + 'the flag must be computed by the Phase 1 verb, not re-derived in prose'); + }); + + test('over budget recommends splitting and is never a blocker', () => { + assert.match(src, /re-slic|split/i, 'must recommend splitting'); + assert.match(src, /WARNING, never a blocker|never a blocker/i, + 'ADR-2629 Decision 5: the flag is advisory'); + }); +}); + +describe('plan-checker validates the estimate (Dimension 5)', () => { + const src = read(PLAN_CHECKER); + + test('Dimension 5 checks the emitted estimate against the budget', () => { + const idx = src.indexOf('Dimension 5'); + assert.ok(idx !== -1, 'Dimension 5 section must exist'); + const section = src.slice(idx, idx + 2500); + assert.match(section, /estimate/i, + 'Scope Sanity must consult the emitted estimate now that one exists'); + }); +}); + +describe('plan-md reference documents the field', () => { + const src = read(PLAN_MD_REF); + + test('the frontmatter field reference has an estimate row', () => { + assert.match(src, /\|\s*`estimate`\s*\|/, 'plan-md.md must document `estimate`'); + }); + + test('the row records that it is optional and additive', () => { + const row = src.split('\n').find((l) => /\|\s*`estimate`\s*\|/.test(l)); + assert.ok(row, 'estimate row not found'); + assert.match(row, /\bNo\b/, 'estimate must be documented as NOT required (additive/optional)'); + }); +}); + +describe('prose ↔ module parity (generative fix divergence guard)', () => { + // The confidence vocabulary now lives in two surfaces: the frozen enum in + // phase-estimation.cjs and the prose the planner emits against. If they + // diverge, the planner starts writing values the parser rejects — silently, + // because the estimate block is optional. Fail loudly instead. + const est = require('../gsd-core/bin/lib/phase-estimation.cjs'); + + test('every confidence value the planner may emit is accepted by the parser', () => { + const tmpl = plannerFrontmatterTemplate(read(PLANNER)); + assert.ok(tmpl, 'template not found'); + + const line = tmpl.split('\n').find((l) => /^\s+confidence:/.test(l)); + assert.ok(line, 'template must show the confidence field'); + + // Pull every bare word on the confidence line that looks like a vocabulary + // token (the comment enumerates the allowed values). + const words = line.match(/\b(low|med|high)\b/g) || []; + assert.ok(words.length > 0, 'confidence line must enumerate the allowed values'); + + for (const w of words) { + assert.ok( + est.CONFIDENCE_VALUES.includes(w), + `planner prose offers confidence "${w}" but the module's CONFIDENCE_VALUES does not accept it`, + ); + } + }); + + test('the documented budget default matches the shipped manifest default', () => { + const manifest = require('../gsd-core/bin/shared/config-defaults.manifest.json'); + const shipped = manifest.workflow.smart_zone_tokens; + assert.ok(Number.isSafeInteger(shipped) && shipped > 0, 'manifest must ship a usable default'); + + // docs/CONFIGURATION.md states the default in prose; a drifted doc silently + // misdescribes the gate to every reader. + const docs = read('docs/CONFIGURATION.md'); + const row = docs.split('\n').find((l) => l.includes('workflow.smart_zone_tokens')); + assert.ok(row, 'CONFIGURATION.md must document the key'); + assert.ok(row.includes(String(shipped)), + `CONFIGURATION.md documents a default that is not the shipped ${shipped}`); + }); +});