diff --git a/.gstack/ship-policy.json b/.gstack/ship-policy.json index bba289225..2e8281470 100644 --- a/.gstack/ship-policy.json +++ b/.gstack/ship-policy.json @@ -16,5 +16,12 @@ "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": 1, "screen_selection": "gstack-paid", "trial_reservation_usd": 1.5 }, "changelog_fragments": { "dir": "changelog.d", "required_checks": ["free-tests", "windows-smoke", "Check VERSION is not stale vs queue", "quality", "check-freshness", "actionlint"] }, "merge": "direct", - "min_gstack_version": "1.95.0.0" + "land": { + "merged_checks": [ + "bun run typecheck", + "bun run gen:skill-docs --host all && test -z \"$(git status --porcelain -- . ':(exclude)node_modules')\"", + "bun run scripts/test-free-shards.ts --files-from test/land-merged-checks.txt --wall-timeout 600" + ] + }, + "min_gstack_version": "1.96.0.0" } diff --git a/changelog.d/gsta258-g-land-merged-checks.md b/changelog.d/gsta258-g-land-merged-checks.md new file mode 100644 index 000000000..06bee7972 --- /dev/null +++ b/changelog.d/gsta258-g-land-merged-checks.md @@ -0,0 +1 @@ +- **gstack's own policy turns on `land.merged_checks`.** When main has moved past a PR's merge base, `land --pin` now runs typecheck, the generated-docs freshness check, and the size and budget guards plus the core of `test:quick` (through the strict free runner, so a test that exits early fails the check) on the PR merged onto main's tip, and lands only when all three pass. This catches the stale-base break from #3886, which pushed `ship/DRIVER.md` past its 12 KB cap on main. `min_gstack_version` moves to 1.96.0.0, the release that added the key. (GST-1332) diff --git a/docs/ship-policy.md b/docs/ship-policy.md index 5a54d736b..0a11024a5 100644 --- a/docs/ship-policy.md +++ b/docs/ship-policy.md @@ -2099,7 +2099,14 @@ the filesystem: a command can still write the checkout's `.git` (the worktree shares it) and the linked `node_modules`, so pass the flag only for a base policy and PRs you trust to run. A list such as typecheck plus the size ratchets takes about two minutes, against a main -merge, a CI round and a new receipt per PR. Without the key, land prints +merge, a CI round and a new receipt per PR. gstack's own policy runs +typecheck, the generated-docs freshness check, and the size and budget guards +plus the core files of `test:quick` through the strict free runner on +`test/land-merged-checks.txt` (`test/ship-driver.test.ts` holds the +`ship/DRIVER.md` cap #3886 broke). Raw `bun test` exits 0 when a test calls +`process.exit(0)` or a listed file is missing; the runner fails both. It +doesn't run `test:quick` itself, because its browser tests find no Chromium +under the throwaway `HOME`. Without the key, land prints `STALE_BASE: warn … no land.merged_checks configured` and merges the stale base untested, as it did before the key. Then land runs `gh pr merge --squash --match-head-commit ` (`--merge` under `history: merge-only`), diff --git a/test/gstack-ship-policy.test.ts b/test/gstack-ship-policy.test.ts index e290256a8..0cb523cf8 100644 --- a/test/gstack-ship-policy.test.ts +++ b/test/gstack-ship-policy.test.ts @@ -188,6 +188,17 @@ describe("gstack's own .gstack/ship-policy.json", () => { expect(raw.changelog_fragments.required_checks.filter((n: string) => !jobs.has(n))).toEqual([]); }); + // Value: protects=land.merged_checks on gstack itself (GST-1332): its tests run through the strict free runner on a committed list that keeps the ship/DRIVER.md cap guard (#3886), so an early exit or a missing file fails instead of passing; fails_when=a command goes back to raw bun test (which exits 0 when a test calls process.exit(0) and skips a missing file), test/ship-driver.test.ts leaves the list, or a listed file is gone; why_new=gstack's own policy gained the land key; seam=the policy file and test/land-merged-checks.txt; no_claim=that the commands pass on a merged tree (ship-queue-land-direct covers the engine) + test('land.merged_checks runs its tests through the strict runner on a list that exists and keeps the ship/DRIVER.md cap guard', () => { + const root = path.join(import.meta.dir, '..'); + const checks: string[] = JSON.parse(fs.readFileSync(path.join(root, '.gstack', 'ship-policy.json'), 'utf8')).land.merged_checks; + expect(checks.filter(c => /\bbun test\b/.test(c))).toEqual([]); + expect(checks).toContain('bun run scripts/test-free-shards.ts --files-from test/land-merged-checks.txt --wall-timeout 600'); + const files = fs.readFileSync(path.join(root, 'test', 'land-merged-checks.txt'), 'utf8').split('\n').filter(Boolean); + expect(files).toContain('test/ship-driver.test.ts'); + expect(files.filter(f => !fs.existsSync(path.join(root, f)))).toEqual([]); + }); + // Value: protects=the fill rule the coordinator relies on: a train dispatches at 5 READY PRs or one hour after the first READY, whichever comes first // (2026-10-26: 5 PRs / 3 hours left READY PRs waiting most of a day, so the fill went to 3 PRs / 1 hour; 2026-10-31: Garry's B1 raised the size back to 5, keeping the 1-hour fill); fails_when=the live block drifts back to a larger or slower fill; why_new=the values were unpinned; seam=the policy file; no_claim=fillDue's arithmetic (ship-queue-train-logic) test('fills a train at 5 READY PRs or one hour after the first READY, whichever comes first', () => { diff --git a/test/land-merged-checks.txt b/test/land-merged-checks.txt new file mode 100644 index 000000000..8bb1ef876 --- /dev/null +++ b/test/land-merged-checks.txt @@ -0,0 +1,16 @@ +test/ship-driver.test.ts +test/skill-md-size-guard.test.ts +test/skill-size-budget.test.ts +test/module-size-ratchet.test.ts +test/context-budget-ratchet.test.ts +test/catalog-budget.test.ts +test/claude-md-size.test.ts +test/parity-suite.test.ts +test/gstack-ship-policy.test.ts +test/strict-output.test.ts +test/gen-skill-docs.test.ts +test/skill-check-driver.test.ts +test/skill-ceo-section-ordering.test.ts +test/qa-functional-observer.test.ts +test/qa-checkpoint-evidence.test.ts +test/test-free-shards-capture.test.ts