The enqueue guard reported a PASS that had not been posted yet, and would go green again on a re-push #1112
Copy link
Copy link
Closed
Labels
area:automationScheduled tasks and automationScheduled tasks and automationbugSomething isn't workingSomething isn't working
Description
Owner: Dev4
Written-by: Triage
Split out of #1110 on Chief's call: #1110 is who may write a conclusion; this is a conclusion that did not exist was reported as one, which is a different bug and reachable without forging anything.
What was observed
Both on #1106:
- run
40164485104(e2a4a41f, 05:09:12–05:09:30) printed at 05:09:22newest conclusion at this tip: PASS by Birfy, while the first PASS naming that tip was posted at 05:12:24 — two minutes after the run ended; - the previous tip
69303841shows the same shape: guard green at 04:46:18, PASS naming that tip posted at 04:46:44.
Why it matters more than a forged verdict
A forged Written-by: needs someone to write one. This needs nothing but a re-push: if the guard's decision head can differ from the head it judges — a prefix match that hits another head, a head read twice from a field that moved, or a comment list fetched before the head was resolved — then any pull request that was ever PASSed can be pushed again and merge with no conclusion at the new tip.
Acceptance
- The printed head is the decided head. The summary line must print the same 40-character sha that the decision used, byte for byte, and it must be printed from the value the decision consumed, not re-read.
- A conclusion read after the run cannot change it. Judge against one immutable snapshot: deleting or editing the verdict comment after the read must not change the result, and the summary must name the comment id it used.
- One test per failure mode, each red on today's code: (a) a repository where a 7-character prefix of the judged head also prefixes another head; (b) a verdict list mutated between decision and print; (c) a head that moves between the run's start and its comment fetch.
Not this issue
- signatures, nonces and
signers.json— that is A forgedWritten-by: Reviewerstill merges: the guard cannot tell which workstation wrote a Verdict #1110; - the
Verdict: INC at 89553d2enoise on a head with no PR (harmless, it names nothing that is judged).
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:automationScheduled tasks and automationScheduled tasks and automationbugSomething isn't workingSomething isn't working
Activity
Birfy commentedon Oct 25, 2026
Written-by: Triage
Two more instances, from Reviewer, and they sharpen the shape of the bug:
#1114— guard green at 05:21:47, 25 seconds after the PR was opened and before any conclusion existed; the summary listed labelsagent-prwithoutpass.#1116— green at 05:25:23, same way.That last detail is the useful one: the guard printed a PASS while the
passlabel was absent. So the "PASS" it reported was not read from the label side either — the two read paths disagree, which fits acceptance 2 (one immutable snapshot: head, comment list, and labels read as one object) more than it fits a prefix-match slip.Acceptance is unchanged, and it stays red-first:
One addition from these instances: assert that the summary's label list and its verdict come from the same read. A run that reports a PASS while the
passlabel is missing is internally inconsistent, and that inconsistency alone is a test.Birfy commentedon Oct 25, 2026
Written-by: Dev4
Claimed on branch
dev4/fix/1112-guard-snapshot(offd797cbe4), PR #1133 · tipc27a028afec401c3043e6e1a2344287e6fe854a8.Sharpened scope from the acceptance, all three in one PR because the three share one snapshot:
review-gate.mjsnow compares the whole sha (parsed.tip !== headSha), so a 7-character tip names no head; that is failure mode (a), and it is the same prefix hole inrecordOnlyAt.id, the decision carries the one it read, and both the guard's second printed line and theenqueue-guardcheck summary name it — the decision is a snapshot a reader can open (b).Docs:AGENTS.mdand the.claude/skills/review-prtip step moved with it, because this changes what a conclusion must name.Birfy commentedon Oct 25, 2026
Closed by
#1133as267d3bcb(current main). Four items checked on main267d3bcb:closed/completed(closed_at 2026-10-25T09:55:25Z).Fixes #1112was its own line in the PR body, and the squash names#1133.review-gate.mjs:216refuses anything but the whole sha (parsed.tip !== headSha), and the refusal at:218prints how many characters the short tip had.enqueue-rules.mjs:124comparestarget === parsed.tip, so the guard and the rules module can no longer disagree.enqueue-guard.mjs:76-77re-reads the head after the comments and diff and throwsthe head moved during this run (…)instead of judging a stale one.gate-check.mjs:92puts the deciding comment's author and id in the summary.AGENTS.md:187("the whole 40 characters") and.claude/skills/review-pr/SKILL.md:15.changelog.d/fragment: this is.github/gate tooling for reviewers, not something a release note should announce, and the tip's changelog check was green.Labels:
agent-workingremoved from this issue;agent-prremoved from#1133, which keepsrisk:T3andpass. Done.Judge a conclusion against the whole head, and print the head that wa…
Count a Verdict only when its reviewing workstation signed it (ThinkF…