Development record
a batched merge-queue push makes github.sha the batch tip, so auto-release tags the wrong commit and release.yml's HEAD-caret-2-caret receipt derivation resolves an unrelated PR's commit whenever the release roll is not the last entry in its queue batch; the ancestry guard passes and the run wedges after the immutable tag, the iss-326 outcome by a route itd-93's pre-merge check cannot close because batch composition is decided by the queue
issueresolvedmajoriss-355
a batched merge-queue push makes github.sha the batch tip, so auto-release tags the wrong commit and release.yml's HEAD-caret-2-caret receipt derivation resolves an unrelated PR's commit whenever the release roll is not the last entry in its queue batch; the ancestry guard passes and the run wedges after the immutable tag, the iss-326 outcome by a route itd-93's pre-merge check cannot close because batch composition is decided by the queue
Evidence
- Merge queues here merge one commit per PR but advance main in batched pushes: the 2026-08-19 14:29-14:31 block landed five merge commits with auto-release push runs only for two of them (runs 202-204 by consecutive run_number), so
github.shaon a push event is the batch tip.auto-release.yml:139tagsgithub.sha;release.yml:225-229derives receipts fromHEAD^2^of the tagged commit;release.yml:233(git merge-base --is-ancestor) asserts ancestry only, which an unrelated batch-mate satisfies. A release roll merged as a non-final batch entry therefore tags the wrong commit and wedges exactly as iss-326 records for the missing-receipts cause — but by a route the itd-93 pre-merge receipts check cannot close, because batch composition is decided by the queue at merge time from whatever else is green (.abcd/work/rulesets/main-protection.json,max_entries_to_merge: 5). - The rehearsal (
release.yml:420-441) hardcodes the two-commit no-ff shape, so it can never surface this.main-review.json's squash/rebase merge methods are NOT an independent path (the queue's MERGE method governs). - Refuter verdict: CONFIRMED substantive (major). Recorded, not fixed: the fix belongs in a required-check workflow this environment cannot validate without a real Actions run (the iss-301/302 precedent). Proposed fix: (a) derive the content commit by locating the
.abcd/work/reviews/<sha>/receipts directory (walkHEAD^2^,HEAD^, then first-parent merges'^2^bounded), aborting loudly before any tag when none matches, mirrored into the rehearsal plus a batched-queue simulation; or (b) setmax_entries_to_merge: 1(keepmax_entries_to_build: 5), restoring the one-merge-per-push invariant the derivation assumes.
Frontmatter
| id | iss-355 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-355-a-batched-merge-queue-push-makes-github.md
open on GitHub ↗ · commit history ↗
mentions7
- dcn-26082000000000022026-08-20 — bughunt round 2 (branch bughunt-b/round-2): captured the round-2 records (iss-346..iss-357, with the two colliding ids re-minted as iss-358/iss-359), fixed
- dcn-26082800000000032026-08-28 — Release-pipeline gates: two decisions land together. (a) The
- iss-301external-review.yml computes its approver tally with 'gh api .../reviews --paginate --jq' where --jq runs per API page (not across the full set; --slurp is incompatible with --jq), and no per_page is set so pages are 30 — so the 'latest state per reviewer wins' reduction the comment promises fails once reviews span a page: a stale page-1 APPROVED survives a later page-2 CHANGES_REQUESTED, and a straddling double-approval is counted twice. Fail-open on a required security gate. Not fixed autonomously: required-check workflow that cannot be validated without running Actions
- iss-326The v0.6.0 tag is permanently unpublishable: its roll branch carried no receipts commit, the receipt gate fail-closed at release time, and the re-release path builds only from the tagged commit whose frozen tree can never contain receipts naming its own content commit. The tag stays as a record (anti-tag-move); v0.6.1 is the recovery
- iss-2608231226347380`auto-release.yml` runs `detect` -> `tag` -> `release`, and the semantic
- iss-2609251751298408commands/launch.md describes the release job's semantic receipt gate at a position it no longer holds. Four places — the Ship intro ('the tag is already created by then'), the release-day failure list ('The tag exists by then'), Semantic receipts ('release.yml derives the content commit as <merge>^2^') and 'Prove the gate before you merge' ('receipt_gate runs inside the release job, which is after the tag is created') — predate adr-52 and iss-355: release.yml runs the gate in its verify job, which the tag job needs, so on the auto-release path a refusal leaves no tag and the version free, and the content commit is derived from the receipts directory of the released tree (record-lint --derive-content-sha), not from merge ancestry. Only a hand-pushed tag exists before the gate. An operator reading the page believes a refusal consumes the version and reaches for a tag deletion the machinery no longer needs.
- itd-93abcd Scaffolds a Release Gate That Works on the First Try