From c58a14a893ef9cc971bff04134e2e880e32fb092 Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 20 Oct 2026 06:23:37 +0000 Subject: [PATCH] v1.91.164.0 feat(ship-policy): turn merge trains on for gstack (GSTA-49 go-live) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- .gstack/ship-policy.json | 4 +++- CHANGELOG.md | 10 ++++++++++ VERSION | 2 +- agents-digest/gstack-AGENTS.md | 2 +- docs/ship-policy.md | 9 +++++---- package.json | 2 +- test/gstack-ship-policy.test.ts | 10 ++++++++++ 7 files changed, 31 insertions(+), 8 deletions(-) diff --git a/.gstack/ship-policy.json b/.gstack/ship-policy.json index c73522088..cd1e67330 100644 --- a/.gstack/ship-policy.json +++ b/.gstack/ship-policy.json @@ -11,5 +11,7 @@ "preregistration_shas": [], "stamp_sensitive_gates": ["tests"], "pr_count": { "default_pr_count": 1, "split_requires_owner": true }, - "maintainers": ["garrytan"] + "maintainers": ["garrytan"], + "train": { "gate_workflow": "evals.yml", "free_workflow": "free-tests.yml", "full_gate_check": "gstack/full-gate", "record_artifact": "gstack-train-record", "max_size": 5, "fill_hours": 3, "screen_selection": "gstack-paid", "trial_reservation_usd": 1.5 }, + "min_gstack_version": "1.91.146.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index dbd6ef057..ca047b077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [1.91.164.0] - 2026-10-19 + +**Merge trains are on for gstack: `.gstack/ship-policy.json` now carries the `train` block.** READY PRs merge in trains of up to 5 (or once the oldest READY is 3 hours old) behind one full gate on the train head, instead of one full gate per PR. Plan GSTA-49, go-live after lanes T1 and T1b. Garry approved going live once one dry-run train passed. + +### Changed +- **`.gstack/ship-policy.json` declares `train`** (`gate_workflow` `evals.yml`, `free_workflow` `free-tests.yml`, `full_gate_check` `gstack/full-gate`, `record_artifact` `gstack-train-record`, `max_size` 5, `fill_hours` 3, `screen_selection` `gstack-paid`, `trial_reservation_usd` 1.5) and `min_gstack_version` 1.91.146.0, which `gstack-ship-policy validate` requires alongside `train`. From this merge on, a PR push routes to `pr-summary` instead of a paid PR gate, `ready` records the screen choice, `train build` dispatches the full gate, `verify --pr --train ` is the merge check, and the main backstop gates any tree no green train covered. +- **Dry-run evidence.** Train `gate.train-24672a1f5b96-b67778cdbbd3-58805a` merged two unstamped scratch docs PRs (#3313 and #3314, closed unmerged) onto a scratch base: main 85b0f531f plus this policy block plus one scratch-only `evals.yml` line that read the policy from that base. A `workflow_dispatch` reads it from the default branch, which had no `train` block yet. The real `train build` refused with `QUEUE_TRAIN_WAITING` until the 3-hour fill passed, then ran Free Tests green (run 39377354548) and dispatched one full gate (run 39377781282: `VERDICT GREEN`, rule 108/108, judge 24/24, behavior 0/0, `gstack/full-gate` success on the train head). `verify --pr 3313` printed `MERGE_CONDITION: sha=33fb98512123…`. Not exercised: the default-branch policy read itself (the scratch line replaced it), PR-push routing to `pr-summary`, a stamp at merge, a screen, a bisect and the main backstop. +- **`docs/ship-policy.md`** now says gstack's own policy carries the block, replacing "does not carry it yet". +- **`test/gstack-ship-policy.test.ts`** validates gstack's own `.gstack/ship-policy.json` and checks that its train workflows exist, so the policy file has a test lane. + ## [1.91.163.0] - 2026-10-19 **A gate red on one test now reruns that test once instead of the whole gate, and a pass is green with a flaky note.** Garry, 2026-10-19 (CREW rule 16): "you can just run the failed test again and note that it is flaky... You don't have to do the whole test pass again for one failed test." Plan GSTA-49, lane F1; the #3308 reuse miss is GSTA-53. diff --git a/VERSION b/VERSION index c21a1c317..e93777d2e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.91.163.0 +1.91.164.0 diff --git a/agents-digest/gstack-AGENTS.md b/agents-digest/gstack-AGENTS.md index 3d318789a..2d905e5c2 100644 --- a/agents-digest/gstack-AGENTS.md +++ b/agents-digest/gstack-AGENTS.md @@ -1,4 +1,4 @@ -# gstack digest v1.91.163.0 — regenerate/re-copy after upgrading gstack +# gstack digest v1.91.164.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, diff --git a/docs/ship-policy.md b/docs/ship-policy.md index 4a8df6e33..53aff4126 100644 --- a/docs/ship-policy.md +++ b/docs/ship-policy.md @@ -281,10 +281,11 @@ warning on stderr). A block for gstack itself would read: "min_gstack_version": "" ``` -gstack's own policy does not carry it yet: trains go live for gstack only -after the workflow side (the train-nonce record, the `cases` and -`prefix_shas` inputs, the dispatched screen) has merged and one dry-run train -of two already-merged heads has passed. `gate_workflow` is dispatched with +gstack's own policy carries this block. It went live after the workflow side +(the train-nonce record, the `cases` and `prefix_shas` inputs, the dispatched +screen) merged and one dry-run train passed: two unstamped scratch docs PRs on +a scratch base whose `route` step read the policy from that base instead of +the default branch. `gate_workflow` is dispatched with `evals_all`, `expected_sha` and `nonce` (bisects add `cases` and `prefix_shas`, screens add `screen_pr`, `screen_merge_sha` and `screen_policy`). That run publishes `full_gate_check` as a check run on the diff --git a/package.json b/package.json index ac986a554..9331fee0a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gstack", - "version": "1.91.163", + "version": "1.91.164", "description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.", "license": "MIT", "type": "module", diff --git a/test/gstack-ship-policy.test.ts b/test/gstack-ship-policy.test.ts index a68f2ca76..d7a4cf269 100644 --- a/test/gstack-ship-policy.test.ts +++ b/test/gstack-ship-policy.test.ts @@ -64,6 +64,16 @@ describe('validatePolicy (schema + containment, pure)', () => { }); }); +describe("gstack's own .gstack/ship-policy.json", () => { + test('validates and declares the merge-train block against workflows that exist', () => { + const root = path.join(import.meta.dir, '..'); + const raw = JSON.parse(fs.readFileSync(path.join(import.meta.dir, '..', '.gstack', 'ship-policy.json'), 'utf8')); + expect(validatePolicy(raw)).toEqual([]); + expect(raw.train).toMatchObject({ gate_workflow: 'evals.yml', free_workflow: 'free-tests.yml', full_gate_check: 'gstack/full-gate', record_artifact: 'gstack-train-record' }); + for (const wf of [raw.train.gate_workflow, raw.train.free_workflow]) expect(fs.existsSync(path.join(root, '.github/workflows', wf))).toBe(true); + }); +}); + describe('gstack-ship-policy (bin)', () => { test('--help prints the exit table; no command is usage (2)', () => { const repo = fixture();