fix(ship-queue): land --pin refuses a stale base with QUEUE_LAND_STALE_BASE (GST-1316)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-11-04 03:45:28 +00:00
1 parent 9e826b2994
commit e5bb3e20fc
8 files changed
+67 -16

No files matched your search

+5
View File
@@ -488,6 +488,11 @@ export const RELEASE_RESULT_CODES = {
summary: 'a check at the pin is red or pending, or a required check never ran there',
fix: 'wait for pending checks (gstack-pr-wait <n> --until green), fix a red one at a new head, or dispatch a missing required workflow on the branch; then rerun land',
},
QUEUE_LAND_STALE_BASE: {
anchor: 'queue-land-stale-base',
summary: 'the base branch moved since the pin\'s merge base, so the checks at the pin never ran on the tree that would land (GST-1316: a stale-base PR pushed a budgeted file past its cap on main)',
fix: 'merge the base into the branch, push, wait for the required checks at the new head, run /ship and gstack-ship-queue ready there, then land the new pin',
},
QUEUE_LAND_NOT_CLEAN: {
anchor: 'queue-land-not-clean',
summary: 'GitHub does not report the PR mergeable and clean (a conflict, a branch rule, or a state GitHub is still computing)',
+5 -3
View File
@@ -1,8 +1,9 @@
/**
* ship-queue-land-direct — B1's merge step, behind `gstack-ship-queue land --pr <n> --pin <sha>` under the base
* policy's merge: direct (docs/ship-policy.md#land-direct). The PR merges at its own green head: no train, no stamp
* commit, no base merge and no extra CI round. It refuses unless the /ship receipt and the READY note are bound to the
* pin, the pin is the PR's head, the diff leaves VERSION, the manifest version and the CHANGELOG at its merge base
* policy's merge: direct (docs/ship-policy.md#land-direct). The PR merges at its own green head: no train and no stamp
* commit. It refuses unless the /ship receipt and the READY note are bound to the pin, the pin is the PR's head, the
* pin's merge base is the base tip (so the checks at the pin ran on the tree that lands, GST-1316), the diff leaves
* VERSION, the manifest version and the CHANGELOG at its merge base
* (versions are set at release), every check at the pin is green with none pending and each required check present,
* and GitHub reports the PR mergeable and clean. Then gh pr merge --squash --match-head-commit <pin>, a check that the
* merge commit's tree is the pin merged onto that commit's parent, and a land note on refs/notes/gstack-trains. A rerun
@@ -132,6 +133,7 @@ export function landDirect(ctx: QueueContext, o: DirectOptions): StepOutput {
const passed = new Set([...ci.checks.filter(c => c.conclusion === 'success').map(c => c.name), ...ci.contexts.filter(c => c.state === 'success').map(c => c.name)]);
const missing = (policy.changelog_fragments?.required_checks ?? []).filter(n => !passed.has(n));
if (missing.length) return fail(lines, { code: 'QUEUE_LAND_CHECKS', message: `required check(s) ${missing.join(', ')} did not pass at the pin ${o.pin.slice(0, 12)}`, fix: `dispatch them on the branch (gh workflow run <workflow> --ref ${info.headRefName}) and rerun land once they pass` });
if (mergeBase !== tip) return fail(lines, { code: 'QUEUE_LAND_STALE_BASE', message: `origin/${ctx.base} moved to ${tip.slice(0, 12)} since the pin's merge base ${mergeBase.slice(0, 12)}, so the checks at the pin never ran on the tree that would land`, fix: `merge the base into the branch (git merge origin/${ctx.base}), push, wait for the required checks at the new head, run /ship and gstack-ship-queue ready --pr ${o.pr} there, then land the new pin` });
if (info.mergeable !== 'MERGEABLE' || info.mergeStateStatus !== 'CLEAN') return fail(lines, { code: 'QUEUE_LAND_NOT_CLEAN', message: `GitHub reports #${o.pr} mergeable=${info.mergeable ?? 'unknown'} mergeStateStatus=${info.mergeStateStatus ?? 'unknown'}`, fix: info.mergeable === 'CONFLICTING' ? `the lane merges origin/${ctx.base} into the branch, resolves, runs /ship and ready at the new head` : 'UNKNOWN means GitHub is still computing: rerun land in a minute; BLOCKED or BEHIND names the branch rule to meet first' });
if (o.dryRun) return { ok: true, run, lines: [...lines, `DRY-RUN: nothing merged (would run gh pr merge ${o.pr} --squash --match-head-commit ${o.pin})`] };