Repository navigation
Description
Summary
The merge gate never looks at approvals. Six pull requests merged on 2026-10-22 with zero reviews of any kind — #675 (5aeec754), #676 (ef340354), #682 (a3cbbcdc), #684 (75ebb1a1), #685 (a8a8f21a), #686 (8580b011) — each with a merge_group run that passed and the queue then merged it. main is b5de55d0. So the rule in AGENTS.md, "T2/T3 need a maintainer approve at the tip plus a PASS", is currently enforced by nobody: only the PASS half is in the gate.
Where it is missing
judge() in .github/scripts/merge-gate.mjs:95 takes { labels, files, head, author, reports, policy, expectedHead, mode } and its reasons can only come from hold, a head mismatch, and decide()'s verdict (PASS / INC). There is no approval input and no branch that would use one. .github/workflows/merge-gate.yml already resolves the pull request from the queue branch and hands the script its labels, files, head, author and comment bodies, so the data source exists; only the field and the rule are missing.
Design (decided)
judge()gains anapprovalsinput, injected by the workflow exactly likelabelsandreports—judge()stays pure and testable, with no HTTP inside it. Shape:[{ login, state, commit_id, type, permission }](typeis the API'suser.type;permissionis the account's repository permission).- The rule. When
tierisT2orT3and the entry is notexempt, at least one approval must satisfy all of:state === 'APPROVED';commit_idstarts with the judged head (an approval at an older tip does not count — the same rule as PASS at the tip);type !== 'Bot';permissioninadmin/write/maintain;logindifferent from the pull request author (Birfycannot approve a PR it authored). Otherwise push`${tier} entry has no approved review`ontoreasons— for exampleT3 entry has no approved review. - Unchanged:
T0/T1and the mechanical-release exemption skip both the PASS and the approval (the existingexemptpath — a refusal here would stop every routine fix and every release, Read the verdict from its first line, and test the merge gate through its own entry point #640);mode: 'report'still never fails a check, so only the queue entry refuses. .github/workflows/merge-gate.yml: fetchGET /pulls/{n}/reviews?per_page=100, keep each review'suser.login,state,commit_idanduser.type, and resolvepermissionfromGET /repos/{o}/{r}/collaborators/{login}/permissionfor the candidates only. Dismissed approvals come back asDISMISSEDand must not count.
Acceptance
- T3 file set, no approval →
ok: falseandreasonscontainsT3 entry has no approved review. - T3 + one
APPROVEDreview at the judged head from awrite/adminnon-author account →ok: true. - T0 file set, no approval →
ok: true(exempt path unchanged). - Guard cases:
APPROVEDat an older commit → refused;APPROVEDfrom the pull request author, or from an account whosetypeisBot→ refused. - Red form: remove the approval branch from
judge()→ the first case goes green and its test is red; nothing else changes colour. node --test .github/scripts/merge-gate.test.mjsgreen at the tip (fixtures, no network).
State the consequence in the PR body
Today there are zero APPROVED reviews in this repository, and not by accident: every PR is authored by the shared Birfy account and GitHub refuses a self-approval, so the only possible approver is the maintainer's own account. The moment this gate lands, every T2/T3 stops until the maintainer clicks Approve at the tip — which is the intent, but the first red merge-gate run it produces must read as "waiting for the human", not as a regression.
Tier: .github/** → T3.
Activity
Birfy commentedon Oct 22, 2026
Written-by: Dev3
Claimed for #691. Branch:
fix/691-approve-gate.Building it to the design in this issue:
judge()gains anapprovalsinput ({ login, state, commit_id, type, permission }) injected by the workflow likelabelsandreports; a T2/T3 entry that is not exempt is refused with<tier> entry has no approved reviewunless one approval isAPPROVED, names the judged head, comes from a non-Botaccount withadmin/write/maintainpermission, and is not the pull request's author..github/workflows/merge-gate.ymlfills the input fromGET /pulls/{n}/reviewsand resolvespermissiononly for the candidates. T0/T1 and the mechanical release PR keep the existingexemptpath, andmode: 'report'still never fails a check.Acceptance cases from this issue (T3 without an approval refuses, T3 with one passes, T0 without one passes, plus the stale-tip / author / Bot guard cases) go into
merge-gate.test.mjsas fixture cases; the red form is removing the approval branch and watching the T3 case go green.Birfy commentedon Oct 22, 2026
Triage label record (2026-10-22):
agent-pradded — PR #693 (fix(merge-gate): refuse a T2/T3 entry that no maintainer approved, tip6a3ec9a0, 9/9 green,clean) implements this issue.Tier is T3 (
.github/**), so the queue still needs a maintainer approve at the tip plus a PASS.Birfy commentedon Oct 22, 2026
Closing as not planned: the requirement this issue adds is withdrawn by the user's decision at 07:15Z on 2026-10-22.
The user asked for T2/T3 to merge without a human approve. That makes "T2/T3 needs an
APPROVEDreview at the head" the wrong gate to add, and it would also have hard-stopped the pipeline: every pull request here is authored by the sharedBirfyaccount and GitHub refuses a self-approval, so no approval could ever exist without the maintainer clicking it personally.What stands in its place (written down in the follow-up documentation issue): the mechanical gates are a PASS at the tip, the three checks, and
merge-gatere-judging the head at enqueue time.holdremains human-only, and an implementer still cannot review its own pull request.PR #693 implemented this issue and is closed unmerged for the same reason (the user's decision, recorded there by its author).