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>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-20 06:23:37 +00:00
1 parent 95152e3c4c
commit c58a14a893
7 files changed
+31 -8

No files matched your search

+3 -1
View File
@@ -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"
}
+10
View File
@@ -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 <n> --train <nonce>` 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.
+1 -1
View File
@@ -1 +1 @@
1.91.163.0
1.91.164.0
+1 -1
View File
@@ -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,
+5 -4
View File
@@ -281,10 +281,11 @@ warning on stderr). A block for gstack itself would read:
"min_gstack_version": "<the version that introduced train>"
```
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
+1 -1
View File
@@ -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",
+10
View File
@@ -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();