From ebabb5934679f92752cfd999ff8b4afe3bb38e5d Mon Sep 17 00:00:00 2001 From: garrytan Date: Sat, 31 Oct 2026 16:19:47 +0000 Subject: [PATCH] test: repin the two ship section briefs under no-loss replay GST-464 GST-464 brief-identity check: every reviewer brief lib/review-brief.ts cuts from ship/sections/review-army.md and adversarial.md stays byte-identical to the bodies the L and GST-257 replays admitted; only lead instructions outside the cut spans changed. The check is a test CI runs. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- changelog.d/gst-464-receipt-targeted-base.md | 1 + test/review-packets-removed.test.ts | 40 +++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/changelog.d/gst-464-receipt-targeted-base.md b/changelog.d/gst-464-receipt-targeted-base.md index 65aa74a5f..145394224 100644 --- a/changelog.d/gst-464-receipt-targeted-base.md +++ b/changelog.d/gst-464-receipt-targeted-base.md @@ -11,3 +11,4 @@ - `test/review-targeted.test.ts`: the generated sections' own commands carry the record from Step 11's verify to `gstack-ship-receipt write`, and ready's line reads `verdict=ok`. A run without the reader passes no flag, and Step 9 clears the file, even when `.git/info` is missing or the exclude file has no final newline. - `test/ship-preflight.test.ts`: a single-branch clone's preflight refreshes `origin/main`, and a 60-file diff against a 3-file or a 61-file PR stops at the pushed head. The stop quotes an unsafe base name and names the fork case. Negative controls: another head, a matching size, or a zero count goes on. - `test/review-plan-cli.test.ts`, `test/gate-diff-cli.test.ts`, `test/diff-scope.test.ts`: a local `main` behind, ahead of or diverged from `origin/main` yields to it, so an unpushed `test.skip` on local `main` is scanned. One next to an unrelated `origin/main` stays the base. A tag or fetched branch named `origin/main` never stands in, and a pinned sha and `HEAD` are never redirected. The review plan of a docs-only branch that merged the newer `origin/main` stays docs; before the fix, the gate scan counted origin's skipped test. +- `test/review-packets-removed.test.ts`: the two ship section pins move under replay GST-464, a brief-identity check. Every reviewer brief `lib/review-brief.ts` cuts from those sections must stay byte-identical to the no-loss-tested bodies; a changed brief fails it. diff --git a/test/review-packets-removed.test.ts b/test/review-packets-removed.test.ts index ab7ff8240..0d2be0c9b 100644 --- a/test/review-packets-removed.test.ts +++ b/test/review-packets-removed.test.ts @@ -8,7 +8,9 @@ import { describe, test, expect } from 'bun:test'; import { spawnSync } from 'child_process'; import { createHash } from 'crypto'; import * as fs from 'fs'; +import * as os from 'os'; import * as path from 'path'; +import { sectionTexts } from '../lib/review-brief'; const ROOT = path.join(import.meta.dir, '..'); const REMOVED = [ @@ -56,11 +58,12 @@ const NO_LOSS_REPLAYS = { 'G1': "GSTA-167 variant G Arm G1: live no-loss replay of both prose voices with part 1's prompts, 2 passes, normalizer v2, paired rule against pooled C' (parts 1 and 3; specialists at medium as in C and C2)", 'L': "GSTA-167 variant G Arm L: live no-loss replay of one batched reader (testing and security plus scoped checklists) and the Red Team on its findings, 2 passes, scored L∘G1 against G1 on the paired rule (part 2)", 'GST-257': "GST-257 PR 1 dispatch-superset no-loss check (test/review-targeted.test.ts): on every fixture diff the branch dispatches main's reviewers at 8be7f21 unchanged plus only the targeted reader, and over the last 100 non-release commits on main it lost none; adds the targeted role and brief to the G1 and L bytes, whose existing reviewer text is unchanged", + 'GST-464': "GST-464 brief-identity no-loss check (the brief-body test below): every reviewer brief lib/review-brief.ts cuts from ship/sections/review-army.md and adversarial.md is byte-identical to the bodies at the L and GST-257 pins; 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: 'b7ca291a1cc63750bfb9710a4c12fd971523b9598da2a32ef80fb258eb0eceab', replay: 'L' }, - 'ship/sections/adversarial.md': { sha: 'd776ee5bc6e0c13930f8f4de05bad2f1ed9e52bc9fc5df80ae3ba7680455b2bb', replay: 'GST-257' }, + 'ship/sections/review-army.md': { sha: 'ebda452b2cf8aeb34020c789d0507893c0f041027890bcbd8342e46d4b29f29a', replay: 'GST-464' }, + 'ship/sections/adversarial.md': { sha: 'b70577b6987869acd76cc3865f16eeeed743558c0ea09e5f9933d0d8aec8b91e', replay: 'GST-464' }, 'review/sections/review-army.md': { sha: '78329b64640260cd8831ca81a9189f3f8dcade9c406431631d3f2cd798cd3967', replay: '#3638' }, 'review/sections/adversarial.md': { sha: 'e6962fa25b78f362654f2954f79683e524d2ebd452d1494f8a2b099888e8e255', replay: 'G1' }, 'review/checklist.md': { sha: '607788afeb2c83f2dff1dccd58d212616ec72393d28cda2fa18690c1a83df699', replay: '#3638' }, @@ -80,7 +83,40 @@ const REVIEWER_BRIEFS: Record = { 'lib/review-depth.ts': { sha: '4542785a1e217d8dd054985978b42c055b9fd79082579cb7664f187f7066e48e', replay: 'GST-257' }, }; +/** + * The six reviewer brief bodies lib/review-brief.ts cuts out of the two ship sections, pinned at + * the bytes replays L and GST-257 admitted. A section repin under GST-464 is valid only while every + * body still hashes here: the section may change outside the cut spans, never inside them. + */ +const BRIEF_BODIES: Record = { + specialist: '1ebe47ddb3291a9e4fe90b5a260eb57e328144959549bc95c119b68d23652cb5', + framing: 'aad667b5ea4c6d23d930dafffa38178c04be388cd4364fb73759fc92af17bb63', + fixtures: '9517c86befe773e76acbf30a0642ed828e211b8ecb524325702072b26429ef16', + redTeam: '1cca7957d13f9da08434870290b92958f37750207047b92c9aa52a802da789e8', + native: 'ddb1041ec1e4dd373c3e4ab9017e49316e78c230055c1ef3db1bbba518b143a2', + outside: 'bd96406da28b03742384560425a333b6d6d7ed2be95bd2026c90e0039632cee2', +}; +const bodyHashes = (root: string) => Object.fromEntries(Object.entries(sectionTexts(root)).map(([k, v]) => [k, createHash('sha256').update(v).digest('hex')])); + describe('reviewer briefs stay at their no-loss-tested bytes (GSTA-167)', () => { + // Value: protects=the GST-464 repin of ship/sections/review-army.md and adversarial.md admits only lead-instruction + // edits: every brief a reviewer reads renders byte-identical to the no-loss-tested bodies; fails_when=an edit inside a + // cut span changes a brief while its section pin is moved; seam=sectionTexts, the function every render uses; + // no_claim=the checklists and the targeted brief, pinned by their own file hashes above + test('every reviewer brief cut from the ship sections is byte-identical to its pinned body', () => { + expect(bodyHashes(ROOT)).toEqual(BRIEF_BODIES); + const copy = fs.mkdtempSync(path.join(os.tmpdir(), 'brief-bodies-')); + try { + fs.mkdirSync(path.join(copy, 'ship', 'sections'), { recursive: true }); + for (const f of ['review-army.md', 'adversarial.md']) fs.copyFileSync(path.join(ROOT, 'ship', 'sections', f), path.join(copy, 'ship', 'sections', f)); + const army = path.join(copy, 'ship', 'sections', 'review-army.md'); + fs.writeFileSync(army, fs.readFileSync(army, 'utf8').replace('You are a specialist code reviewer.', 'You are a specialist code reviewer. Skim.')); + expect(bodyHashes(copy).specialist).not.toBe(BRIEF_BODIES.specialist); + } finally { + fs.rmSync(copy, { recursive: true, force: true }); + } + }); + test('every pinned reviewer brief, checklist and depth rule hashes to its pin', () => { const drift = Object.entries(REVIEWER_BRIEFS).filter(([file, pin]) => { const text = fs.readFileSync(path.join(ROOT, file), 'utf8').replace(/\r\n/g, '\n');