You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding(check-governed-merges): the merge queue's build-to-land skew can put a governed landing BELOW a round's --since boundary, where it is silently absent from the audit list #12633
Turned up while measuring #11996 (whether the audit sees a governed-surface PR that lands through a merge-queue batch). That question measured YES — every batch topology lists the governed file. This is a different, adjacent property: not the batch topology, but the sweep's window. Filed separately rather than fixed on #11996, whose disposition rule fires only on a blind-to-the-topology verdict.
The mechanism, measured
GitHub's merge queue writes a queue entry's commit when the batch is built, and main receives it when the batch lands. Those are not the same moment. Measured on this repo, on two governed PRs from one batch:
Consequence: a commit can sit above another on main's first-parent chain (so it landed later) while carrying an earlier committer date. That is not hypothetical — over the last 3612 first-parent pairs on main:
inversions: 10 of 3612 pairs; max 874 s; median 539 s
max skew as a fraction of a 24h window: 1.012%
git merge-base --is-ancestor 98eec7e248 01a7337fc0 exits 0, so the ancestry direction is not an inference.
The measured miss, on real history
mainlineCommits() windows with git log --first-parent --since=DATE, which cuts on committer date. Set the boundary to the parent's date — exactly what --since-ref ROUND_TIP yields, and what "since the last round" yields whenever the last round ended between the two:
Same tree, same landed set. The only difference is a boundary ten minutes earlier. A governed ADR landing that reached main after the boundary is absent from the first list, and nothing in the output marks the gap — no warning, no incomplete classification on that account. In the degenerate case where it is the only governed landing in the window, the sweep prints a clean window and exits 0 = "swept COMPLETELY", which is precisely the reading #4690 and #9902 say must never be producible.
Reproduced as a constructed signature too (queue chain whose governed entry is dated 874 s — the measured maximum — before its own first parent):
QS-7 build-land skew, --since at the previous round tip
BLIND — governed file absent from the list [exit 0, 0 entries, 0 mainline commits scanned]
QS-7c same tree, --since backed off by the measured 874 s max skew
SEES — listed, attributed to the governed PR [exit 2, 1 entry, 2 mainline commits scanned]
What is NOT broken
The hole opens only when the window start falls between the governed commit's committer date and its landing time. The documented default invocation is safe by a wide margin: the largest measured skew is 874 s = 1.012% of a --since 24h window, and consecutive default rounds overlap by nearly a full day. The exposure is the knife-edge boundary — --since-ref ROUND_TIP, or an exact previous-round timestamp, which is what "实跑 --since 上轮" invites.
Also worth stating plainly: this is queue-specific. A direct merge writes its commit at merge time, so it produces no skew and no inversion.
Options (not taken here; this needs a ruling on windowing semantics)
A. Widen by a stated margin. Subtract a skew budget from any operator-supplied --since (and from --since-ref), and say so in the report line. Cheap, mechanical; costs some re-listing of already-recognised entries at each boundary, which the audit tolerates by design (a re-listed entry is recognised again; a dropped one is never seen).
B. Window on landing order, not on dates. Take --since-ref topologically: enumerate REF..origin/main instead of --since on the ref's date. That is exactly right for the round-to-round question and immune to any date skew, but it needs the previous round's ref to be recorded, which today it is not.
D. Accept and document. State in the header that --since is a date window, not a landing window, and pin the default-invocation margin.
Recommendation: B for --since-ref, A for --since — B is the only one that makes the round-to-round reading exact, and A covers the operator who passes a bare timestamp. Both keep the audit's report-only posture and neither weakens any judgment.
Measured on tree 0043c9224 (plus 4be2e880e, which main fast-forwarded to mid-measurement). Gate baseline is untouched: node scripts/pm/check-governed-merges.mjs --self-test prints "157 assertions" and the lock's VERDICT line reads command-exit 0.
Back-link: #11996 (the measurement this came out of), #11831 (the ruling that chartered it).
Turned up while measuring #11996 (whether the audit sees a governed-surface PR that lands through a merge-queue batch). That question measured YES — every batch topology lists the governed file. This is a different, adjacent property: not the batch topology, but the sweep's window. Filed separately rather than fixed on #11996, whose disposition rule fires only on a blind-to-the-topology verdict.
The mechanism, measured
GitHub's merge queue writes a queue entry's commit when the batch is built, and
mainreceives it when the batch lands. Those are not the same moment. Measured on this repo, on two governed PRs from one batch:merged_at.claude/agents/os-dev.md+ 2 references)04600d939@ 2026-08-26T05:00:27Z.claude/skills/pm-dispatch/SKILL.md+ 1 reference)ade757fb3@ 2026-08-26T05:00:49ZConsequence: a commit can sit above another on
main's first-parent chain (so it landed later) while carrying an earlier committer date. That is not hypothetical — over the last 3612 first-parent pairs onmain:Two of those ten inverted commits are governed:
01a7337fc0—docs/adr/0029-kernel-object-ownership-and-platform-objects-decomposition.md, PR fix(objectql): a tenant's rename beats a package extension's scalar in the object fold (#8460) #8620, committer date 2026-08-14T05:44:53Z, but a descendant of98eec7e248(dated 2026-08-14T05:55:02Z), so it landed 609 s of clock LATER while dated EARLIER.87e2faa06c— five.claude/skills/pm-dispatch/references/**files, PR docs(pm-dispatch): references platform-facts sweep — seven measured reading rules #8490, 57 s inverted against its own first parentccd46760b4.git merge-base --is-ancestor 98eec7e248 01a7337fc0exits 0, so the ancestry direction is not an inference.The measured miss, on real history
mainlineCommits()windows withgit log --first-parent --since=DATE, which cuts on committer date. Set the boundary to the parent's date — exactly what--since-ref ROUND_TIPyields, and what "since the last round" yields whenever the last round ended between the two:Same tree, same landed set. The only difference is a boundary ten minutes earlier. A governed ADR landing that reached
mainafter the boundary is absent from the first list, and nothing in the output marks the gap — no warning, no incomplete classification on that account. In the degenerate case where it is the only governed landing in the window, the sweep prints a clean window and exits0= "swept COMPLETELY", which is precisely the reading #4690 and #9902 say must never be producible.Reproduced as a constructed signature too (queue chain whose governed entry is dated 874 s — the measured maximum — before its own first parent):
What is NOT broken
The hole opens only when the window start falls between the governed commit's committer date and its landing time. The documented default invocation is safe by a wide margin: the largest measured skew is 874 s = 1.012% of a
--since 24hwindow, and consecutive default rounds overlap by nearly a full day. The exposure is the knife-edge boundary —--since-ref ROUND_TIP, or an exact previous-round timestamp, which is what "实跑--since 上轮" invites.Also worth stating plainly: this is queue-specific. A direct merge writes its commit at merge time, so it produces no skew and no inversion.
Options (not taken here; this needs a ruling on windowing semantics)
--since(and from--since-ref), and say so in the report line. Cheap, mechanical; costs some re-listing of already-recognised entries at each boundary, which the audit tolerates by design (a re-listed entry is recognised again; a dropped one is never seen).--since-reftopologically: enumerateREF..origin/maininstead of--sinceon the ref's date. That is exactly right for the round-to-round question and immune to any date skew, but it needs the previous round's ref to be recorded, which today it is not.--sinceis a date window, not a landing window, and pin the default-invocation margin.Recommendation: B for
--since-ref, A for--since— B is the only one that makes the round-to-round reading exact, and A covers the operator who passes a bare timestamp. Both keep the audit's report-only posture and neither weakens any judgment.Measured on tree
0043c9224(plus4be2e880e, whichmainfast-forwarded to mid-measurement). Gate baseline is untouched:node scripts/pm/check-governed-merges.mjs --self-testprints "157 assertions" and the lock's VERDICT line readscommand-exit 0.Back-link: #11996 (the measurement this came out of), #11831 (the ruling that chartered it).
Generated by Claude Code