From 0da1b8291267db4935d0ca756bbec2700be123db Mon Sep 17 00:00:00 2001 From: garrytan Date: Wed, 28 Oct 2026 00:17:02 +0000 Subject: [PATCH] fix(GSTA-131): ship review pass-2 repairs: Step 11 follows the last full-pass plan, Important Rules restored, ship goldens regenerated, true per-class medians, review-policy transitions and once-per-invocation voices, docs/** assets stay docs-class, CLI and fix-check binding tests Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- docs/review-policy.md | 11 +++++--- lib/autoplan-scope.ts | 2 +- lib/review-depth.ts | 4 +-- scripts/review-dispatch-compare.ts | 7 +++-- ship/SKILL.md | 30 +++++++++++++--------- ship/SKILL.md.tmpl | 30 +++++++++++++--------- ship/sections/adversarial.md | 2 +- ship/sections/adversarial.md.tmpl | 2 +- test/autoplan-token-efficiency.test.ts | 2 +- test/fixtures/golden/agy-ship-SKILL.md | 23 +++++++++-------- test/fixtures/golden/claude-ship-SKILL.md | 23 +++++++++-------- test/fixtures/golden/codex-ship-SKILL.md | 23 +++++++++-------- test/fixtures/golden/factory-ship-SKILL.md | 23 +++++++++-------- test/helpers/carve-guards.ts | 4 +-- test/review-log-findings.test.ts | 13 ++++++++++ test/review-plan-cli.test.ts | 16 ++++++++++++ test/ship-control-flow.test.ts | 6 ++--- 17 files changed, 134 insertions(+), 87 deletions(-) diff --git a/docs/review-policy.md b/docs/review-policy.md index 81095755a..11513a049 100644 --- a/docs/review-policy.md +++ b/docs/review-policy.md @@ -112,7 +112,7 @@ incomplete coverage, which blocks until the reviewer is rerun. | a full pass whose fix touched a gate-class path, below the cap | full pass on the range | | a full pass that fixed only below-floor findings | one fix-check | | a full pass at the cap that fixed a blocking finding | one fix-check | -| a full pass at the cap, or with no fix, leaving a blocking finding open | stop: name it for the owner | +| a full pass that leaves a blocking finding open (fixed or not) | stop: name it for the owner | | a fix-check that found nothing blocking | done | | a fix-check that found a blocking finding | stop: name it for the owner | @@ -134,7 +134,7 @@ to code, and code with the gate scan unavailable fails closed to gate. |---|---|---| | **docs** | markdown no agent or gate reads: README, CHANGELOG, TODOS, how-to and explanation docs | the parent's core checklist only | | **tests** | test files and fixtures only | core plus the testing specialist | -| **code** | everything else, including every `SKILL.md` and template, `*/sections/**`, `scripts/resolvers/**`, `docs/*-policy.md`, checklists, specialists and bins | specialists by scope (as before), the native adversarial pass, the outside voice; the Red Team past 200 lines or after a CRITICAL | +| **code** | everything else, including every `SKILL.md` and template, any markdown in a skill directory, `*/sections/**`, `scripts/resolvers/**`, `docs/*-policy.md`, `docs/captain.md`, `docs/unattended.md`, `docs/pregate.md`, checklists, specialists and bins | specialists by scope (as before), the native adversarial pass, the outside voice; the Red Team past 200 lines or after a CRITICAL | | **gate** | code plus auth or migration scope, a hunk the gate scan tags (an RH rule: a relaxed threshold, a skipped test), a deleted code file, CI workflows, credential or state-root paths | code's set, plus the Red Team always, at full effort, and no hit-rate gating | **Numbers keep the outside voice.** On a docs or tests diff the outside voice is @@ -145,8 +145,11 @@ claim always dispatches it. The claims' fingerprint (`fingerprint=`) is passed back as `--reviewed-numbers`; when a later fix changes a claim, only the outside voice reruns, on the numbers. -**The adversarial and outside voices run once**, on pass 1's diff (Step 11). -Later full passes rerun the specialists on the range only. +**The adversarial and outside voices run once per invocation** (Step 11, on the +final tree), as the last full-pass plan lists them; after they ran, decision 2 +passes `--adversarial-ran` so a repeat lists them no more. Later full passes rerun the +specialists on the range only. Step 11.5 binds a planned skip through the Step 9 +record's `CURRENT` freshness instead of a native record. **Effort.** Every reviewer that can block a ship (the core checklist, specialists, the Red Team, the native adversarial pass, fix-checks) runs at diff --git a/lib/autoplan-scope.ts b/lib/autoplan-scope.ts index 893ae975a..736bcf437 100644 --- a/lib/autoplan-scope.ts +++ b/lib/autoplan-scope.ts @@ -50,7 +50,7 @@ export function detectUiScope(implementationPlan: string): UiScope { /** Docs-class: what /ship's review classifies as docs (lib/review-depth.ts classifyPath: README, CHANGELOG, TODOS and prose markdown outside agent-read and skill-directory paths), plus a docs directory named without a file. */ export function docsClassPath(file: string, root: string): boolean { const p = file.replace(/^\.\//, ''); - if (/^docs\/[^.]*$/.test(p)) return true; + if (p.startsWith('docs/') && !/\.mdx?$/i.test(p)) return true; return classifyPath(p, root) === 'docs'; } diff --git a/lib/review-depth.ts b/lib/review-depth.ts index 991648ed7..f7331dbba 100644 --- a/lib/review-depth.ts +++ b/lib/review-depth.ts @@ -128,8 +128,8 @@ export interface NextStep { step: Step; reason: string } /** * The transition table (docs/review-policy.md#transitions). Pass 1 is full. A fixed * blocking finding earns a second full pass on the range; any other fix earns one - * fix-check; two full passes is the cap; a blocker still open after the last - * allowed step stops for the owner. + * fix-check; two full passes is the cap; a blocker still open after any step + * stops for the owner. */ export function nextStep(h: History): NextStep { if (h.last === 'none') return { step: 'full', reason: 'pass 1 reviews the whole diff' }; diff --git a/scripts/review-dispatch-compare.ts b/scripts/review-dispatch-compare.ts index a4d90faa3..966e4e1e6 100755 --- a/scripts/review-dispatch-compare.ts +++ b/scripts/review-dispatch-compare.ts @@ -162,13 +162,12 @@ function main(argv: string[]): number { for (const r of rows) lines.push(`| ${r.fixture} | ${r.class} | ${r.scenario} | ${r.legacy_dispatches} → ${r.new_dispatches} | ${r.legacy_tokens} → ${r.new_tokens} | ${r.ratio} | ${r.ending} |`); lines.push('', '| fixture | /autoplan scenario | voice dispatches legacy → new |', '|---|---|---|'); for (const a of ap) for (const r of a.runs) lines.push(`| ${a.fixture} | ${r.scenario} | ${r.legacy} → ${r.next} |`); + const median = (v: number[]) => { const m = Math.floor(v.length / 2); return v.length % 2 ? v[m]! : Math.round(((v[m - 1]! + v[m]!) / 2) * 100) / 100; }; const ratios = rows.map(r => Number(r.ratio)).sort((a, b) => a - b); - const mid = Math.floor(ratios.length / 2); - const median = ratios.length % 2 ? ratios[mid]! : Math.round(((ratios[mid - 1]! + ratios[mid]!) / 2) * 100) / 100; - lines.push('', `SHIP_TOKEN_RATIO: median=${median} min=${ratios[0]} max=${ratios[ratios.length - 1]} (n=${ratios.length})`); + lines.push('', `SHIP_TOKEN_RATIO: median=${median(ratios)} min=${ratios[0]} max=${ratios[ratios.length - 1]} (n=${ratios.length})`); const byClass = new Map(); for (const r of rows) byClass.set(String(r.class), [...(byClass.get(String(r.class)) ?? []), Number(r.ratio)].sort((x, y) => x - y)); - lines.push(`SHIP_TOKEN_RATIO_BY_CLASS: ${[...byClass].map(([c, v]) => `${c} median=${v[Math.floor(v.length / 2)]} (n=${v.length})`).join('; ')}`); + lines.push(`SHIP_TOKEN_RATIO_BY_CLASS: ${[...byClass].map(([c, v]) => `${c} median=${median(v)} (n=${v.length})`).join('; ')}`); process.stdout.write(lines.join('\n') + '\n'); return 0; } diff --git a/ship/SKILL.md b/ship/SKILL.md index 1cc42f384..3d1978825 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -206,8 +206,8 @@ not a commit ID. Use `git diff ` to compare these snapshots 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; 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. +is the cap. `NO FINDINGS` is a complete reviewer result. `ship: review_scope` (Step 3) +is the knob; the receipt's `review_loop` records the outcome. ### Ship control flow @@ -329,11 +329,10 @@ repository-landing asks, including on Apple repos. 4. **Carry check.** If `gh pr list --head --state open --json number` names a PR, run `~/.claude/skills/gstack/bin/gstack-ship-receipt carry --pr --base --dry-run`. - `CARRY: would carry` (product patch-id unchanged; only a base merge, tests or release - metadata moved): skip review, fix only what the merge broke (CHANGELOG, size caps), run - `gstack-pregate --allow-repo-commands`, then carry without `--dry-run`, adding `--pregate - .gstack/tmp/pregate.json`. `CARRY: carried` binds HEAD: stop at its `GSTACK_RESULT` line. - A failed check, a moved HEAD, `refused` or `current` re-runs Steps 2–16. + `CARRY: would carry` (same product patch-id): skip review, fix what the merge broke + (CHANGELOG, size caps), run `gstack-pregate --allow-repo-commands`, then carry without + `--dry-run` plus `--pregate .gstack/tmp/pregate.json`. `CARRY: carried` binds HEAD: stop. + A failed check, HEAD moving after pregate, `refused` or `current` re-runs Steps 2–16. 5. Display historical readiness using the dashboard below, then finish Step 1. Prior CLEAR reviews or dashboard skips never replace Step 9's gates. @@ -506,13 +505,13 @@ repeat Step 2 (decisions included) on the merged content, then Step 4. ## Step 11.5: Bind the reviews 1. **Select the two reviews.** Run `~/.claude/skills/gstack/bin/gstack-review-read`. - Select this invocation's final Step 9 record (`skill:"review"`, `via:"ship"`, or - 3.5's `review-fix-check` when it ended the loop) and Step 11 native record - (`skill:"adversarial-review"`; none after `ADVERSARIAL: skipped`). Match each to its saved + Select this invocation's final Step 9 record (`skill:"review"`, `via:"ship"`, or a + last 3.5 `review-fix-check`) and Step 11 native record (`skill:"adversarial-review"`; + none after `ADVERSARIAL: skipped`). Match each to its saved handle, original token and source; reject outside-provider or older records. 2. **Compare their content.** Require the native record's `review_binding.state` - to be `verified`. All three snapshots must match: its `wtree` (with no native record, - `review_freshness` `CURRENT`), the Step 9 record's + to be `verified`. All three snapshots must match: its `wtree` (none: `review_freshness` + `CURRENT`), the Step 9 record's `review_binding.start_wtree` and `review_binding.end_wtree`. A mismatch or missing record/field blocks release preparation: report **Review records missing or mismatched** and insert `9 → 10 → 11 → 11.5` before Step 12. Bind the new records at 11.5. @@ -998,3 +997,10 @@ hand-roll VERSION/package.json writes. ## Important Rules Follow the numbered gates and their explicit exceptions. + +- **Never force push.** Use regular `git push` only. +- **An early Greptile PR stays up.** A stop after Step 6.5 opened it leaves the draft + open with one comment saying why (greptile-early section); never close it. +- **Use the configured version file's format** (4-digit for VERSION); under NO_VERSION, + never invent one. +- **Step 7 generates coverage tests.** They must pass before committing. Never commit failing tests. diff --git a/ship/SKILL.md.tmpl b/ship/SKILL.md.tmpl index 10772230d..ec7995e29 100644 --- a/ship/SKILL.md.tmpl +++ b/ship/SKILL.md.tmpl @@ -72,8 +72,8 @@ not a commit ID. Use `git diff ` to compare these snapshots 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; 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. +is the cap. `NO FINDINGS` is a complete reviewer result. `ship: review_scope` (Step 3) +is the knob; the receipt's `review_loop` records the outcome. ### Ship control flow @@ -136,11 +136,10 @@ repository-landing asks, including on Apple repos. 4. **Carry check.** If `gh pr list --head --state open --json number` names a PR, run `~/.claude/skills/gstack/bin/gstack-ship-receipt carry --pr --base --dry-run`. - `CARRY: would carry` (product patch-id unchanged; only a base merge, tests or release - metadata moved): skip review, fix only what the merge broke (CHANGELOG, size caps), run - `gstack-pregate --allow-repo-commands`, then carry without `--dry-run`, adding `--pregate - .gstack/tmp/pregate.json`. `CARRY: carried` binds HEAD: stop at its `GSTACK_RESULT` line. - A failed check, a moved HEAD, `refused` or `current` re-runs Steps 2–16. + `CARRY: would carry` (same product patch-id): skip review, fix what the merge broke + (CHANGELOG, size caps), run `gstack-pregate --allow-repo-commands`, then carry without + `--dry-run` plus `--pregate .gstack/tmp/pregate.json`. `CARRY: carried` binds HEAD: stop. + A failed check, HEAD moving after pregate, `refused` or `current` re-runs Steps 2–16. 5. Display historical readiness using the dashboard below, then finish Step 1. Prior CLEAR reviews or dashboard skips never replace Step 9's gates. @@ -229,13 +228,13 @@ repeat Step 2 (decisions included) on the merged content, then Step 4. ## Step 11.5: Bind the reviews 1. **Select the two reviews.** Run `~/.claude/skills/gstack/bin/gstack-review-read`. - Select this invocation's final Step 9 record (`skill:"review"`, `via:"ship"`, or - 3.5's `review-fix-check` when it ended the loop) and Step 11 native record - (`skill:"adversarial-review"`; none after `ADVERSARIAL: skipped`). Match each to its saved + Select this invocation's final Step 9 record (`skill:"review"`, `via:"ship"`, or a + last 3.5 `review-fix-check`) and Step 11 native record (`skill:"adversarial-review"`; + none after `ADVERSARIAL: skipped`). Match each to its saved handle, original token and source; reject outside-provider or older records. 2. **Compare their content.** Require the native record's `review_binding.state` - to be `verified`. All three snapshots must match: its `wtree` (with no native record, - `review_freshness` `CURRENT`), the Step 9 record's + to be `verified`. All three snapshots must match: its `wtree` (none: `review_freshness` + `CURRENT`), the Step 9 record's `review_binding.start_wtree` and `review_binding.end_wtree`. A mismatch or missing record/field blocks release preparation: report **Review records missing or mismatched** and insert `9 → 10 → 11 → 11.5` before Step 12. Bind the new records at 11.5. @@ -717,3 +716,10 @@ hand-roll VERSION/package.json writes. ## Important Rules Follow the numbered gates and their explicit exceptions. + +- **Never force push.** Use regular `git push` only. +- **An early Greptile PR stays up.** A stop after Step 6.5 opened it leaves the draft + open with one comment saying why (greptile-early section); never close it. +- **Use the configured version file's format** (4-digit for VERSION); under NO_VERSION, + never invent one. +- **Step 7 generates coverage tests.** They must pass before committing. Never commit failing tests. diff --git a/ship/sections/adversarial.md b/ship/sections/adversarial.md index 66bc6fa5c..bfff86465 100644 --- a/ship/sections/adversarial.md +++ b/ship/sections/adversarial.md @@ -1,6 +1,6 @@ -**Step 9's latest plan governs this step:** run the native pass only on +**Step 9's last full-pass 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)`. diff --git a/ship/sections/adversarial.md.tmpl b/ship/sections/adversarial.md.tmpl index 8700e5fed..ddffca679 100644 --- a/ship/sections/adversarial.md.tmpl +++ b/ship/sections/adversarial.md.tmpl @@ -1,4 +1,4 @@ -**Step 9's latest plan governs this step:** run the native pass only on +**Step 9's last full-pass 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)`. diff --git a/test/autoplan-token-efficiency.test.ts b/test/autoplan-token-efficiency.test.ts index fe52ba6df..c6d396948 100644 --- a/test/autoplan-token-efficiency.test.ts +++ b/test/autoplan-token-efficiency.test.ts @@ -182,7 +182,7 @@ describe('ESCALATE at the round cap', () => { describe('docs-only', () => { test('docs-class paths', () => { - for (const p of ['README.md', 'CHANGELOG.md', 'TODOS.md', 'docs/howto-retry.md', 'docs/designs/x/PLAN.md', 'notes.md', './README.md']) expect(docsClassPath(p, ROOT), p).toBe(true); + for (const p of ['README.md', 'CHANGELOG.md', 'TODOS.md', 'docs/howto-retry.md', 'docs/designs/x/PLAN.md', 'notes.md', './README.md', 'docs/images/flow.png', 'docs/designs']) expect(docsClassPath(p, ROOT), p).toBe(true); for (const p of ['docs/review-policy.md', 'autoplan/SKILL.md.tmpl', 'autoplan/SKILL.md', 'review/sections/army.md', 'scripts/resolvers/autoplan-driver.ts', 'autoplan/DRIVER.md', 'captain/templates/brief.md', 'autoplan/notes.md', 'AGENTS.md', 'CLAUDE.md', 'lib/worker.ts', 'bin/gstack-autoplan']) expect(docsClassPath(p, ROOT), p).toBe(false); expect(detectDocsOnly('Rewrite README.md and docs/howto.md.', ROOT)).toMatchObject({ docsOnly: true, code: [] }); diff --git a/test/fixtures/golden/agy-ship-SKILL.md b/test/fixtures/golden/agy-ship-SKILL.md index 8afc8d3ee..0a8e260df 100644 --- a/test/fixtures/golden/agy-ship-SKILL.md +++ b/test/fixtures/golden/agy-ship-SKILL.md @@ -206,8 +206,8 @@ not a commit ID. Use `git diff ` to compare these snapshots 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; 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. +is the cap. `NO FINDINGS` is a complete reviewer result. `ship: review_scope` (Step 3) +is the knob; the receipt's `review_loop` records the outcome. ### Ship control flow @@ -328,12 +328,11 @@ repository-landing asks, including on Apple repos. `git log origin/..HEAD --oneline`. 4. **Carry check.** If `gh pr list --head --state open --json number` - names a PR, run `~/.gemini/antigravity-cli/skills/gstack/bin/gstack-ship-receipt carry --pr --base `. - `CARRY: carried` means the product diff against `origin/` has the same - patch-id as the reviewed head's (only a base merge, tests or release metadata - changed; the receipt's `carry` object holds the range-diff evidence): the receipt - now binds HEAD and the run is complete at its `GSTACK_RESULT` line, so stop. - `CARRY: refused` or `current` continues; a product change always re-runs Steps 2–16. + names a PR, run `~/.gemini/antigravity-cli/skills/gstack/bin/gstack-ship-receipt carry --pr --base --dry-run`. + `CARRY: would carry` (same product patch-id): skip review, fix what the merge broke + (CHANGELOG, size caps), run `gstack-pregate --allow-repo-commands`, then carry without + `--dry-run` plus `--pregate .gstack/tmp/pregate.json`. `CARRY: carried` binds HEAD: stop. + A failed check, HEAD moving after pregate, `refused` or `current` re-runs Steps 2–16. 5. Display historical readiness using the dashboard below, then finish Step 1. Prior CLEAR reviews or dashboard skips never replace Step 9's gates. @@ -506,11 +505,13 @@ repeat Step 2 (decisions included) on the merged content, then Step 4. ## Step 11.5: Bind the reviews 1. **Select the two reviews.** Run `~/.gemini/antigravity-cli/skills/gstack/bin/gstack-review-read`. - Select this invocation's final Step 9.4 record (`skill:"review"`, `via:"ship"`) - and Step 11 native record (`skill:"adversarial-review"`). Match each to its saved + Select this invocation's final Step 9 record (`skill:"review"`, `via:"ship"`, or a + last 3.5 `review-fix-check`) and Step 11 native record (`skill:"adversarial-review"`; + none after `ADVERSARIAL: skipped`). Match each to its saved handle, original token and source; reject outside-provider or older records. 2. **Compare their content.** Require the native record's `review_binding.state` - to be `verified`. All three snapshots must match: its `wtree`, Step 9.4's + to be `verified`. All three snapshots must match: its `wtree` (none: `review_freshness` + `CURRENT`), the Step 9 record's `review_binding.start_wtree` and `review_binding.end_wtree`. A mismatch or missing record/field blocks release preparation: report **Review records missing or mismatched** and insert `9 → 10 → 11 → 11.5` before Step 12. Bind the new records at 11.5. diff --git a/test/fixtures/golden/claude-ship-SKILL.md b/test/fixtures/golden/claude-ship-SKILL.md index 0d8b8c0b5..3d1978825 100644 --- a/test/fixtures/golden/claude-ship-SKILL.md +++ b/test/fixtures/golden/claude-ship-SKILL.md @@ -206,8 +206,8 @@ not a commit ID. Use `git diff ` to compare these snapshots 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; 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. +is the cap. `NO FINDINGS` is a complete reviewer result. `ship: review_scope` (Step 3) +is the knob; the receipt's `review_loop` records the outcome. ### Ship control flow @@ -328,12 +328,11 @@ repository-landing asks, including on Apple repos. `git log origin/..HEAD --oneline`. 4. **Carry check.** If `gh pr list --head --state open --json number` - names a PR, run `~/.claude/skills/gstack/bin/gstack-ship-receipt carry --pr --base `. - `CARRY: carried` means the product diff against `origin/` has the same - patch-id as the reviewed head's (only a base merge, tests or release metadata - changed; the receipt's `carry` object holds the range-diff evidence): the receipt - now binds HEAD and the run is complete at its `GSTACK_RESULT` line, so stop. - `CARRY: refused` or `current` continues; a product change always re-runs Steps 2–16. + names a PR, run `~/.claude/skills/gstack/bin/gstack-ship-receipt carry --pr --base --dry-run`. + `CARRY: would carry` (same product patch-id): skip review, fix what the merge broke + (CHANGELOG, size caps), run `gstack-pregate --allow-repo-commands`, then carry without + `--dry-run` plus `--pregate .gstack/tmp/pregate.json`. `CARRY: carried` binds HEAD: stop. + A failed check, HEAD moving after pregate, `refused` or `current` re-runs Steps 2–16. 5. Display historical readiness using the dashboard below, then finish Step 1. Prior CLEAR reviews or dashboard skips never replace Step 9's gates. @@ -506,11 +505,13 @@ repeat Step 2 (decisions included) on the merged content, then Step 4. ## Step 11.5: Bind the reviews 1. **Select the two reviews.** Run `~/.claude/skills/gstack/bin/gstack-review-read`. - Select this invocation's final Step 9.4 record (`skill:"review"`, `via:"ship"`) - and Step 11 native record (`skill:"adversarial-review"`). Match each to its saved + Select this invocation's final Step 9 record (`skill:"review"`, `via:"ship"`, or a + last 3.5 `review-fix-check`) and Step 11 native record (`skill:"adversarial-review"`; + none after `ADVERSARIAL: skipped`). Match each to its saved handle, original token and source; reject outside-provider or older records. 2. **Compare their content.** Require the native record's `review_binding.state` - to be `verified`. All three snapshots must match: its `wtree`, Step 9.4's + to be `verified`. All three snapshots must match: its `wtree` (none: `review_freshness` + `CURRENT`), the Step 9 record's `review_binding.start_wtree` and `review_binding.end_wtree`. A mismatch or missing record/field blocks release preparation: report **Review records missing or mismatched** and insert `9 → 10 → 11 → 11.5` before Step 12. Bind the new records at 11.5. diff --git a/test/fixtures/golden/codex-ship-SKILL.md b/test/fixtures/golden/codex-ship-SKILL.md index f6040b513..d3689b753 100644 --- a/test/fixtures/golden/codex-ship-SKILL.md +++ b/test/fixtures/golden/codex-ship-SKILL.md @@ -204,8 +204,8 @@ not a commit ID. Use `git diff ` to compare these snapshots 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; 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. +is the cap. `NO FINDINGS` is a complete reviewer result. `ship: review_scope` (Step 3) +is the knob; the receipt's `review_loop` records the outcome. ### Ship control flow @@ -326,12 +326,11 @@ repository-landing asks, including on Apple repos. `git log origin/..HEAD --oneline`. 4. **Carry check.** If `gh pr list --head --state open --json number` - names a PR, run `$GSTACK_ROOT/bin/gstack-ship-receipt carry --pr --base `. - `CARRY: carried` means the product diff against `origin/` has the same - patch-id as the reviewed head's (only a base merge, tests or release metadata - changed; the receipt's `carry` object holds the range-diff evidence): the receipt - now binds HEAD and the run is complete at its `GSTACK_RESULT` line, so stop. - `CARRY: refused` or `current` continues; a product change always re-runs Steps 2–16. + names a PR, run `$GSTACK_ROOT/bin/gstack-ship-receipt carry --pr --base --dry-run`. + `CARRY: would carry` (same product patch-id): skip review, fix what the merge broke + (CHANGELOG, size caps), run `gstack-pregate --allow-repo-commands`, then carry without + `--dry-run` plus `--pregate .gstack/tmp/pregate.json`. `CARRY: carried` binds HEAD: stop. + A failed check, HEAD moving after pregate, `refused` or `current` re-runs Steps 2–16. 5. Display historical readiness using the dashboard below, then finish Step 1. Prior CLEAR reviews or dashboard skips never replace Step 9's gates. @@ -505,11 +504,13 @@ repeat Step 2 (decisions included) on the merged content, then Step 4. ## Step 11.5: Bind the reviews 1. **Select the two reviews.** Run `$GSTACK_ROOT/bin/gstack-review-read`. - Select this invocation's final Step 9.4 record (`skill:"review"`, `via:"ship"`) - and Step 11 native record (`skill:"adversarial-review"`). Match each to its saved + Select this invocation's final Step 9 record (`skill:"review"`, `via:"ship"`, or a + last 3.5 `review-fix-check`) and Step 11 native record (`skill:"adversarial-review"`; + none after `ADVERSARIAL: skipped`). Match each to its saved handle, original token and source; reject outside-provider or older records. 2. **Compare their content.** Require the native record's `review_binding.state` - to be `verified`. All three snapshots must match: its `wtree`, Step 9.4's + to be `verified`. All three snapshots must match: its `wtree` (none: `review_freshness` + `CURRENT`), the Step 9 record's `review_binding.start_wtree` and `review_binding.end_wtree`. A mismatch or missing record/field blocks release preparation: report **Review records missing or mismatched** and insert `9 → 10 → 11 → 11.5` before Step 12. Bind the new records at 11.5. diff --git a/test/fixtures/golden/factory-ship-SKILL.md b/test/fixtures/golden/factory-ship-SKILL.md index 4adc441a2..c88d04459 100644 --- a/test/fixtures/golden/factory-ship-SKILL.md +++ b/test/fixtures/golden/factory-ship-SKILL.md @@ -206,8 +206,8 @@ not a commit ID. Use `git diff ` to compare these snapshots 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; 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. +is the cap. `NO FINDINGS` is a complete reviewer result. `ship: review_scope` (Step 3) +is the knob; the receipt's `review_loop` records the outcome. ### Ship control flow @@ -328,12 +328,11 @@ repository-landing asks, including on Apple repos. `git log origin/..HEAD --oneline`. 4. **Carry check.** If `gh pr list --head --state open --json number` - names a PR, run `$GSTACK_ROOT/bin/gstack-ship-receipt carry --pr --base `. - `CARRY: carried` means the product diff against `origin/` has the same - patch-id as the reviewed head's (only a base merge, tests or release metadata - changed; the receipt's `carry` object holds the range-diff evidence): the receipt - now binds HEAD and the run is complete at its `GSTACK_RESULT` line, so stop. - `CARRY: refused` or `current` continues; a product change always re-runs Steps 2–16. + names a PR, run `$GSTACK_ROOT/bin/gstack-ship-receipt carry --pr --base --dry-run`. + `CARRY: would carry` (same product patch-id): skip review, fix what the merge broke + (CHANGELOG, size caps), run `gstack-pregate --allow-repo-commands`, then carry without + `--dry-run` plus `--pregate .gstack/tmp/pregate.json`. `CARRY: carried` binds HEAD: stop. + A failed check, HEAD moving after pregate, `refused` or `current` re-runs Steps 2–16. 5. Display historical readiness using the dashboard below, then finish Step 1. Prior CLEAR reviews or dashboard skips never replace Step 9's gates. @@ -507,11 +506,13 @@ repeat Step 2 (decisions included) on the merged content, then Step 4. ## Step 11.5: Bind the reviews 1. **Select the two reviews.** Run `$GSTACK_ROOT/bin/gstack-review-read`. - Select this invocation's final Step 9.4 record (`skill:"review"`, `via:"ship"`) - and Step 11 native record (`skill:"adversarial-review"`). Match each to its saved + Select this invocation's final Step 9 record (`skill:"review"`, `via:"ship"`, or a + last 3.5 `review-fix-check`) and Step 11 native record (`skill:"adversarial-review"`; + none after `ADVERSARIAL: skipped`). Match each to its saved handle, original token and source; reject outside-provider or older records. 2. **Compare their content.** Require the native record's `review_binding.state` - to be `verified`. All three snapshots must match: its `wtree`, Step 9.4's + to be `verified`. All three snapshots must match: its `wtree` (none: `review_freshness` + `CURRENT`), the Step 9 record's `review_binding.start_wtree` and `review_binding.end_wtree`. A mismatch or missing record/field blocks release preparation: report **Review records missing or mismatched** and insert `9 → 10 → 11 → 11.5` before Step 12. Bind the new records at 11.5. diff --git a/test/helpers/carve-guards.ts b/test/helpers/carve-guards.ts index ce3c0a512..1da3dfe17 100644 --- a/test/helpers/carve-guards.ts +++ b/test/helpers/carve-guards.ts @@ -155,7 +155,7 @@ export const CARVE_GUARDS: Record = { }, behavioral: 'external', externalTest: 'test/skill-e2e-ship-section-loading.test.ts', - maxSkeletonBytes: 60_913, // GSTA-96 hoist merged with GSTA-92 phase 2 (2026-10-26): the hoisted skeleton (59,706) plus this PR's growth, keeping the hoist's 65-byte headroom; measured 60,848. Before: + v2.0 {{ASIDE_RESEARCH}} (Aside first, WebSearch fallback); measured 78_275. + gate integrity (HONEST_WORK_GATE_INTEGRITY W3): Step 16 stage 4 ZERO-RUN/unknown receipt rows, stage 5 honesty inventory, Step 17 open-gate ready-PR gate; measured 80,540 (2026-10-10). + rule 22 / GBRA-108 (2026-10-25): Step 22's terminal line (gstack-ship-receipt result, the GSTACK_RESULT line a parent greps) and Step 18's unattended routing (an open PR at HEAD gets its body updated, else handoff), offset in part by tightening Steps 14, 16, 18 and 20 prose after the 2026-10-14 tightening left 2 bytes of headroom; measured 81,535. + harness lane E (2026-10-26, issue row 3: four Captains re-ran /ship on identical product code): Step 1's carry check (gstack-ship-receipt carry binds an earlier receipt to HEAD when the product patch-id is unchanged); measured 82,165. + the Review policy paragraph (the blocking floor, pass structure and cap, NO FINDINGS, the review_loop receipt counters, the review_scope knob), stated in the skeleton because gstack is the canonical holder of its review policy (docs/review-policy.md); measured 82,645. + maxSkeletonBytes: 60_960, // GSTA-131 token efficiency (2026-10-27, Garry's D1): Step 1's delta carry (dry run, pregate at HEAD, then carry --pregate) and Step 11.5's fix-check and planned-skip binding, +60 bytes after tightening Step 1 and the Review policy paragraph; measured 60,960 (before: 60,900 on 10b77de4). Before that: GSTA-96 hoist merged with GSTA-92 phase 2 (2026-10-26): the hoisted skeleton (59,706) plus this PR's growth, keeping the hoist's 65-byte headroom; measured 60,848. Before: + v2.0 {{ASIDE_RESEARCH}} (Aside first, WebSearch fallback); measured 78_275. + gate integrity (HONEST_WORK_GATE_INTEGRITY W3): Step 16 stage 4 ZERO-RUN/unknown receipt rows, stage 5 honesty inventory, Step 17 open-gate ready-PR gate; measured 80,540 (2026-10-10). + rule 22 / GBRA-108 (2026-10-25): Step 22's terminal line (gstack-ship-receipt result, the GSTACK_RESULT line a parent greps) and Step 18's unattended routing (an open PR at HEAD gets its body updated, else handoff), offset in part by tightening Steps 14, 16, 18 and 20 prose after the 2026-10-14 tightening left 2 bytes of headroom; measured 81,535. + harness lane E (2026-10-26, issue row 3: four Captains re-ran /ship on identical product code): Step 1's carry check (gstack-ship-receipt carry binds an earlier receipt to HEAD when the product patch-id is unchanged); measured 82,165. + the Review policy paragraph (the blocking floor, pass structure and cap, NO FINDINGS, the review_loop receipt counters, the review_scope knob), stated in the skeleton because gstack is the canonical holder of its review policy (docs/review-policy.md); measured 82,645. minUnionBytes: 181_000, // token-reduction Phases 1-2 (v1.69.x branch); measured union 201,464 mustContain: ['VERSION', 'CHANGELOG', 'review', 'merge', 'PR'], // v1.58.5.0: pre-push-guard install (#2077) stacks on the shared first-run-guidance preamble. @@ -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.7615, // 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), the adversarial section's plan line, Step 1's delta carry sequence, Step 11.5's fix-check and planned-skip binding and the specialists' 400-word findings cap and the Step 7/8 audits' light-effort line, offset by a tighter Step 1, +1,614 union bytes; measured 330,638 / 187,706 = 1.76147 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. + maxSizeRatio: 1.7631, // 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), the adversarial section's plan line, Step 1's delta carry sequence, Step 11.5's fix-check and planned-skip binding and the specialists' 400-word findings cap and the Step 7/8 audits' light-effort line, offset by a tighter Step 1, +1,917 union bytes; measured 330,941 / 187,706 = 1.76308 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-log-findings.test.ts b/test/review-log-findings.test.ts index 361fdd824..c463521c3 100644 --- a/test/review-log-findings.test.ts +++ b/test/review-log-findings.test.ts @@ -178,6 +178,19 @@ describe('lib: deriveFromFindings, bindReview order and reviewFreshness', () => expect(fs.existsSync(path.join(dir, `${token}.json`))).toBe(false); }); + test('a review-fix-check record binds like a pass, so Step 11.5 can read its CURRENT freshness', () => { + const dir = path.join(home, 'starts', '.review-starts'); + fs.mkdirSync(dir, { recursive: true }); + const token = '66666666-2222-3333-4444-555555555555'; + const w = 'c'.repeat(64); + fs.writeFileSync(path.join(dir, `${token}.json`), JSON.stringify({ skill: 'review-fix-check', repo: '/r', branch: 'b', wtree: w, started_at: 't' })); + const env = { GSTACK_REVIEW_DIR: path.join(home, 'starts'), GSTACK_REVIEW_REPO: '/r', GSTACK_REVIEW_BRANCH: 'b', GSTACK_STAMP_WTREE: w }; + const bound = bindReview({ skill: 'review-fix-check', status: 'clean', completed: true, converged: true }, token, env); + expect(bound.review_binding).toMatchObject({ state: 'verified', start_wtree: w, end_wtree: w }); + expect(reviewFreshness(bound, w)?.status).toBe('CURRENT'); + expect(reviewFreshness(bound, 'd'.repeat(64))?.status).toBe('STALE'); + }); + test('reviewFreshness reads findings_open: resolved criticals are CURRENT, claimed issues_found stays conservative', () => { const w = 'b'.repeat(64); const base = { skill: 'review', completed: true, converged: true, wtree: w, review_binding: { state: 'verified', start_wtree: w, end_wtree: w } }; diff --git a/test/review-plan-cli.test.ts b/test/review-plan-cli.test.ts index 45ceade59..c8aedb90e 100644 --- a/test/review-plan-cli.test.ts +++ b/test/review-plan-cli.test.ts @@ -92,4 +92,20 @@ describe('gstack-review-plan', () => { expect(spawnSync(BIN, [], { encoding: 'utf8', timeout: 10_000 }).status).toBe(2); expect(run(repo({ 'a.md': 'x' }), ['--last', 'later']).status).toBe(2); }); + test('scratch under .gstack/tmp/ is ignored, a subdirectory run plans the whole repository, and --adversarial-ran drops the repeat adversarial pass', () => { + const r = repo({ 'docs/a.md': '# a\n' }); + fs.mkdirSync(path.join(r.dir, '.gstack', 'tmp'), { recursive: true }); + fs.writeFileSync(path.join(r.dir, '.gstack', 'tmp', 'fix-check-list.ts'), 'export const x = 1;\n'); + const plan = JSON.parse(run(r, ['--json']).stdout); + expect(plan.class).toBe('docs'); + expect(plan.paths).toEqual(['docs/a.md']); + const sub = spawnSync(BIN, ['--base', 'main', '--gate-tagged', '0', '--json'], { cwd: path.join(r.dir, 'docs'), encoding: 'utf8', timeout: 60_000, env: { ...process.env, GSTACK_STATE_ROOT: r.state } }); + expect(JSON.parse(sub.stdout).paths).toEqual(['docs/a.md']); + const code = repo({ 'src/a.ts': Array.from({ length: 80 }, (_, i) => `export const v${i} = ${i};`).join('\n') + '\n' }); + const repeat = ['--pass', '1', '--last', 'full', '--fixed-blocking']; + expect(run(code, repeat).stdout).toContain('DISPATCH: adversarial-native adversarial effort=full'); + const ran = run(code, [...repeat, '--adversarial-ran']).stdout; + expect(ran).not.toContain('DISPATCH: adversarial-native'); + expect(ran).toContain('SKIP: adversarial-native and outside-voice (run once per invocation)'); + }); }); diff --git a/test/ship-control-flow.test.ts b/test/ship-control-flow.test.ts index 3e0a270cc..3373cb28f 100644 --- a/test/ship-control-flow.test.ts +++ b/test/ship-control-flow.test.ts @@ -46,16 +46,16 @@ describe('ship source controller', () => { expect(decide).toContain('`NEXT: stop` stops here, unfixed; `full` is decision 3, `fix-check` 3.5, `done` 4.'); expect(decide).toContain('`gstack-review-log --start review-fix-check` token'); expect(decide).toContain('`"skill":"review-fix-check"` with `--finish `'); - expect(compact(read('ship/sections/adversarial.md.tmpl'))).toContain("Step 9's latest plan governs this step"); + expect(compact(read('ship/sections/adversarial.md.tmpl'))).toContain("Step 9's last full-pass plan governs this step"); const bind = compact(entry.slice(entry.indexOf('## Step 11.5:'), entry.indexOf('## Step 12:'))); - expect(bind).toContain("3.5's `review-fix-check` when it ended the loop"); + expect(bind).toContain("last 3.5 `review-fix-check`"); expect(bind).toContain('none after `ADVERSARIAL: skipped`'); expect(bind).toContain('`review_freshness` `CURRENT`'); }); test('Step 1 carries a receipt only after a dry run and a pregate at HEAD', () => { const carry = compact(entry.slice(entry.indexOf('4. **Carry check.**'), entry.indexOf('5. Display historical readiness'))); - const order = ['carry --pr --base --dry-run', '`CARRY: would carry`', 'skip review', '`gstack-pregate --allow-repo-commands`', 'carry without `--dry-run`', '`--pregate .gstack/tmp/pregate.json`', '`CARRY: carried` binds HEAD', 'A failed check, a moved HEAD, `refused` or `current` re-runs Steps 2–16.']; + const order = ['carry --pr --base --dry-run', '`CARRY: would carry`', 'skip review', '`gstack-pregate --allow-repo-commands`', 'carry without `--dry-run`', '`--pregate .gstack/tmp/pregate.json`', '`CARRY: carried` binds HEAD', 'A failed check, HEAD moving after pregate, `refused` or `current` re-runs Steps 2–16.']; const at = order.map(t => carry.indexOf(t)); expect(at.every(i => i >= 0)).toBe(true); expect(at).toEqual([...at].sort((a, b) => a - b));