From 889b99bbda1cc84bd38470bcae35e731cf770211 Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 3 Nov 2026 03:51:25 +0000 Subject: [PATCH] review: default review.panel all, one-reader shape opt-in (GSTA-258 build C, option B) Today's reviewer panel stays the default dispatch. review.panel risk (and off) opt in to the one-reader shape, whose no-loss replay came back FIX-METHOD (readers saw only the diff, no repository: 2/68 non-risk blockers). Raise-only additions stay on by default: HARN-1, eval and policy classes, GST-1129 spend-binding money and --declare-risk, doc secrets. Platform depth stays off by default. Repins ship/sections/review-army.md, ship/sections/adversarial.md, lib/review-depth.ts and lib/review-risk.ts under no-loss check GSTA-258-C: the planner over the last 200 first-parent main commits to 6f1dfd5b7c plans the same depth, classes and dispatches on 196 and only raises on the other 4; brief bodies still hash to their pins. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- bin/gstack-review-plan | 5 +++-- changelog.d/gsta258-c-review-depth.md | 6 +++--- docs/review-policy.md | 8 ++++++-- lib/review-depth.ts | 3 ++- lib/ship-policy-review.ts | 2 +- lib/ship-policy.ts | 4 ++-- test/review-depth-lean.test.ts | 11 +++++++++-- test/review-dispatch-origin-base.test.ts | 2 -- test/review-exclude.test.ts | 5 ++--- test/review-packets-removed.test.ts | 9 +++++---- test/review-pass.test.ts | 4 ---- test/review-plan-cli.test.ts | 7 +------ test/review-targeted.test.ts | 7 +++---- test/ship-receipt-platform.test.ts | 2 +- 14 files changed, 38 insertions(+), 37 deletions(-) diff --git a/bin/gstack-review-plan b/bin/gstack-review-plan index fa224a4b1..bb5e5ab9f 100755 --- a/bin/gstack-review-plan +++ b/bin/gstack-review-plan @@ -75,10 +75,11 @@ const HELP = `gstack-review-plan — the review depth of the next /ship step (do the detected classes and only raise the depth; RISK_DECLARED: prints both (GST-1129) --fix-check-prompt print the fix-check reviewer's prompt from --fix-list, --hunks, --tests and --callers files -Mode (R65, the base policy's review key): lean (default) runs one reader: risk class gets the merged reader on review.risk_model +Mode (R65, the base policy's review key): review.panel all (default) keeps today's panel on every class. review.panel risk +(opt-in; its one-reader shape has not passed a no-loss replay) runs lean off risk class, and review.panel off everywhere: lean runs one reader: risk class gets the merged reader on review.risk_model (the native adversarial brief plus the checklists, the Red Team and the targeted brief), non-risk code the single reader at the lane's reasoning (cross-family when it publishes numbers), docs and tests the core read; one fix-check after fixes, never more -than 2 rounds. review.panel risk|all keeps today's panel there. review.platform on: a light diff with no numeric claims whose PR +than 2 rounds. review.platform on (any panel): a light diff with no numeric claims whose PR carries capy-ai[bot]'s review at HEAD plans NEXT: platform and a planned SKIP for every reader. Depth (R51, risk not size): full on gate class, a risk class (money, auth, secret, security, data, review.risk_paths) or any DEPTH_ERROR; standard only by --depth-floor or a raise; otherwise light. TARGETED: the money, diff --git a/changelog.d/gsta258-c-review-depth.md b/changelog.d/gsta258-c-review-depth.md index 0710321a2..32f06c39e 100644 --- a/changelog.d/gsta258-c-review-depth.md +++ b/changelog.d/gsta258-c-review-depth.md @@ -1,7 +1,7 @@ -**/ship's review now scales to risk: risk-class code keeps the full panel, everything else gets one strong reader, and a repo with platform review can skip /ship's readers on plain docs (GSTA-258 build C).** Across 153 reviewed units, docs and tests PRs produced no proven review blocker, and non-risk code got most of its proven blockers from the native adversarial pass. Risk-class code (money, auth, secrets, security, user data, eval scorers and the merge checks) found 33 of its 110 proven blockers in a second or third pass, so it keeps the full panel and a full second round. +**/ship sends more diffs to full review: spend-binding code, eval scorers, a repo's own risk paths and docs that print secrets now count as risk, and a lane can declare a class the matchers miss (GSTA-258 build C).** Today's reviewer panel stays the default. The one-reader shape for non-risk code ships as an opt-in (`review.panel risk`) because its no-loss replay didn't pass: with only the diff and no repository, the reader found 2 of 68 non-risk blockers, so the replay is being redone with readers working in a checkout. A repo with platform review on can skip /ship's readers on plain docs. ### Added -- **A `review` key in `.gstack/ship-policy.json`**, read from the base branch only (needs `min_gstack_version` at the release that adds it): `panel` (`risk` by default, `off` or `all`), `reader_model` (`lane` by default), `platform` (`off` by default) and `risk_paths` (globs of code paths that count as risk class `policy`). +- **A `review` key in `.gstack/ship-policy.json`**, read from the base branch only (needs `min_gstack_version` at the release that adds it): `panel` (`all` by default, today's panel; `risk` or `off` opt in to one reader), `reader_model` (`lane` by default), `platform` (`off` by default) and `risk_paths` (globs of code paths that count as risk class `policy`). - **`gstack-review-plan` prints `REVIEW_MODE:`** and, under `review.platform on`, `PLATFORM_REVIEW:`. `DISPATCH:` lines name the reader's `model=` and `classes=`. New flags: `--fix-checks ` and `--pr `. - **The platform depth.** Under `review.platform on`, a docs or tests diff with no numeric, results or prereg claims, whose PR carries capy-ai[bot]'s review at the exact head, plans `NEXT: platform` with a planned `SKIP:` for every reader. Step 11.5 accepts it, and `gstack-ship-receipt write --platform-review ` records `review_depth: platform` with that review's id and head. It never applies to runtime code, and never where platform review is off. - **New risk classes**: `eval` (scorer, judge and grader paths) and `policy` (`review.risk_paths`). A docs line that can print a credential (an `Authorization:` header, a bearer value, a key-shaped literal, `mcp get`) is security risk (gbrain#8417). @@ -10,6 +10,6 @@ - **Code that reserves, binds, leases or settles spend is money** (GST-1129): `bindCheck(ledger, …)`, `reserveSpend` or `bindCap` in a code file now plans full depth. gstack-evals#170 got its full review only because an unrelated `UBICLOUD_API_KEY` line was in the diff. ### Changed -- **Non-risk code gets one native adversarial reader** at the lane's reasoning, carrying the code checklists and the Red Team checklist, in place of the batched specialist plus the native pass. One fix-check follows a fixed blocker; below-floor fixes ship as notes. `review.panel all` restores the old plan and loop everywhere. +- **Opt-in one-reader review** (`review.panel risk`): non-risk code gets one native adversarial reader at the lane's reasoning, carrying the code checklists and the Red Team checklist, and risk class keeps the full panel. One fix-check follows a fixed blocker. It isn't the default until its no-loss replay passes. - **Fewer false risk triggers** (harness-evals#231): committed `*.sqlite` and other binary data files count as data, never code; JSON, YAML and other config files are judged by their path, so a `lease_usd` value no longer means money; analysis scripts under `docs/`, `results/` or `benchmarks/` are judged by their path. - The outside voice reads its diff from a file the brief names with its sha256, instead of a diff pasted into the prompt. Past 2,000,000 bytes it reads a path-scoped slice whose brief lists the files it left out, instead of stopping the pass. diff --git a/docs/review-policy.md b/docs/review-policy.md index 092a908e1..e9e5aa1ca 100644 --- a/docs/review-policy.md +++ b/docs/review-policy.md @@ -182,11 +182,13 @@ A plan that fails closed to full exits 0; a diff git cannot list exits 1. ### Who reads (R65, the base policy's `review` key) The depth says how much review a diff needs; the base branch's `.gstack/ship-policy.json` -`review` key says who does it (`review.panel`, default `risk`). `gstack-review-plan` +`review` key says who does it (`review.panel`, default `all`: today's panel on every class). `gstack-review-plan` prints `REVIEW_MODE: panel=

reader_model= platform=` and, under `review.platform on`, `PLATFORM_REVIEW: reviewer=capy-ai[bot] head=` or `PLATFORM_REVIEW: none ()`. +Under the opt-in `review.panel risk` (and `review.platform on` under any panel): + | Diff | Pass 1 | After fixes (never more than 2 rounds) | |---|---|---| | Risk class: gate class, an R51 class (money, auth, secret, security, data), `eval` (scorer, judge and grader paths) or `policy` (`review.risk_paths`) | The full-depth plan as printed: the batched specialist, Red Team, native adversarial, outside voice and the targeted reader | A fixed blocker re-runs the printed plan on the fixed head as round 2; below-floor fixes get one fix-check | @@ -194,7 +196,9 @@ or `PLATFORM_REVIEW: none ()`. | Docs and tests | The core read; the outside voice only when the diff publishes numbers | One fix-check after a fixed blocker | | Docs or tests with no numeric, results or prereg claims, under `review.platform on`, with capy-ai[bot]'s review on the PR at HEAD | No reader: `NEXT: platform` and a planned `SKIP:` for every reader, the core read included | none | -`review.panel all` keeps today's panel and loop on every class; `review.panel off` gives +`review.panel all`, the default, keeps today's panel and loop on every class. `risk` is +opt-in because its one-reader shape has not passed its no-loss replay: build C's replay +scored it FIX-METHOD (its readers saw the diff without the repository). `review.panel off` gives risk class one merged reader too (`DISPATCH: adversarial-native merged`: the native adversarial brief, the gate-class checklists, the Red Team checklist and the targeted brief, whose ledger record is the receipt's `review_targeted`). The risk classes come diff --git a/lib/review-depth.ts b/lib/review-depth.ts index 43c6c7415..f1fc52688 100644 --- a/lib/review-depth.ts +++ b/lib/review-depth.ts @@ -549,7 +549,8 @@ export function planReview(i: PlanInput): ReviewPlan { const depth = decideDepth(c, c.runtimeLines ?? (CLASS_RANK[c.class] >= CLASS_RANK.code ? i.diffLines : 0), i.floor); const { class: cls, runtimeLines } = depth; const sh = i.shape; - const panel = !sh || sh.panel === 'all' || (sh.panel === 'risk' && riskClass(c, depth)); + const platformNow = !!sh && sh.platform === 'on' && sh.platformReview?.head === sh.head && i.history.fullPasses === 0 && !riskClass(c, depth) && platformFits(i.paths, depth.runtimeLines, i.numbers); + const panel = !platformNow && (!sh || sh.panel === 'all' || (sh.panel === 'risk' && riskClass(c, depth))); const next = nextStep({ ...i.history, gate: cls === 'gate', adversarialRan: !!i.adversarialRan, lean: !panel }, depth.depth); if (!panel) return leanPlan(i, c, depth, next); const h = i.history; diff --git a/lib/ship-policy-review.ts b/lib/ship-policy-review.ts index d2ed4165a..a039a4dff 100644 --- a/lib/ship-policy-review.ts +++ b/lib/ship-policy-review.ts @@ -6,7 +6,7 @@ * paths to R51's risk classes. */ export interface ReviewPolicy { panel: 'off' | 'risk' | 'all'; reader_model: string; platform: 'off' | 'on'; risk_paths: string[] } -export const REVIEW_POLICY_DEFAULT: ReviewPolicy = { panel: 'risk', reader_model: 'lane', platform: 'off', risk_paths: [] }; +export const REVIEW_POLICY_DEFAULT: ReviewPolicy = { panel: 'all', reader_model: 'lane', platform: 'off', risk_paths: [] }; const MODEL = /^[A-Za-z0-9][\w./:-]{0,127}$/; /** The key with every omitted field at its default (a policy file may set any subset). */ diff --git a/lib/ship-policy.ts b/lib/ship-policy.ts index 5ae121d92..5365a69fd 100644 --- a/lib/ship-policy.ts +++ b/lib/ship-policy.ts @@ -159,7 +159,7 @@ export const DEFAULT_POLICY: ShipPolicy = { stamp_sensitive_gates: [], review_scope: 'frozen', review_exclude_paths: [], - review: { panel: 'risk', reader_model: 'lane', platform: 'off', risk_paths: [] }, + review: { panel: 'all', reader_model: 'lane', platform: 'off', risk_paths: [] }, pr_count: { default_pr_count: 1, split_requires_owner: true }, maintainers: [], stamp_files: [], @@ -192,7 +192,7 @@ export const POLICY_EXPLANATIONS: Record = { stamp_sensitive_gates: 'Evidence labels (gstack-evidence --label) that read VERSION or generated bytes; the tree receipt never marks them reusable.', review_scope: 'frozen | diff-plus-blocking | all. frozen (default): /ship Step 9 fixes findings in the diff on pass 1, files blocking findings in code the PR did not change as issues, and later passes read only the range since the previous pass and fix only blocking findings; diff-plus-blocking: the same, but a blocking finding in code the PR did not change is fixed in this PR instead of filed; all: every finding is fixed now (the pre-1.91.249 loop).', review_exclude_paths: 'Git glob pathspecs, root-anchored and case-sensitive (`receipts/**`, `**/*.jsonl`; at most 64, 256 characters each, no leading `:` or `!`): /ship\'s reviewers leave out the data, docs, binary and .log files they match. Read from the base branch only, so a branch cannot exclude its own files. Code, gate paths, agent prose, config, executables, .gstack/ and .github/ files, gate-tagged or secret-bearing files and files a kept code or test file names by full path stay reviewed (docs/ship-policy.md#review-exclude-paths).', - review: '{ panel?, reader_model?, platform?, risk_paths? }, read from the base branch only. panel: risk (default: today\'s panel at full depth on risk class, one reader on the rest) | off (one merged reader on risk class too) | all (today\'s panel and loop everywhere); every value but all runs one fix round. reader_model: the model of the one reader a lean depth runs, `lane` (default: the lane\'s own model, the native adversarial reader), `cross-family` or a model id. platform: off (default) | on: a docs or tests diff with no numeric, results or prereg claims whose PR carries the platform review (capy-ai[bot]) at the exact head skips /ship\'s readers (review_depth platform); never runtime code. risk_paths: globs of code paths that are risk class on top of R51\'s; data, docs and binary files under them stay out (docs/review-policy.md#review-depth).', + review: '{ panel?, reader_model?, platform?, risk_paths? }, read from the base branch only. panel: all (default: today\'s panel and loop everywhere) | risk (opt-in: today\'s panel at full depth on risk class, one reader on the rest) | off (one merged reader on risk class too); risk and off run one fix round. Their one-reader shape has not passed a no-loss replay yet (GSTA-258 C: FIX-METHOD). reader_model: the model of the one reader a lean depth runs, `lane` (default: the lane\'s own model, the native adversarial reader), `cross-family` or a model id. platform: off (default) | on: a docs or tests diff with no numeric, results or prereg claims whose PR carries the platform review (capy-ai[bot]) at the exact head skips /ship\'s readers (review_depth platform); never runtime code. risk_paths: globs of code paths that are risk class on top of R51\'s; data, docs and binary files under them stay out (docs/review-policy.md#review-depth).', pr_count: '{ default_pr_count, split_requires_owner }: the PR-count row in plan reviews.', maintainers: 'GitHub logins treated as maintainers by /review contributor mode; empty uses repository push permission.', stamp_files: 'The repo’s stamp rule: globs of the only files that may differ by a stamp (version file, manifests, plugin trees, the PR’s own since: rows). Empty keeps gstack’s per-file rules; set, it applies before every rule.', diff --git a/test/review-depth-lean.test.ts b/test/review-depth-lean.test.ts index 60cdbf40e..45d520a46 100644 --- a/test/review-depth-lean.test.ts +++ b/test/review-depth-lean.test.ts @@ -86,7 +86,7 @@ describe('R65 lean dispatch (GSTA-258 build C): one reviewer, once', () => { expect(roles(p)).toEqual(['adversarial-native:single:full:lane']); expect(roles(planReview(base({ shape: lean({ readerModel: 'cross-family' }) })))).toEqual(['adversarial-native:single:full:cross-family']); }); - test('default (panel risk, FOUN-64 item 1): a risk-class diff gets the printed full-depth panel; a fixed blocker re-runs the printed plan as round 2 (FOUN-32 §5)', () => { + test('panel risk (opt-in, FOUN-64 item 1): a risk-class diff gets the printed full-depth panel; a fixed blocker re-runs the printed plan as round 2 (FOUN-32 §5)', () => { const today = roles(planReview(base({ paths: ['lib/billing.ts'], targeted: { classes: ['money'] }, shape: undefined }))); const p = planReview(base({ paths: ['lib/billing.ts'], targeted: { classes: ['money'] } })); expect([p.depth, p.mode, roles(p)]).toEqual(['full', 'panel', today]); @@ -140,6 +140,13 @@ describe('C6 platform depth (FOUN-32, founderstar#496): a planned skip needs the expect(planReview(base({ paths: ['lib/billing.ts'], shape: on(ev) })).mode).toBe('panel'); expect(planReview(base({ paths: ['src/app.ts', 'docs/r.md'], numbers: scanNumbers({ 'docs/r.md': ['p50 4.7 h'] }), shape: on(ev) })).mode).toBe('lean'); }); + test('under the default panel all, platform on still takes the platform depth at HEAD; everything else keeps today\'s panel', () => { + const ev = { id: '1', head: HEAD, reviewer: 'capy-ai[bot]' }; + const all = (review: ReviewShape['platformReview']) => lean({ panel: 'all', platform: 'on', platformReview: review }); + expect(planReview(base({ ...docs, shape: all(ev) })).mode).toBe('platform'); + expect(planReview(base({ ...docs, shape: all(null) })).mode).toBe('panel'); + expect(planReview(base({ shape: all(ev) })).mode).toBe('panel'); + }); test('the platform skip is pass 1 only: a later step plans as lean', () => { const p = planReview(base({ shape: on({ id: '1', head: HEAD, reviewer: 'capy-ai[bot]' }), history: { fullPasses: 1, last: 'full', fixedBlocking: true } })); expect(p.next.step).toBe('fix-check'); @@ -193,7 +200,7 @@ describe('C7 merged reader brief and the outside voice reading its diff from dis describe('ship policy: the base-read review key', () => { const codes = (raw: object) => validatePolicy(raw).map(e => `${e.code}:${e.path}`); - test('defaults: panel risk, the lane\'s native reader, platform off, no extra risk paths', () => expect(DEFAULT_POLICY.review).toEqual({ panel: 'risk', reader_model: 'lane', platform: 'off', risk_paths: [] })); + test('defaults: panel all (today\'s panel; risk is opt-in until its no-loss replay passes), the lane\'s native reader, platform off, no extra risk paths', () => expect(DEFAULT_POLICY.review).toEqual({ panel: 'all', reader_model: 'lane', platform: 'off', risk_paths: [] })); test('valid values pass with min_gstack_version; bad ones name the field', () => { const v = REVIEW_POLICY_KEY_VERSION; expect(codes({ review: { platform: 'on', reader_model: 'anthropic/claude-opus-5-5', panel: 'risk', risk_paths: ['src/balance/**'] }, min_gstack_version: v })).toEqual([]); diff --git a/test/review-dispatch-origin-base.test.ts b/test/review-dispatch-origin-base.test.ts index 40391918a..9f35c0cac 100644 --- a/test/review-dispatch-origin-base.test.ts +++ b/test/review-dispatch-origin-base.test.ts @@ -22,8 +22,6 @@ function fixture() { fs.writeFileSync(path.join(root, 'gitconfig'), '[commit]\n\tgpgsign = false\n'); const sh = (cwd: string, c: string) => { const r = spawnSync('bash', ['-c', c], { cwd, env, encoding: 'utf8', timeout: 60_000 }); if (r.status !== 0) throw new Error(`${c}: ${r.stderr}`); return r.stdout.trim(); }; sh(root, 'git init -q --bare -b main origin.git && git clone -q origin.git work && git clone -q origin.git other 2>/dev/null || true'); - // The briefs this case reads are today's panel (review.panel all); R65's one native reader renders at Step 11. - fs.mkdirSync(path.join(work, '.gstack')); fs.writeFileSync(path.join(work, '.gstack', 'ship-policy.json'), JSON.stringify({ review: { panel: 'all' }, min_gstack_version: '1.94.0.0' }) + '\n'); sh(work, "echo '{}' > package.json && mkdir src && echo 'export const a = 1;' > src/a.ts && git add -A && git commit -qm base && git push -q origin main"); sh(root, 'rm -rf other && git clone -q origin.git other'); sh(path.join(root, 'other'), 'mkdir -p up && for i in $(seq 1 12); do echo "export const u$i = $i;" > up/u$i.ts; done && git add -A && git commit -qm upstream && git push -q origin main'); diff --git a/test/review-exclude.test.ts b/test/review-exclude.test.ts index 2f16f5fca..714b8ab75 100644 --- a/test/review-exclude.test.ts +++ b/test/review-exclude.test.ts @@ -12,14 +12,13 @@ import { KEEP_ARG_MAX_BYTES, readSelection, reviewExclude, tooLargeLine, unreada import { collectCommit, collectLive } from '../lib/review-collect'; import { extractReceipt } from '../lib/ship-receipt'; import { bindShippedReceipt, receiptReviewAtHead } from '../lib/ship-receipt-depth'; -import { REVIEW_EXCLUDE_KEY_VERSION, REVIEW_POLICY_KEY_VERSION, isUnavailable } from '../lib/ship-policy'; +import { REVIEW_EXCLUDE_KEY_VERSION, isUnavailable } from '../lib/ship-policy'; import { loadPolicyAt } from '../lib/ship-policy-at'; import { cleanup, commit, makeFixtureRepo, runBin, write, type FixtureRepo } from './helpers/pregate-fixture'; const repos: FixtureRepo[] = []; afterEach(() => { while (repos.length) cleanup(repos.pop()!); }); -// review.panel all keeps today's panel, whose every brief these cases read; R65's one-reader default is pinned in review-depth-lean.test.ts. -const policyText = (patterns: string[]) => JSON.stringify({ review_exclude_paths: patterns, review: { panel: 'all' }, min_gstack_version: REVIEW_POLICY_KEY_VERSION }) + '\n'; +const policyText = (patterns: string[]) => JSON.stringify({ review_exclude_paths: patterns, min_gstack_version: REVIEW_EXCLUDE_KEY_VERSION }) + '\n'; const GZ = Buffer.from([0x1f, 0x8b, 0, 1, 2, 3, 0, 0]); const EXCLUDED = ['receipts/a/result.json', 'receipts/a/trial.gz', 'receipts/config/blob.bin', 'receipts/c/result.json', 'receipts/e/result.json', 'receipts/notes.md']; const KEPT = ['receipts/b/result.json', 'receipts/d/fixture.json', 'receipts/run.sh']; diff --git a/test/review-packets-removed.test.ts b/test/review-packets-removed.test.ts index 89eaf5aa5..70344cd38 100644 --- a/test/review-packets-removed.test.ts +++ b/test/review-packets-removed.test.ts @@ -73,11 +73,12 @@ const NO_LOSS_REPLAYS = { 'GST-967': "GST-967 dispatch-superset no-loss check (test/review-depth.test.ts, 'GST-967 dispatch-superset no-loss check'): over 1,440 planReview cases the branch plans exactly what main planned at 1022f4f except the 71 gate-class stops before Step 11, which add only Step 11's adversarial, outside and targeted voices, so no reviewer main dispatches is dropped or re-efforted; the brief bodies lib/review-brief.ts cuts from the ship sections hash to main's; only the lead's repeat-decision and Step 11 instructions changed; reviewer text is unchanged", 'GST-508+GST-464+GST-967': "wave review-53cfb5bfe011 merge of main 68410828f into #3852: ship/sections/review-army.md and adversarial.md are main's GST-967 bytes plus exactly the GST-508+GST-464 lines (the merged file minus main equals the wave minus fc292e22f, line for line), so GST-967's dispatch-superset no-loss check and GST-508's no-loss replay both hold; merged-head checks in GStack Minder drive handoffs/GSTA-202/merge-6841-checks.md", 'GST-508+R51+GST-911+GST-967': "wave review-53cfb5bfe011 merge of main 68410828f into #3852: lib/review-depth.ts is main's GST-967 bytes plus exactly the GST-508+R51+GST-911 lines (the merged file minus main equals the wave minus fc292e22f, line for line); GST-967's dispatch-superset no-loss check (test/review-depth.test.ts) passes on the merged file, and the GST-508+R51+GST-911 checks are rerun at the merged head (GStack Minder drive handoffs/GSTA-202/merge-6841-checks.md)", + 'GSTA-258-C': "GSTA-258 build C planner no-loss check: review.panel defaults to all, so the default plan is today's panel; over the last 200 first-parent main commits to 6f1dfd5b7c, planReview and collectCommit from origin/main and from this branch give the same depth, class, risk classes, next step and dispatches on 196, and the other 4 only raise: 4eff47c5cf goes light to full (eval class, main's own full panel adds the red team), e25997515c gains money at full gate, and 83ae96e086 and 7f815411f3 lose a money class from JSON lines (HARN-1) but keep full depth and the same reviewers (GStack Minder drive handoffs/GSTA-258/C-planner-noloss/); the one-reader shape ships opt-in (review.panel risk) because its own no-loss replay was FIX-METHOD (handoffs/GSTA-258/C-noloss-results.md); every brief body lib/review-brief.ts cuts from the ship sections still hashes to its pin, so only lead instructions outside the cut spans changed", } as const; type Replay = keyof typeof NO_LOSS_REPLAYS; const REVIEWER_BRIEFS: Record = { - 'ship/sections/review-army.md': { sha: 'de3e0d35f2af56492c42fd287b6868fbe19bce96b86321e74a4947ba909b74eb', replay: 'GST-508+GST-464+GST-967' }, - 'ship/sections/adversarial.md': { sha: '0142d6bc3f2a5bceb9f8b2adef1d3a206e5836c99a0667fe995568da2e80a5a6', replay: 'GST-508+GST-464+GST-967' }, + 'ship/sections/review-army.md': { sha: '25c09be7ea5c0e438ee059af2064f5bf038f6b69e3b5378f6c47be669f7ecd17', replay: 'GSTA-258-C' }, + 'ship/sections/adversarial.md': { sha: 'c623d65c4c53054108ac0166773c89233d76a25b0d35eb72d1c546f7bec4bdc9', replay: 'GSTA-258-C' }, 'review/sections/review-army.md': { sha: '78329b64640260cd8831ca81a9189f3f8dcade9c406431631d3f2cd798cd3967', replay: '#3638' }, 'review/sections/adversarial.md': { sha: 'e6962fa25b78f362654f2954f79683e524d2ebd452d1494f8a2b099888e8e255', replay: 'G1' }, 'review/checklist.md': { sha: 'a32963ef38d9d3613c58c805e8517cd2e593c63769b30dd7d40e2592fa48fc78', replay: 'GSTA-224 H1' }, @@ -94,8 +95,8 @@ const REVIEWER_BRIEFS: Record = { 'review/specialists/testing.md': { sha: 'd6cc2bef7be562387d0c0a22fefe646dff9c295f1d34d8453a6cb496584150e4', replay: '#3638' }, 'review/specialists/targeted.md': { sha: '7f92a0d0a8319c3c0e076fb3b1a4689c45120b2be2c14dfc7d4cb03aafa9d6a5', replay: 'GST-257' }, 'scripts/resolvers/fix-check.ts': { sha: '712bf5b0c439e96bac32f19cbe25394dabb4c94aa66600100bfde9b100e15a08', replay: '#3638' }, - 'lib/review-depth.ts': { sha: 'd76a9afcfadfe0a95b15428c04e6f7a88c6b0fa75591201c45f023fec2bb90db', replay: 'GST-508+R51+GST-911+GST-967' }, - 'lib/review-risk.ts': { sha: '126eff0f1de6982e03dc24d8017c67e6807c91dfa518b18d2db3aa910aac911f', replay: 'GST-508+R51+GST-911' }, + 'lib/review-depth.ts': { sha: '7d5e18436404ed8d620bfe6a7d042dff9251956a790d978407445ec7444a70ee', replay: 'GSTA-258-C' }, + 'lib/review-risk.ts': { sha: '2c5b96b921b8be5b977b734b3787361ed80e7d3f779ff6c4a20948f85e4d4474', replay: 'GSTA-258-C' }, }; /** diff --git a/test/review-pass.test.ts b/test/review-pass.test.ts index da11af484..9a2935dd6 100644 --- a/test/review-pass.test.ts +++ b/test/review-pass.test.ts @@ -10,7 +10,6 @@ import * as path from 'node:path'; import { promptHash, sectionTexts, withWorkspace, workspaceParagraph, workspaceStep } from '../lib/review-brief'; import { CHUNK_CHARS, PARENT_ONLY_QA, buildManifest, chunkCount, chunkOf, markReader, renderStage, specialistFindings, type Manifest } from '../lib/review-pass'; import type { Dispatch } from '../lib/review-depth'; -import { REVIEW_POLICY_KEY_VERSION } from '../lib/ship-policy'; const ROOT = path.resolve(import.meta.dir, '..'); const BIN = path.join(ROOT, 'bin'); @@ -132,9 +131,6 @@ function repo(): { dir: string; env: NodeJS.ProcessEnv } { const dir = tmp('rp-repo-'); const env = { ...process.env, PATH: BIN + path.delimiter + process.env.PATH, GSTACK_STATE_ROOT: path.join(dir, '.state'), GIT_AUTHOR_NAME: 't', GIT_AUTHOR_EMAIL: 't@t', GIT_COMMITTER_NAME: 't', GIT_COMMITTER_EMAIL: 't@t' }; const sh = (c: string) => { const r = spawnSync('bash', ['-c', c], { cwd: dir, env, encoding: 'utf8', timeout: 60_000 }); if (r.status !== 0) throw new Error(r.stderr); }; - // The pass program cases pin today's panel (review.panel all keeps it on every class); R65's default one-reader table is - // pinned in test/review-depth-lean.test.ts. - fs.mkdirSync(path.join(dir, '.gstack')); fs.writeFileSync(path.join(dir, '.gstack', 'ship-policy.json'), JSON.stringify({ review: { panel: 'all' }, min_gstack_version: REVIEW_POLICY_KEY_VERSION }) + '\n'); sh("git init -q -b main && echo '{}' > package.json && mkdir src && echo 'export const a = 1;' > src/a.ts && echo .state > .gitignore && git add -A && git commit -qm base && git checkout -qb feat"); sh('for i in $(seq 1 80); do echo "export const v$i = $i;" >> src/a.ts; done && git commit -qam feat'); return { dir, env }; diff --git a/test/review-plan-cli.test.ts b/test/review-plan-cli.test.ts index 5ec7e9657..3b2f9c47b 100644 --- a/test/review-plan-cli.test.ts +++ b/test/review-plan-cli.test.ts @@ -3,7 +3,6 @@ import { spawnSync } from 'node:child_process'; import * as fs from 'node:fs'; import * as os from 'node:os'; import * as path from 'node:path'; -import { REVIEW_POLICY_KEY_VERSION } from '../lib/ship-policy'; const BIN = path.join(import.meta.dir, '..', 'bin', 'gstack-review-plan'); const dirs: string[] = []; @@ -15,11 +14,7 @@ function repo(files: Record): { dir: string; state: string } { dirs.push(dir, state); const git = (...a: string[]) => spawnSync('git', a, { cwd: dir, encoding: 'utf8', timeout: 10_000 }); git('init', '-q', '-b', 'main'); git('config', 'user.email', 't@t'); git('config', 'user.name', 't'); git('config', 'commit.gpgsign', 'false'); - fs.writeFileSync(path.join(dir, 'README.md'), '# x\n'); - // These cases pin today's panel and its scoped second pass, which review.panel all keeps for every class; R65's default - // table (one reader off risk class) is pinned in test/review-depth-lean.test.ts. - fs.mkdirSync(path.join(dir, '.gstack')); fs.writeFileSync(path.join(dir, '.gstack', 'ship-policy.json'), JSON.stringify({ review: { panel: 'all' }, min_gstack_version: REVIEW_POLICY_KEY_VERSION }) + '\n'); - git('add', '.'); git('commit', '-qm', 'base'); + fs.writeFileSync(path.join(dir, 'README.md'), '# x\n'); git('add', '.'); git('commit', '-qm', 'base'); git('checkout', '-qb', 'feat'); for (const [p, text] of Object.entries(files)) { fs.mkdirSync(path.dirname(path.join(dir, p)), { recursive: true }); fs.writeFileSync(path.join(dir, p), text); } git('add', '.'); git('commit', '-qm', 'feat'); diff --git a/test/review-targeted.test.ts b/test/review-targeted.test.ts index 54f4574f7..d624c869c 100644 --- a/test/review-targeted.test.ts +++ b/test/review-targeted.test.ts @@ -189,8 +189,8 @@ function planRepo(f: Fixture) { */ const FULL_PANEL = ['specialist', 'red-team', 'adversarial-native', 'outside-voice']; const R51: Record = { - 'none-light': { depth: 'light', panel: false, dropped: ['specialist'] }, 'none-standard': { depth: 'light', panel: false, dropped: ['specialist', 'outside-voice'] }, 'none-docs': { depth: 'light', panel: false }, - 'none-tests': { depth: 'light', panel: false }, 'none-costume': { depth: 'light', panel: false, dropped: ['specialist'] }, 'none-gate-path': { depth: 'full', panel: true }, + 'none-light': { depth: 'light', panel: false }, 'none-standard': { depth: 'light', panel: false, dropped: ['outside-voice'] }, 'none-docs': { depth: 'light', panel: false }, + 'none-tests': { depth: 'light', panel: false }, 'none-costume': { depth: 'light', panel: false }, 'none-gate-path': { depth: 'full', panel: true }, 'secret-light': { depth: 'full', panel: true }, 'secret-path': { depth: 'full', panel: true }, 'secret-removed': { depth: 'full', panel: true }, 'gate-tagged': { depth: 'full', panel: true }, 'money-light': { depth: 'full', panel: true }, 'money-path': { depth: 'full', panel: true }, 'money-removed': { depth: 'full', panel: true }, 'money-standard': { depth: 'full', panel: true }, 'auth-path': { depth: 'full', panel: true }, 'money-fix-check': { depth: 'full', panel: false }, 'money-pass2': { depth: 'full', panel: false }, 'gate-repeat': { depth: 'full', panel: false }, @@ -204,8 +204,7 @@ describe('gstack-review-plan prints TARGETED and dispatches the targeted reader' // with no class; seam=bin/gstack-review-plan end to end; no_claim=that the reader finds the defect (the paid replay's question) // R51 changes the table: risk and gate fixtures plan full (every role main dispatched, plus the full panel), and the one // non-risky standard fixture drops only the outside voice; fails_when=a risk fixture loses a role main dispatched or misses - // the full panel, or a non-risky fixture loses a role the R51 table does not list. R65 (GSTA-258 build C): a non-risky code - // fixture runs one native adversarial reader carrying the checklists and the Red Team, so its batched specialist drops too + // the full panel, or a non-risky fixture loses a role the R51 table does not list test('the dispatch-superset no-loss check: risk and gate fixtures keep every role main dispatched plus the full panel; non-risky ones drop only what R51 lists', () => { expect(Object.keys(MAIN).sort()).toEqual(Object.keys(FIXTURES).sort()); expect(Object.keys(R51).sort()).toEqual(Object.keys(FIXTURES).sort()); diff --git a/test/ship-receipt-platform.test.ts b/test/ship-receipt-platform.test.ts index c84181b53..5a8122f74 100644 --- a/test/ship-receipt-platform.test.ts +++ b/test/ship-receipt-platform.test.ts @@ -41,7 +41,7 @@ describe('C6 receipt: review_depth platform, honest and bound to the exact head test('a docs diff under review.platform on with capy-ai[bot]\'s review at HEAD records platform, and the queue accepts it at that head only', () => { const { dir, head } = repo({ review: { platform: 'on' } }, { 'docs/howto.md': 'Run the installer.\n' }); const d = receiptReviewAtHead(dir, 'main', BIN, head).depth; - expect([d.depth, d.mode]).toEqual(['light', 'lean']); + expect([d.depth, d.mode]).toEqual(['light', 'panel']); const p = withGh(head, () => platformReceiptDepth(dir, 'main', d, head, '99')); expect(p.ok && [p.depth.depth, p.depth.mode, p.depth.platform_review]).toEqual(['platform', 'platform', { id: '99', head, reviewer: 'capy-ai[bot]' }]); const r = { gstack_version: '1.94.0.0', review_depth: (p as any).depth } as ShipReceipt;