mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 15:04:37 +01:00
v1.91.275.0 fix(ship-policy): DOCS_CAPS_KEY_VERSION is 1.91.270.0; key versions move through stamp_paths at the slot stamp (#3511)
* fix(ship-policy): DOCS_CAPS_KEY_VERSION is 1.91.270.0; key versions move through stamp_paths at the slot stamp #3487 was written naming 1.91.258.0 for docs.caps and main stamped that number for another PR first; the slot call cannot edit anything beyond the four stamp files, so the constant shipped stale. lib/ship-policy.ts now sits in .gstack/ship-policy.json stamp_paths with the _KEY_VERSION = '{version}' template, so a new key written as a tentative future version is moved by gstack-restamp's stamp_paths stage at the slot (read from the base policy, so live from the next PR on). test/gstack-ship-policy.test.ts gains the check the incident lacked: a released key's version must be a heading whose entry names the key. Dry-run proof: a tentative 1.91.272.0 on a branch line moved to the allocated 1.91.271.0 with --policy-from head. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(ship-policy): tentative key versions are clear of main's pace; the receipt accepts the move only at the base version until #3512 Pass-1 review notes: the next patch above the fork point is the number main takes first, so the convention names the next minor; and the tree receipt's stamp-only check (lib/tree-receipt.ts stampOnlyHunks, with stampOnlyText and stampFreeTree) rewrites only base VERSION -> new VERSION, so a tentative move passes the train only when the tentative value equals the base version at the slot. Filed as #3512 (outside this diff, deferred under the frozen scope). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * 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> * docs(ship-policy): gstack's own policy excerpt names lib/ship-policy.ts under stamp_paths Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
d87eec950a
commit
f8d0ab0339
8 files changed
+52
-11
No files matched your search
@@ -4,7 +4,8 @@
|
||||
"queue_mode": "stamp-at-merge",
|
||||
"release_tool": "bun scripts/gen-agents-digest.ts",
|
||||
"release_outputs": ["agents-digest/gstack-AGENTS.md"],
|
||||
"stamp_paths": [],
|
||||
"stamp_paths": ["lib/ship-policy.ts"],
|
||||
"stamp_patterns": ["_KEY_VERSION = '{version}'"],
|
||||
"changelog": "CHANGELOG.md",
|
||||
"mirror": null,
|
||||
"history": "squash-ok",
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## [1.91.275.0] - 2026-10-27
|
||||
|
||||
### 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, 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.274.0] - 2026-10-27
|
||||
|
||||
**/design-html and /design-review now run the design lint, and its copy tier reports every content and ship-floor tell at its line (hallmark phase 2, GSTA-92; Garry 2026-10-26 "Do it", option a).** The two design skills still called `gstack-design-craft`, which counted tells without positions and kept accepted exceptions in finalized.json. The lint already carried the same ids, so the skills now call it, the copy tier has its own patterns with line numbers, and a page answers a tell with a waiver comment beside it instead of a side record.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# gstack digest v1.91.274.0 — regenerate/re-copy after upgrading gstack
|
||||
# gstack digest v1.91.275.0 — regenerate/re-copy after upgrading gstack
|
||||
|
||||
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||
for agent hosts without a full skill install. The full skills add workflows,
|
||||
|
||||
+2
-1
@@ -85,7 +85,8 @@ hard-code:
|
||||
"queue_mode": "stamp-at-merge",
|
||||
"release_tool": "bun scripts/gen-agents-digest.ts",
|
||||
"release_outputs": ["agents-digest/gstack-AGENTS.md"],
|
||||
"stamp_paths": [],
|
||||
"stamp_paths": ["lib/ship-policy.ts"],
|
||||
"stamp_patterns": ["_KEY_VERSION = '{version}'"],
|
||||
"changelog": "CHANGELOG.md",
|
||||
"history": "squash-ok",
|
||||
"stamp_sensitive_gates": ["tests"]
|
||||
|
||||
+14
-4
@@ -161,11 +161,21 @@ export const SHIP_QUEUE_KEYS_VERSION = '1.91.108.0';
|
||||
export const TRAIN_KEYS_VERSION = '1.91.146.0';
|
||||
// Each constant is the release that first reads its key: a policy declaring the key must name at least
|
||||
// that version as min_gstack_version, so an older gstack refuses the policy by name instead of failing on
|
||||
// an unknown key. review_scope shipped in 1.91.256.0 (#3481). docs.caps is unreleased: its constant names the
|
||||
// release expected to stamp it (test/gstack-ship-policy.test.ts holds it newer than every CHANGELOG heading the
|
||||
// checkout sees); whoever stamps that release under another number moves the constant to the stamped version.
|
||||
// an unknown key. review_scope shipped in 1.91.256.0 (#3481), docs.caps in 1.91.270.0 (#3487). A new key's
|
||||
// constant is written as a tentative future version clear of main's pace (the next minor, never the next patch,
|
||||
// which main takes first and the released-key test below then reds at the pin), in this exact
|
||||
// `_KEY_VERSION = '<version>'` form: this file is in .gstack/ship-policy.json stamp_paths with that form as a
|
||||
// stamp_patterns template, so the slot stamp's gstack-restamp stamp_paths stage moves the tentative value to the
|
||||
// allocated version. The stage reads lines the branch added: a tentative value newer than the fork version moves,
|
||||
// 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, 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';
|
||||
export const DOCS_CAPS_KEY_VERSION = '1.91.258.0';
|
||||
export const DOCS_CAPS_KEY_VERSION = '1.91.270.0';
|
||||
export const POLICY_KEY_INTRODUCED: Record<PolicyKey, string> = {
|
||||
bump: '1.91.73.0', queue_mode: '1.91.73.0', release_tool: '1.91.73.0', release_outputs: '1.91.73.0', stamp_paths: '1.91.73.0',
|
||||
changelog: '1.91.73.0', mirror: '1.91.73.0', history: '1.91.73.0', preregistration_shas: '1.91.73.0', stamp_sensitive_gates: '1.91.73.0',
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gstack",
|
||||
"version": "1.91.274",
|
||||
"version": "1.91.275",
|
||||
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
||||
@@ -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]!;
|
||||
@@ -91,6 +92,29 @@ describe('policy key versions against the CHANGELOG', () => {
|
||||
expect(cmp(parse(version), newest), `${key} is introduced in ${version}, which has no CHANGELOG heading and is not newer than the newest heading ${newest.join('.')}; an unreleased key names the release that will stamp it`).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
test('a released key names the heading whose entry introduces it, so a tentative version no stamp moved fails', () => {
|
||||
// #3487 was written naming the next version and main stamped that number for another PR first, so the constant
|
||||
// pointed at a release that never read `docs` while the heading existed and the test above skipped it. A key version that is
|
||||
// a heading must be the one whose entry names the key (`\`<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, maxBuffer: 64 * 1024 * 1024 });
|
||||
const text = fs.readFileSync(path.join(root, 'CHANGELOG.md'), 'utf8') + (base.status === 0 ? base.stdout : '');
|
||||
const sections = new Map<string, string>();
|
||||
for (const part of text.split(/^(?=## \[)/m)) {
|
||||
const heading = /^## \[(\d+\.\d+\.\d+\.\d+)\]/.exec(part);
|
||||
if (heading && !sections.has(heading[1]!)) sections.set(heading[1]!, part);
|
||||
}
|
||||
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]!;
|
||||
for (const [key, version] of Object.entries(POLICY_KEY_INTRODUCED)) {
|
||||
if (cmp(parse(version), parse(REVIEW_SCOPE_KEY_VERSION)) < 0) continue;
|
||||
const section = sections.get(version);
|
||||
if (section === undefined) continue;
|
||||
expect(new RegExp('`' + key + '(?![A-Za-z0-9_/-])').test(section), `${key} is introduced in ${version}, but that release's CHANGELOG entry never names \`${key}: the constant still holds the tentative version the branch was written with; the slot stamp moves it through stamp_paths, or move it to the release that shipped the key`).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("gstack's own .gstack/ship-policy.json", () => {
|
||||
|
||||
Reference in new issue
Block a user