From 2bb3f5885a1d025db97c66f36b5cf5cf8a5f880b Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 27 Oct 2026 08:51:42 +0000 Subject: [PATCH] test(ship-policy): read origin/main's CHANGELOG with a 64 MiB maxBuffer; say plainly that a new-key PR cannot clear the train until #3512 Pass-2 Red Team: CHANGELOG.md is past spawnSync's default 1 MiB maxBuffer, so both CHANGELOG-reading tests dropped origin/main's file silently and the incident shape passed at the pin. With the buffer the released-key test sees main's headings on a full clone. The comment and CHANGELOG now state the practical consequence of the receipt limit. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- CHANGELOG.md | 2 +- lib/ship-policy.ts | 3 ++- test/gstack-ship-policy.test.ts | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02bd216aa..d02ced696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## [Unreleased] ### Changed -- **Policy key versions are stamped, not hand-moved** (`lib/ship-policy.ts`, `.gstack/ship-policy.json`): `DOCS_CAPS_KEY_VERSION` is 1.91.270.0, the release that shipped `docs.caps` (#3487 was written naming 1.91.258.0, which main stamped for another PR; a slot call never permits an edit beyond the four stamp files, so the constant could not move at the slot). gstack's own policy now lists `lib/ship-policy.ts` under `stamp_paths` with the `_KEY_VERSION = '{version}'` template in `stamp_patterns`: a new key's constant is written as a tentative future version clear of main's pace (the next minor) and `gstack-restamp`'s stamp_paths stage moves it to the allocated version at the slot stamp, reading the base branch's policy, so the stage is live for the PRs after this one; the tree receipt's stamp-only check still accepts that move only when the tentative value equals the base version at the slot (#3512). `test/gstack-ship-policy.test.ts` adds the check the incident lacked: a released key's version must be a CHANGELOG heading whose entry names the key, so a tentative value no stamp moved fails (keys from before `review_scope` are exempt). +- **Policy key versions are stamped, not hand-moved** (`lib/ship-policy.ts`, `.gstack/ship-policy.json`): `DOCS_CAPS_KEY_VERSION` is 1.91.270.0, the release that shipped `docs.caps` (#3487 was written naming 1.91.258.0, which main stamped for another PR; a slot call never permits an edit beyond the four stamp files, so the constant could not move at the slot). gstack's own policy now lists `lib/ship-policy.ts` under `stamp_paths` with the `_KEY_VERSION = '{version}'` template in `stamp_patterns`: a new key's constant is written as a tentative future version clear of main's pace (the next minor) and `gstack-restamp`'s stamp_paths stage moves it to the allocated version at the slot stamp, reading the base branch's policy, so the stage is live for the PRs after this one; the tree receipt's stamp-only check still accepts that move only when the tentative value equals the base version at the slot, so a PR that adds a key cannot clear the train until #3512 lands. `test/gstack-ship-policy.test.ts` adds the check the incident lacked: a released key's version must be a CHANGELOG heading whose entry names the key, so a tentative value no stamp moved fails (keys from before `review_scope` are exempt). ## [1.91.270.0] - 2026-10-27 diff --git a/lib/ship-policy.ts b/lib/ship-policy.ts index 5b1f0b803..8575cbdbd 100644 --- a/lib/ship-policy.ts +++ b/lib/ship-policy.ts @@ -170,7 +170,8 @@ export const TRAIN_KEYS_VERSION = '1.91.146.0'; // an untouched released constant stays, and a branch-added line naming the base's own version is re-read as a // stale stamp and moves too, so never rewrite a released constant's line. Until the tree receipt accepts a // stamp-form move whose old value is not the base version (#3512), the train's stamp-only check passes such a -// move only when the tentative value equals the base version at the slot. test/gstack-ship-policy.test.ts holds +// move only when the tentative value equals the base version at the slot, which a next-minor value never does: +// a PR that adds a key cannot clear the train until #3512 lands. test/gstack-ship-policy.test.ts holds // an unreleased constant newer than every CHANGELOG heading and a released one on a heading whose entry names // its key, so a value a stamp did not move fails there. export const REVIEW_SCOPE_KEY_VERSION = '1.91.256.0'; diff --git a/test/gstack-ship-policy.test.ts b/test/gstack-ship-policy.test.ts index 4b4b5b49e..983491aa5 100644 --- a/test/gstack-ship-policy.test.ts +++ b/test/gstack-ship-policy.test.ts @@ -76,11 +76,12 @@ describe('validatePolicy (schema + containment, pure)', () => { describe('policy key versions against the CHANGELOG', () => { test('an unreleased key names a release newer than every heading the checkout can see', () => { - // The base branch's CHANGELOG counts when the checkout has it (a depth-1 CI clone does not): a stamp-at-merge + // The base branch's CHANGELOG counts when the checkout has it (a depth-1 CI clone does not; the file is past 1 MiB, + // so spawnSync needs the explicit maxBuffer or it drops the read silently): a stamp-at-merge // branch carries main's headings only after the slot merge, and a release stamped under another number // leaves the constant naming a release that never read the key, which the stamper moves by hand. const root = path.join(import.meta.dir, '..'); - const base = spawnSync('git', ['show', 'origin/main:CHANGELOG.md'], { cwd: root, encoding: 'utf8', timeout: 30_000 }); + const base = spawnSync('git', ['show', 'origin/main:CHANGELOG.md'], { cwd: root, encoding: 'utf8', timeout: 30_000, maxBuffer: 64 * 1024 * 1024 }); const text = fs.readFileSync(path.join(root, 'CHANGELOG.md'), 'utf8') + (base.status === 0 ? base.stdout : ''); const parse = (v: string) => v.split('.').map(Number); const cmp = (a: number[], b: number[]) => a[0]! - b[0]! || a[1]! - b[1]! || a[2]! - b[2]! || a[3]! - b[3]!; @@ -98,7 +99,7 @@ describe('policy key versions against the CHANGELOG', () => { // a heading must be the one whose entry names the key (`\`` at a word boundary in the section body, so `\`docs/` paths do not count); lib/ship-policy.ts sits in // stamp_paths so the slot stamp moves a tentative value there itself. Keys from before the convention are exempt. const root = path.join(import.meta.dir, '..'); - const base = spawnSync('git', ['show', 'origin/main:CHANGELOG.md'], { cwd: root, encoding: 'utf8', timeout: 30_000 }); + const base = spawnSync('git', ['show', 'origin/main:CHANGELOG.md'], { cwd: root, encoding: 'utf8', timeout: 30_000, maxBuffer: 64 * 1024 * 1024 }); const text = fs.readFileSync(path.join(root, 'CHANGELOG.md'), 'utf8') + (base.status === 0 ? base.stdout : ''); const sections = new Map(); for (const part of text.split(/^(?=## \[)/m)) {