diff --git a/bin/gstack-review-plan b/bin/gstack-review-plan index 16e557e4f..af2f14ba0 100755 --- a/bin/gstack-review-plan +++ b/bin/gstack-review-plan @@ -5,6 +5,7 @@ * gstack-review-plan --base [--pass ] [--last none|full|fix-check] [--fixed-blocking] [--fixed-below] * [--blocking-open] [--touched-gate] [--critical-found] [--gate-tagged ] [--gated a,b] [--force a,b|all] * [--reviewed-numbers ] [--no-tiers] [--json] + * gstack-review-plan --fix-check-prompt --fix-list --hunks --tests [--callers ] * * Reads the diff against the merge base with (committed, working tree and * untracked), runs gstack-diff-scope and, unless --gate-tagged is given, @@ -17,6 +18,7 @@ import * as path from 'node:path'; import { EXIT, exitTableHelp } from '../lib/headless-artifacts'; import { runScriptSync } from '../lib/script-invocation'; import { planReview, reviewPlanLine, scanNumbers, type History } from '../lib/review-depth'; +import { renderFixCheckPrompt } from '../scripts/resolvers/fix-check'; const HELP = `gstack-review-plan — the review depth of the next /ship step (docs/review-policy.md#review-depth) @@ -34,6 +36,7 @@ const HELP = `gstack-review-plan — the review depth of the next /ship step (do --reviewed-numbers the numeric-claim fingerprint the outside voice already reviewed --no-tiers audits and watchers at the host's default effort too (as gstack-config review_effort_tiers off does) --json the plan as JSON + --fix-check-prompt print the fix-check reviewer's prompt from --fix-list, --hunks, --tests and --callers files ${exitTableHelp()}`; const args = process.argv.slice(2); @@ -44,6 +47,17 @@ const git = (a: string[]) => { const r = spawnSync('git', ['-c', 'core.quotePath function main(): number { if (has('--help') || has('-h')) { process.stdout.write(HELP + '\n'); return EXIT.ok; } + if (has('--fix-check-prompt')) { + const read = (n: string, required: boolean) => { + const f = flag(n); + if (!f) return required ? null : ''; + try { return fs.readFileSync(f, 'utf8'); } catch { return null; } + }; + const [fixList, touchedHunks, testResults, callers] = [read('--fix-list', true), read('--hunks', true), read('--tests', true), read('--callers', false)]; + if (fixList === null || touchedHunks === null || testResults === null || callers === null) { process.stderr.write('gstack-review-plan: --fix-check-prompt needs readable --fix-list, --hunks and --tests files (--callers optional)\n'); return EXIT.usage; } + process.stdout.write(renderFixCheckPrompt({ fixList, touchedHunks, testResults, callers })); + return EXIT.ok; + } const base = flag('--base'); if (!base) { process.stderr.write(`gstack-review-plan: --base is required\n${HELP}\n`); return EXIT.usage; } const last = (flag('--last') ?? 'none') as History['last']; diff --git a/docs/unattended.md b/docs/unattended.md index 2bc59fa6d..1a59d627d 100644 --- a/docs/unattended.md +++ b/docs/unattended.md @@ -165,7 +165,7 @@ unpushed head is `QUEUE_RECEIPT_STALE` to the queue. The receipt's `review_loop` object, written from `gstack-review-scope summary --since `, is the loop's machine-readable outcome: `passes` -(Step 9 passes this ship ran, 1 to 3), `blocking_found` (distinct blocking +(Step 9 full passes this ship ran, 1 or 2), `blocking_found` (distinct blocking findings across them), `deferred` (blocking findings in unchanged code filed as issues, Step 11's late defers included), `noted`, `fixed` and `scope`. The summary line a parent reads prints it as `passes= blocking_found= @@ -173,6 +173,18 @@ deferred=`; the PR body lists the deferred issues under `## Deferred findings` and the below-floor items under `## Review notes`. A pass that finds nothing is `NO FINDINGS` from every reviewer and a pass with no edits, which is the loop's exit, never a failed review ([review-policy.md](review-policy.md)). +Three counters appear only when the run recorded them: `fix_checks` (fix-check +rows, never passes), `dispatches` (reviewer dispatches by role) and +`est_input_tokens` (prompt plus diff bytes the dispatches read, / 4, a relative +estimate and never a bill). + +### Report length + +A parent reads many lanes, so length is a cost it pays per lane. A READY block +holds its required lines plus at most 150 words; a completion report holds at +most 300 words; a subagent brief names the files to read instead of pasting +them. Evidence goes in files the report points to. Waits go on operations, +never on polling wakes. ### The outside voice on Capy diff --git a/ship/SKILL.md b/ship/SKILL.md index 04062758f..0d8b8c0b5 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -205,7 +205,7 @@ not a commit ID. Use `git diff ` to compare these snapshots **Review policy** (docs/review-policy.md): a finding blocks another review pass only when it would fail a test, break production, or lose data or secrets; everything below that floor ships as a named note in the PR body. Pass 1 reviews -the diff; later passes read only the range since the previous pass; three passes +the diff; a later pass reads only the range; two full passes plus one fix-check is the cap. `NO FINDINGS` is a complete reviewer result. The ship receipt's `review_loop` counters record the outcome; `ship: review_scope` (Step 3) is the knob. diff --git a/ship/SKILL.md.tmpl b/ship/SKILL.md.tmpl index f943e38ad..c44aea895 100644 --- a/ship/SKILL.md.tmpl +++ b/ship/SKILL.md.tmpl @@ -71,7 +71,7 @@ not a commit ID. Use `git diff ` to compare these snapshots **Review policy** (docs/review-policy.md): a finding blocks another review pass only when it would fail a test, break production, or lose data or secrets; everything below that floor ships as a named note in the PR body. Pass 1 reviews -the diff; later passes read only the range since the previous pass; three passes +the diff; a later pass reads only the range; two full passes plus one fix-check is the cap. `NO FINDINGS` is a complete reviewer result. The ship receipt's `review_loop` counters record the outcome; `ship: review_scope` (Step 3) is the knob. diff --git a/ship/sections/adversarial.md b/ship/sections/adversarial.md index 45e860108..3d221d322 100644 --- a/ship/sections/adversarial.md +++ b/ship/sections/adversarial.md @@ -1,5 +1,9 @@ +**Step 9's plan governs this step:** run the native pass only on +`DISPATCH: adversarial-native` and the outside voice only on +`DISPATCH: outside-voice`; otherwise print `ADVERSARIAL: skipped (review plan)`. + ## Step 11: Adversarial review (always-on) Every diff gets the Claude adversarial pass. Add Codex when its preflight is ready; unavailable or disabled outside coverage stays explicit. diff --git a/ship/sections/adversarial.md.tmpl b/ship/sections/adversarial.md.tmpl index fe27bb106..f3a1c0be4 100644 --- a/ship/sections/adversarial.md.tmpl +++ b/ship/sections/adversarial.md.tmpl @@ -1,3 +1,7 @@ +**Step 9's plan governs this step:** run the native pass only on +`DISPATCH: adversarial-native` and the outside voice only on +`DISPATCH: outside-voice`; otherwise print `ADVERSARIAL: skipped (review plan)`. + {{ADVERSARIAL_STEP}} {{LEARNINGS_LOG}} diff --git a/ship/sections/review-army.md b/ship/sections/review-army.md index b9c3f17ef..6d31b1afc 100644 --- a/ship/sections/review-army.md +++ b/ship/sections/review-army.md @@ -13,6 +13,14 @@ Steps 10–11 queue findings without editing; include those findings in this pas Every repeat starts before the checklist read and captures a fresh REVIEW_START. Finish the complete review and QA before applying any fix in Step 9.4. +**Review plan.** Pass 1 runs `~/.claude/skills/gstack/bin/gstack-review-plan +--base `; a repeat uses decision 2's run. Save its `REVIEW_PLAN:` line and +fingerprint. Dispatch exactly its `DISPATCH:` lines (specialists, Red Team, Step +11's voices), each at its `effort=` on the same model family; a `SKIP:` reviewer +logs `{"dispatched":false,"reason":"scope"}`. Audits run at `AUDIT_EFFORT:`. The +core checklist, gate scan and QA always run. Item 6's row adds +`"dispatches":[{"role":R,"prompt_bytes":N,"diff_bytes":N}]`. + **Pass 2+ reads the range, not the tree** (`review_scope frozen`): the core read in item 2 becomes `git diff $RANGE_BASE`, and every dispatched prompt (specialists, Red Team, the Step 11 reviewers) carries a literal sha you write in: @@ -797,18 +805,27 @@ Apply these decisions in order: resume at the next decision; otherwise run a fresh complete Step 9. A successful peer or a QA exception cannot replace missing dispatched coverage. 2. **Third fixing cycle reached (`CYCLES >= 3`):** STOP and report recurring findings with - `converged:false`; do not run a fourth fixing cycle. Under `frozen` the cap comes - sooner: a blocking finding still in `fix` scope on pass 3 (`PASS >= 3`) stops - here too, unfixed. Name each blocking finding (path, severity, problem) for the - owner. Deferred and noted items are never recurring findings, so a capped pass + `converged:false`; do not run a fourth fixing cycle. Under `frozen` the plan + decides sooner: `gstack-review-plan --base --pass $PASS --last full` with + each flag that holds (`--fixed-blocking`, `--fixed-below`, `--blocking-open`, + `--touched-gate` for a fix to an item 2.5 path, `--reviewed-numbers `). + `NEXT: stop` stops here, unfixed; `full` is decision 3, `fix-check` 3.5, `done` 4. + Name each blocking finding (path, severity, problem) for the owner. Deferred and noted items are never recurring findings, so a capped pass whose remaining findings are all below the floor applies no edits and continues at decision 4: the loop ships below the floor. The cap is a stop, not an approval: a blocker it leaves standing is reported, never waved through. 3. **Fixes applied below the cap:** Insert Step 5, affected Steps 6–8 and all of - Step 9 before the pending Step 10 in the work list. If the fixes grew the diff + Step 9 before the pending Step 10 in the work list (`NEXT: full`). If the fixes grew the diff past what the request needs (a helper, a guard, a case nothing takes), cut that growth before anything else; the repeat reviews the cut as part of the range. Tests must pass or retain approval for the same verified pre-existing failures and scope. Keep CYCLES and scoped approvals across this repeat; PASS increments on entry. +3.5. **One fix-check.** Take a `gstack-review-log --start review` token; write the + fix list, touched hunks (`git diff $PASS_HEAD`), callers of changed symbols and + test results to `.gstack/tmp/fix-check-*.txt`; dispatch one full-effort reviewer + on `gstack-review-plan --fix-check-prompt --fix-list --hunks --tests + --callers `, plus the outside voice only if the plan lists it. Log item 6's + row as `"skill":"review-fix-check"` with `--finish `; not a pass. A + CRITICAL stops as decision 2; else decision 4. 4. **No edits in this pass:** Resolve the required-probe gate below. Only after it clears may you continue to Step 10. A pass whose only findings were deferred or noted applied no edits and continues here: the loop converges below the floor. diff --git a/ship/sections/review-army.md.tmpl b/ship/sections/review-army.md.tmpl index 1daa1035c..b94850cce 100644 --- a/ship/sections/review-army.md.tmpl +++ b/ship/sections/review-army.md.tmpl @@ -11,6 +11,14 @@ Steps 10–11 queue findings without editing; include those findings in this pas Every repeat starts before the checklist read and captures a fresh REVIEW_START. Finish the complete review and QA before applying any fix in Step 9.4. +**Review plan.** Pass 1 runs `~/.claude/skills/gstack/bin/gstack-review-plan +--base `; a repeat uses decision 2's run. Save its `REVIEW_PLAN:` line and +fingerprint. Dispatch exactly its `DISPATCH:` lines (specialists, Red Team, Step +11's voices), each at its `effort=` on the same model family; a `SKIP:` reviewer +logs `{"dispatched":false,"reason":"scope"}`. Audits run at `AUDIT_EFFORT:`. The +core checklist, gate scan and QA always run. Item 6's row adds +`"dispatches":[{"role":R,"prompt_bytes":N,"diff_bytes":N}]`. + **Pass 2+ reads the range, not the tree** (`review_scope frozen`): the core read in item 2 becomes `git diff $RANGE_BASE`, and every dispatched prompt (specialists, Red Team, the Step 11 reviewers) carries a literal sha you write in: @@ -211,18 +219,27 @@ Apply these decisions in order: resume at the next decision; otherwise run a fresh complete Step 9. A successful peer or a QA exception cannot replace missing dispatched coverage. 2. **Third fixing cycle reached (`CYCLES >= 3`):** STOP and report recurring findings with - `converged:false`; do not run a fourth fixing cycle. Under `frozen` the cap comes - sooner: a blocking finding still in `fix` scope on pass 3 (`PASS >= 3`) stops - here too, unfixed. Name each blocking finding (path, severity, problem) for the - owner. Deferred and noted items are never recurring findings, so a capped pass + `converged:false`; do not run a fourth fixing cycle. Under `frozen` the plan + decides sooner: `gstack-review-plan --base --pass $PASS --last full` with + each flag that holds (`--fixed-blocking`, `--fixed-below`, `--blocking-open`, + `--touched-gate` for a fix to an item 2.5 path, `--reviewed-numbers `). + `NEXT: stop` stops here, unfixed; `full` is decision 3, `fix-check` 3.5, `done` 4. + Name each blocking finding (path, severity, problem) for the owner. Deferred and noted items are never recurring findings, so a capped pass whose remaining findings are all below the floor applies no edits and continues at decision 4: the loop ships below the floor. The cap is a stop, not an approval: a blocker it leaves standing is reported, never waved through. 3. **Fixes applied below the cap:** Insert Step 5, affected Steps 6–8 and all of - Step 9 before the pending Step 10 in the work list. If the fixes grew the diff + Step 9 before the pending Step 10 in the work list (`NEXT: full`). If the fixes grew the diff past what the request needs (a helper, a guard, a case nothing takes), cut that growth before anything else; the repeat reviews the cut as part of the range. Tests must pass or retain approval for the same verified pre-existing failures and scope. Keep CYCLES and scoped approvals across this repeat; PASS increments on entry. +3.5. **One fix-check.** Take a `gstack-review-log --start review` token; write the + fix list, touched hunks (`git diff $PASS_HEAD`), callers of changed symbols and + test results to `.gstack/tmp/fix-check-*.txt`; dispatch one full-effort reviewer + on `gstack-review-plan --fix-check-prompt --fix-list --hunks --tests + --callers `, plus the outside voice only if the plan lists it. Log item 6's + row as `"skill":"review-fix-check"` with `--finish `; not a pass. A + CRITICAL stops as decision 2; else decision 4. 4. **No edits in this pass:** Resolve the required-probe gate below. Only after it clears may you continue to Step 10. A pass whose only findings were deferred or noted applied no edits and continues here: the loop converges below the floor. diff --git a/test/fixtures/golden/agy-ship-SKILL.md b/test/fixtures/golden/agy-ship-SKILL.md index b1367a6c8..8afc8d3ee 100644 --- a/test/fixtures/golden/agy-ship-SKILL.md +++ b/test/fixtures/golden/agy-ship-SKILL.md @@ -205,7 +205,7 @@ not a commit ID. Use `git diff ` to compare these snapshots **Review policy** (docs/review-policy.md): a finding blocks another review pass only when it would fail a test, break production, or lose data or secrets; everything below that floor ships as a named note in the PR body. Pass 1 reviews -the diff; later passes read only the range since the previous pass; three passes +the diff; a later pass reads only the range; two full passes plus one fix-check is the cap. `NO FINDINGS` is a complete reviewer result. The ship receipt's `review_loop` counters record the outcome; `ship: review_scope` (Step 3) is the knob. diff --git a/test/fixtures/golden/claude-ship-SKILL.md b/test/fixtures/golden/claude-ship-SKILL.md index 04062758f..0d8b8c0b5 100644 --- a/test/fixtures/golden/claude-ship-SKILL.md +++ b/test/fixtures/golden/claude-ship-SKILL.md @@ -205,7 +205,7 @@ not a commit ID. Use `git diff ` to compare these snapshots **Review policy** (docs/review-policy.md): a finding blocks another review pass only when it would fail a test, break production, or lose data or secrets; everything below that floor ships as a named note in the PR body. Pass 1 reviews -the diff; later passes read only the range since the previous pass; three passes +the diff; a later pass reads only the range; two full passes plus one fix-check is the cap. `NO FINDINGS` is a complete reviewer result. The ship receipt's `review_loop` counters record the outcome; `ship: review_scope` (Step 3) is the knob. diff --git a/test/fixtures/golden/codex-ship-SKILL.md b/test/fixtures/golden/codex-ship-SKILL.md index e45732e74..f6040b513 100644 --- a/test/fixtures/golden/codex-ship-SKILL.md +++ b/test/fixtures/golden/codex-ship-SKILL.md @@ -203,7 +203,7 @@ not a commit ID. Use `git diff ` to compare these snapshots **Review policy** (docs/review-policy.md): a finding blocks another review pass only when it would fail a test, break production, or lose data or secrets; everything below that floor ships as a named note in the PR body. Pass 1 reviews -the diff; later passes read only the range since the previous pass; three passes +the diff; a later pass reads only the range; two full passes plus one fix-check is the cap. `NO FINDINGS` is a complete reviewer result. The ship receipt's `review_loop` counters record the outcome; `ship: review_scope` (Step 3) is the knob. diff --git a/test/fixtures/golden/factory-ship-SKILL.md b/test/fixtures/golden/factory-ship-SKILL.md index 0c756e903..4adc441a2 100644 --- a/test/fixtures/golden/factory-ship-SKILL.md +++ b/test/fixtures/golden/factory-ship-SKILL.md @@ -205,7 +205,7 @@ not a commit ID. Use `git diff ` to compare these snapshots **Review policy** (docs/review-policy.md): a finding blocks another review pass only when it would fail a test, break production, or lose data or secrets; everything below that floor ships as a named note in the PR body. Pass 1 reviews -the diff; later passes read only the range since the previous pass; three passes +the diff; a later pass reads only the range; two full passes plus one fix-check is the cap. `NO FINDINGS` is a complete reviewer result. The ship receipt's `review_loop` counters record the outcome; `ship: review_scope` (Step 3) is the knob. diff --git a/test/helpers/carve-guards.ts b/test/helpers/carve-guards.ts index b5d1f1d3f..88d3320a9 100644 --- a/test/helpers/carve-guards.ts +++ b/test/helpers/carve-guards.ts @@ -168,7 +168,7 @@ export const CARVE_GUARDS: Record = { // wave's headline capability) grows the union to 1.195x. Deliberate: // the section is on-demand (loads only for Apple store targets), so // per-invocation cost for non-iOS ships is one manifest line. - maxSizeRatio: 1.7529, // GSTA-128 flaky pass (#3516, 2026-10-27, GStack Minder's rule 19 grant): the review-army section's small-diff clause names State Machines as item 9's call and a State Machines-only specialist list, +5 union bytes on a main (703b0049, v1.91.280.0) that sat 4 bytes under the 1.7524 cap after #3515 and #3517; measured 328,937 / 187,706 = 1.7524 (before: 328,932 = 1.7524) on the merged tree, pinned one in the last place above. Before that: GSTA-96 hoist merged with v1.91.273.0 (#3501, 2026-10-27): main's 1.7500 plus the hoist's own raise, no trim; measured 328,918 / 187,706 = 1.7523 on the merged tree. Before: Shared advisory identity/dedup + critical-severity validation: 248,065 union bytes / 187,706 baseline = 1.3216 (2026-09-17). + test value bar in the lazy Step 7 section (value cards, weak paths, gate table, base control, machine checks; ~13.6KB): measured 1.396 (2026-09-29). + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.401 (2026-09-30). + the shared QA review step's plan-check timing rule (plan checks and their revalidation run on --timeout-ms after smoke expiry); measured 1.4022 (2026-09-30). + the shared TEST_BOOTSTRAP owned-change undo rules (s03 H1-H3, M1) and the value-bar coverage wording (s03 M2/M3); measured 1.4044 (2026-10-02). + severe fix wave ship-review lane: G1 NO_VERSION dispatch, title and CHANGELOG arms (versionless repos ship without inventing a version), F1 parent-side plan binding before the audit dispatch (binding, docs/designs candidates, exact not-run line) and D3's data-not-instructions line; +2,126 union bytes, measured 1.4135 (2026-10-03). + INV-1 consumer lines (outside review `unverified`/`unavailable` is missing coverage in the readiness note and PR body); measured 1.4148 (2026-10-03). + v1.91.19.0 severe fix wave: verdict-form outside-review callers with sandbox preflight and first-use notice (INV-1/B1/Q2), P0/P1 gate and unverified wording, bound-plan discovery (F1), visible LEARNINGS failures (B5), carved external pointers; measured 267,752 / 187,706 = 1.4264 (2026-10-03). + v1.91.18.0 (#3023) merged into the v1.91.19.0 wave; measured 267,874 / 187,706 = 1.4271 (2026-10-04). + Oct 6 fix wave: quota_exhausted preflight branch (#3051) and quoted-heredoc issue template (#3046); measured 268,199 / 187,706 = 1.4288 (2026-10-06). + Step 10's inline no-PR check before the Greptile dispatch (#3020); measured 268,655 / 187,706 = 1.4313 (2026-10-06). + Oct 6 fix wave A3/CEO-12: free text reaches commands only through agent-written mktemp files (mktemp block, write rule, path rebuild and refusal at each posting, prompt and brief site); measured 275,422 / 187,706 = 1.4673 (2026-10-06). Combined Oct 6 wave head (B free-text files + D measure loop + A rework): measured 280,363 / 187,706 = 1.4936 (2026-10-06). + gate-once push rule (no second paid gate after a CI push); measured 280,573 / 187,706 = 1.4947 (2026-10-06). + the measurement bar's decision step (exit-code branches for MEETS, MEETS-qualified, needs-classify with the qualifying and fix-round classes, EXTEND once; the measure prose was tightened to offset most of it); measured 281,058 / 187,706 = 1.4973 (2026-10-06). + Oct 7 wave B3: the Codex probe runs as a command (select-model capture lines, check-sandbox/probe-model/run-with-timeout subcommands) instead of being sourced; measured 281,438 / 187,706 = 1.4994 (2026-10-07). + Oct 7 wave CEO-28/DX-11: review prompts ask for a severity per finding and design proposals use the proposal gate; measured 281,556 / 187,706 = 1.5000 (2026-10-07). + Oct 7 wave B4: /ship publishes through gstack-post (title prepared in a file in the pr-body section, compose block plus gstack-post exit-code branches replacing the two scans); measured 281,894 / 187,706 = 1.5018 (2026-10-07). + B2 early Greptile PR (lazy Step 6.5 section: detect, public-repo consent, push, provisional title file, open; Step 10's bounded wait; the stop-leaves-the-draft rule); measured 286,626 / 187,706 = 1.5270 (2026-10-07). B3, the classifier, B4 and B2 merged: measured 287,128 / 187,706 = 1.5297 (2026-10-07). + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site; measured 281,370 / 187,706 = 1.4990 (2026-10-07). Merged with the guard area: measured 287,440 / 187,706 = 1.5313 (2026-10-07). + Oct 8 wave: the defensive-security framing and fixture summary mode reach the security specialist and the Red Team (stated once in the dispatch, referenced by the Red Team); measured 288,783 / 187,706 = 1.5385 (2026-10-08). + gate integrity (HONEST_WORK_GATE_INTEGRITY W3): the shared GATE_SCAN_BLOCK in the core checklist (2.5) and after Step 7's child, the four-option gate question and record fields in 9.4, tests_ran states in Step 5, Gate edits and per-lane counts in the PR body with draft-while-open; measured 300,143 / 187,706 = 1.5990 (2026-10-10). + Honest Work wave merged (W2/W3 gate integrity in review/ship, W4 kernel + DONE line in every tier-2+ skill, W5/W7 in spec/ship): re-measured on the integrated tree (2026-10-10). + Oct 10 multi-agent wave PR E2 (ship/sections/handoff.md: the no-PR automation handoff, with its Section-index row and STOP pointer in the skeleton), offset by tightening Steps 1–3, 12, 14–17 and 20–21 prose so the skeleton stays under 81,000 bytes (80,997); C-pregate landed 1,123 union bytes in tests.md in between; measured 308,619 / 187,706 = 1.6442 on the integrated tree (2026-10-10). + next-wave Lane D (GBRA-40 item 12): the pr-body Issue links step (promotion candidates and `fix #N`, owner-confirmed Supersedes/credit, `--author` for app-identity gh, lookup failures, ISSUE_LINKS_MISSING) and the handoff's issue-links run, with the `## Issues` placeholder shortened; Step 15's `Fixes:` trailer rule offset by tightening Step 15 so the skeleton stays at 80,998; measured 309,483 / 187,706 = 1.6488 (2026-10-14). + GSTA-61 Capy merge asks: the on-demand capy-pr section (What changed with before/after images, Why, Approvals; loads only when Step 19 prints CAPY_PR: on, so a non-Capy ship pays one manifest line and the three-line CAPY_PR check); measured 313,265 / 187,706 = 1.6689 (2026-10-20). + rule 22 / GBRA-108 (2026-10-25): Step 22 terminal line and Step 18 unattended routing in the skeleton, the pr-body section's receipt-check sentence and the handoff's Step 22 pointer; measured 314,027 / 187,706 = 1.6730. + harness lane E (2026-10-26, issue row 1: five Captains hit the review-loop cap): the review-army section's frozen finding scope (PASS/RANGE_BASE, gstack-review-scope classify/defer/render in Step 9.4, deferred and noted actions, the cap rule naming blocking findings), the pr-body section's Deferred findings / Review notes sections and --review-loop receipt input, Step 1's carry check; measured 318,010 / 187,706 = 1.6942. + the same lane's pass-3 cap rule in Step 9.4 decision 2 (a blocking finding still in fix scope on pass 3 stops for the owner; below the floor ships) and the adversarial step's RANGE_BASE scoping of queued fixes; measured 318,476 / 187,706 = 1.6967. + the skeleton's Review policy paragraph and the diff-plus-blocking scope value in Step 9.4; measured 319,071 / 187,706 = 1.6998. + the same lane's pass-1 review fixes: the lead-judgment stage in the finding merge (nit gravity, hypothetical vs actual, premature structure, a different design, missing context; absorbed from pstack's interrogate), the `cause` anchor in every specialist prompt, the blocker/should/nit glossary in Step 9.4 and the two stop rules in the repeat decisions (absorbed from tstack's sol-loop), pass 2's range in the core read; measured 321,630 / 187,706 = 1.7135 (2026-10-26). + the same lane's PR 2 (2026-10-26): the review-army section's cap check before any edit, named .gstack/tmp/ scratch files, the Step 11 late-defer row and the --review-fanout batches in the specialist dispatch; the documentation section's docs-caps run, docs-audit.json record and owner-exception shape; the pr-body section's --docs receipt input and exception line; the changelog section's no-CHANGELOG line; measured 326,437 / 187,706 = 1.7391 (2026-10-26). + absorb lane B (2026-10-26, Garry: "Do it, send" on plans/absorb-tstack.md item 4 and absorb-pstack.md items 2(a) and 11): the pr-body section's lead-with-the-problem Summary instruction with its BAD/GOOD pair, the `## Blast radius` item in pr-body and capy-pr, capy-pr's `## Why` pair, and one gstack-prose-lint command line each in the changelog and pr-body sections; skeleton unchanged by this lane; measured 328,319 / 187,706 = 1.7491 on the tree with #3487 merged (2026-10-27). + absorb-tstack (2026-10-27): the handoff section's one-line pointer to docs/unattended.md's unattended grant; measured 329,024 / 187,706 = 1.7529 on the tree merged with 392f50d4. + maxSizeRatio: 1.7610, // GSTA-131 token efficiency (2026-10-27, Garry's D1; GStack Minder's re-pin with the rule 19 grant): the review-army section's review plan (gstack-review-plan dispatch lines and effort, the plan-driven repeat decision, the 3.5 fix-check) and the adversarial section's plan line, +1,515 union bytes; measured 330,539 / 187,706 = 1.76094 on the tree merged with 10b77de4 (before: 329,024 = 1.75287). Before that: GSTA-128 flaky pass (#3516, 2026-10-27, GStack Minder's rule 19 grant): the review-army section's small-diff clause names State Machines as item 9's call and a State Machines-only specialist list, +5 union bytes on a main (703b0049, v1.91.280.0) that sat 4 bytes under the 1.7524 cap after #3515 and #3517; measured 328,937 / 187,706 = 1.7524 (before: 328,932 = 1.7524) on the merged tree, pinned one in the last place above. Before that: GSTA-96 hoist merged with v1.91.273.0 (#3501, 2026-10-27): main's 1.7500 plus the hoist's own raise, no trim; measured 328,918 / 187,706 = 1.7523 on the merged tree. Before: Shared advisory identity/dedup + critical-severity validation: 248,065 union bytes / 187,706 baseline = 1.3216 (2026-09-17). + test value bar in the lazy Step 7 section (value cards, weak paths, gate table, base control, machine checks; ~13.6KB): measured 1.396 (2026-09-29). + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.401 (2026-09-30). + the shared QA review step's plan-check timing rule (plan checks and their revalidation run on --timeout-ms after smoke expiry); measured 1.4022 (2026-09-30). + the shared TEST_BOOTSTRAP owned-change undo rules (s03 H1-H3, M1) and the value-bar coverage wording (s03 M2/M3); measured 1.4044 (2026-10-02). + severe fix wave ship-review lane: G1 NO_VERSION dispatch, title and CHANGELOG arms (versionless repos ship without inventing a version), F1 parent-side plan binding before the audit dispatch (binding, docs/designs candidates, exact not-run line) and D3's data-not-instructions line; +2,126 union bytes, measured 1.4135 (2026-10-03). + INV-1 consumer lines (outside review `unverified`/`unavailable` is missing coverage in the readiness note and PR body); measured 1.4148 (2026-10-03). + v1.91.19.0 severe fix wave: verdict-form outside-review callers with sandbox preflight and first-use notice (INV-1/B1/Q2), P0/P1 gate and unverified wording, bound-plan discovery (F1), visible LEARNINGS failures (B5), carved external pointers; measured 267,752 / 187,706 = 1.4264 (2026-10-03). + v1.91.18.0 (#3023) merged into the v1.91.19.0 wave; measured 267,874 / 187,706 = 1.4271 (2026-10-04). + Oct 6 fix wave: quota_exhausted preflight branch (#3051) and quoted-heredoc issue template (#3046); measured 268,199 / 187,706 = 1.4288 (2026-10-06). + Step 10's inline no-PR check before the Greptile dispatch (#3020); measured 268,655 / 187,706 = 1.4313 (2026-10-06). + Oct 6 fix wave A3/CEO-12: free text reaches commands only through agent-written mktemp files (mktemp block, write rule, path rebuild and refusal at each posting, prompt and brief site); measured 275,422 / 187,706 = 1.4673 (2026-10-06). Combined Oct 6 wave head (B free-text files + D measure loop + A rework): measured 280,363 / 187,706 = 1.4936 (2026-10-06). + gate-once push rule (no second paid gate after a CI push); measured 280,573 / 187,706 = 1.4947 (2026-10-06). + the measurement bar's decision step (exit-code branches for MEETS, MEETS-qualified, needs-classify with the qualifying and fix-round classes, EXTEND once; the measure prose was tightened to offset most of it); measured 281,058 / 187,706 = 1.4973 (2026-10-06). + Oct 7 wave B3: the Codex probe runs as a command (select-model capture lines, check-sandbox/probe-model/run-with-timeout subcommands) instead of being sourced; measured 281,438 / 187,706 = 1.4994 (2026-10-07). + Oct 7 wave CEO-28/DX-11: review prompts ask for a severity per finding and design proposals use the proposal gate; measured 281,556 / 187,706 = 1.5000 (2026-10-07). + Oct 7 wave B4: /ship publishes through gstack-post (title prepared in a file in the pr-body section, compose block plus gstack-post exit-code branches replacing the two scans); measured 281,894 / 187,706 = 1.5018 (2026-10-07). + B2 early Greptile PR (lazy Step 6.5 section: detect, public-repo consent, push, provisional title file, open; Step 10's bounded wait; the stop-leaves-the-draft rule); measured 286,626 / 187,706 = 1.5270 (2026-10-07). B3, the classifier, B4 and B2 merged: measured 287,128 / 187,706 = 1.5297 (2026-10-07). + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site; measured 281,370 / 187,706 = 1.4990 (2026-10-07). Merged with the guard area: measured 287,440 / 187,706 = 1.5313 (2026-10-07). + Oct 8 wave: the defensive-security framing and fixture summary mode reach the security specialist and the Red Team (stated once in the dispatch, referenced by the Red Team); measured 288,783 / 187,706 = 1.5385 (2026-10-08). + gate integrity (HONEST_WORK_GATE_INTEGRITY W3): the shared GATE_SCAN_BLOCK in the core checklist (2.5) and after Step 7's child, the four-option gate question and record fields in 9.4, tests_ran states in Step 5, Gate edits and per-lane counts in the PR body with draft-while-open; measured 300,143 / 187,706 = 1.5990 (2026-10-10). + Honest Work wave merged (W2/W3 gate integrity in review/ship, W4 kernel + DONE line in every tier-2+ skill, W5/W7 in spec/ship): re-measured on the integrated tree (2026-10-10). + Oct 10 multi-agent wave PR E2 (ship/sections/handoff.md: the no-PR automation handoff, with its Section-index row and STOP pointer in the skeleton), offset by tightening Steps 1–3, 12, 14–17 and 20–21 prose so the skeleton stays under 81,000 bytes (80,997); C-pregate landed 1,123 union bytes in tests.md in between; measured 308,619 / 187,706 = 1.6442 on the integrated tree (2026-10-10). + next-wave Lane D (GBRA-40 item 12): the pr-body Issue links step (promotion candidates and `fix #N`, owner-confirmed Supersedes/credit, `--author` for app-identity gh, lookup failures, ISSUE_LINKS_MISSING) and the handoff's issue-links run, with the `## Issues` placeholder shortened; Step 15's `Fixes:` trailer rule offset by tightening Step 15 so the skeleton stays at 80,998; measured 309,483 / 187,706 = 1.6488 (2026-10-14). + GSTA-61 Capy merge asks: the on-demand capy-pr section (What changed with before/after images, Why, Approvals; loads only when Step 19 prints CAPY_PR: on, so a non-Capy ship pays one manifest line and the three-line CAPY_PR check); measured 313,265 / 187,706 = 1.6689 (2026-10-20). + rule 22 / GBRA-108 (2026-10-25): Step 22 terminal line and Step 18 unattended routing in the skeleton, the pr-body section's receipt-check sentence and the handoff's Step 22 pointer; measured 314,027 / 187,706 = 1.6730. + harness lane E (2026-10-26, issue row 1: five Captains hit the review-loop cap): the review-army section's frozen finding scope (PASS/RANGE_BASE, gstack-review-scope classify/defer/render in Step 9.4, deferred and noted actions, the cap rule naming blocking findings), the pr-body section's Deferred findings / Review notes sections and --review-loop receipt input, Step 1's carry check; measured 318,010 / 187,706 = 1.6942. + the same lane's pass-3 cap rule in Step 9.4 decision 2 (a blocking finding still in fix scope on pass 3 stops for the owner; below the floor ships) and the adversarial step's RANGE_BASE scoping of queued fixes; measured 318,476 / 187,706 = 1.6967. + the skeleton's Review policy paragraph and the diff-plus-blocking scope value in Step 9.4; measured 319,071 / 187,706 = 1.6998. + the same lane's pass-1 review fixes: the lead-judgment stage in the finding merge (nit gravity, hypothetical vs actual, premature structure, a different design, missing context; absorbed from pstack's interrogate), the `cause` anchor in every specialist prompt, the blocker/should/nit glossary in Step 9.4 and the two stop rules in the repeat decisions (absorbed from tstack's sol-loop), pass 2's range in the core read; measured 321,630 / 187,706 = 1.7135 (2026-10-26). + the same lane's PR 2 (2026-10-26): the review-army section's cap check before any edit, named .gstack/tmp/ scratch files, the Step 11 late-defer row and the --review-fanout batches in the specialist dispatch; the documentation section's docs-caps run, docs-audit.json record and owner-exception shape; the pr-body section's --docs receipt input and exception line; the changelog section's no-CHANGELOG line; measured 326,437 / 187,706 = 1.7391 (2026-10-26). + absorb lane B (2026-10-26, Garry: "Do it, send" on plans/absorb-tstack.md item 4 and absorb-pstack.md items 2(a) and 11): the pr-body section's lead-with-the-problem Summary instruction with its BAD/GOOD pair, the `## Blast radius` item in pr-body and capy-pr, capy-pr's `## Why` pair, and one gstack-prose-lint command line each in the changelog and pr-body sections; skeleton unchanged by this lane; measured 328,319 / 187,706 = 1.7491 on the tree with #3487 merged (2026-10-27). + absorb-tstack (2026-10-27): the handoff section's one-line pointer to docs/unattended.md's unattended grant; measured 329,024 / 187,706 = 1.7529 on the tree merged with 392f50d4. }, 'plan-ceo-review': { skill: 'plan-ceo-review', diff --git a/test/review-plan-cli.test.ts b/test/review-plan-cli.test.ts index 2544feee7..d0769249f 100644 --- a/test/review-plan-cli.test.ts +++ b/test/review-plan-cli.test.ts @@ -46,6 +46,18 @@ describe('gstack-review-plan', () => { expect(r.stdout).toContain('next=fix-check dispatches=1'); expect(r.stdout).toContain('DISPATCH: fix-check fix-check effort=full'); }); + test('--fix-check-prompt renders the fix-check reviewer prompt from the parent\'s files and refuses a missing one', () => { + const r = repo({ 'docs/howto.md': '# x\n' }); + const w = (n: string, t: string) => { const f = path.join(r.state, n); fs.writeFileSync(f, t); return f; }; + const files = ['--fix-list', w('fix.txt', 'lib/a.ts:3 guard now refuses unknown'), '--hunks', w('hunks.diff', '+if (s !== "MERGED") return false;'), '--tests', w('tests.txt', '12 pass 0 fail')]; + const ok = spawnSync(BIN, ['--fix-check-prompt', ...files], { cwd: r.dir, encoding: 'utf8', timeout: 60_000 }); + expect(ok.status).toBe(0); + expect(ok.stdout).toContain('You are the fix-check reviewer'); + expect(ok.stdout).toContain('lib/a.ts:3 guard now refuses unknown'); + expect(ok.stdout).toContain('(none found)'); + const missing = spawnSync(BIN, ['--fix-check-prompt', '--fix-list', path.join(r.state, 'nope.txt'), '--hunks', files[3]!, '--tests', files[5]!], { cwd: r.dir, encoding: 'utf8', timeout: 60_000 }); + expect(missing.status).toBe(2); + }); test('usage errors exit 2', () => { expect(spawnSync(BIN, [], { encoding: 'utf8', timeout: 10_000 }).status).toBe(2); expect(run(repo({ 'a.md': 'x' }), ['--last', 'later']).status).toBe(2);