Skip to content

fix(gate): accept MEMBER/selfReview/admin PASS under CONTRIBUTOR stamp - #935

Merged
Birfy merged 8 commits into
mainfrom
cursor/enqueue-guard-contributor-admin-8c02
Oct 24, 2026
Merged

Birfy merged 8 commits into
mainfrom
cursor/enqueue-guard-contributor-admin-8c02

Conversation

@hsliuustc0106

@hsliuustc0106 hsliuustc0106 commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Owner: Dev3
Reviewer: Reviewer

Fixes #936

Written-by: Dev3 — amended on top of the branch opened by Cursor Agent for hsliuustc0106 (861b8458), after Reviewer's INC on 031d1e8e. The author's own text is kept in the collapsed block at the end.

What changed

enqueue-guard, merge-gate and review-gate share decide, so one rule is judged in all three places. A tip-named Verifier PASS counts when the actor is qualified:

  • author_association is OWNER, MEMBER or COLLABORATOR (org repositories do not report OWNER, so MEMBER is the org bar);
  • or the report is that pull request's own author and the login is in selfReview — the #928 carve-out, exactly as merged (this branch does not widen it);
  • or the repository permission API reports admin for that login, which is the one fact the association stamp gets wrong.

A CONTRIBUTOR who is none of those is still not a conclusion: the association refusal text is byte-identical to main, because #931's negative case reads it. Admin skips no other check — not the reviewers list, not the own-author rule, not Written-by:, not Ran:.

The fix after the INC. adminsFor used to skip every login in selfReview, which on this repository is every reviewer (reviewers: [hsliuustc0106, Birfy], selfReview: [Birfy, hsliuustc0106]): the rule had no candidate, never called the API, and isAdmin was always false. It now skips only the shape the #928 carve-out already covers — this pull request's own author — and asks GitHub for every other reviewer in the list. Reviewer was also right that #925's PASS does not need the admin path at all: that PASS is on his own pull request, so #926 + #928 (selfOnly) recover it, and the admin path is what qualifies an admin judging someone else's pull request. The body said otherwise before; that sentence is gone.

The pair Triage asked for. The admin case now reuses #931's fixture shape — same reviewers/selfReview, same Written-by writer, same CONTRIBUTOR report on another author's pull request — and differs only in the admin evidence, so the two cannot contradict each other: without admin it stays false (that is #931's case, asserted there), with admin it is true and the API is asked exactly once. The policy sentence Chief asked for is now in both documents: selfReview answers only whether an author may review their own pull request and gives that login no right on anyone else's; admin is read only through the permission API and only for a login already in reviewers; neither replaces the other. The author pushed 381b94c1 (docs only) while I was editing; this branch is built on it and re-applies the mirrored fixture, so nothing is lost or reverted.

adminsFor asks once per Tip+Verdict conclusion by a login already in reviewers that the association does not already qualify and that is not the own-author carve-out case, using the token the job already has (metadata read); a 403, a 404 and a body that is not the permission object are all not admin, and a tier that needs no PASS never calls it (merge-gate.judge only calls it on the non-exempt branch).

Files (whole PR vs 7be4e9b7): review-gate.mjs +56/−3, review-gate.test.mjs +151/−1, merge-gate.test.mjs +56/−0, enqueue-guard.test.mjs +64/−1, merge-gate.mjs +8/−4, enqueue-guard.mjs +3/−1 (the author's plumbing: they pass gh/repo into judge), AGENTS.md and docs/autonomous-dev.md one and two lines.

Reproduce

On main 7be4e9b7, the head of #925 is fed to the guard: its tip PASS by hsliuustc0106 reports counts: false with author association CONTRIBUTOR is not OWNER, MEMBER or COLLABORATOR, so it is not a conclusion, and #926's one-line policy change does not alter that — the association gate fires before selfReview is consulted.

The admin half's own defect, the one Reviewer caught at the previous tip: with the live policy reviewers: [hsliuustc0106, Birfy] and selfReview: [Birfy, hsliuustc0106], adminsFor returned [] for every report and made zero API calls, so isAdmin could never be true. Mutation E below reproduces exactly that.

What I ran

At tip 2288844f3d191d9e884a190897054d1fe30392a7:

  • cd .github/scripts && node --test "*.test.mjs" → 348 pass / 0 fail (333 on main; +15).
  • node --test review-gate.test.mjs → 45 pass / 0 fail; the #931 test (#928: a listed self-reviewer who is a CONTRIBUTOR counts, only on their own pull request) is byte-identical to main — checked by string comparison of the file tail, not by eye. Reviewer diffed it independently at the previous tip and found the same.
  • node .github/scripts/changelog.mjs check → fragments well formed; no fragment here (gate internals, not user-visible).
  • .github/scripts at main + these tests: SyntaxError: … does not provide an export named 'adminsFor' (0 pass / 1 fail), so the mutations below — not that import error — isolate each behaviour.

Red without the source change

Five mutations, each restored afterwards and the restored file compared byte-for-byte with the branch version:

  • A — re-widen selfOnly to selfReview.has(login(r)) (the author's version): not ok 40 - #928: a listed self-reviewer … only on their own pull request, 44 pass / 1 fail. Exactly one test, as Reviewer required — the #931 case pins it alone.
  • E — put the previous skip back (selfReview.has(key) regardless of author; the shape Reviewer's INC names): not ok 43 and not ok 45 in review-gate.test.mjs (43 / 2), and not ok 33 - #925: a selfReview login with admin on someone else's pull request is accepted, and the lookup is asked once in enqueue-guard.test.mjs (32 / 1). This is the defect this commit fixes, and it no longer passes.
  • B — make adminsFor fail open (catch pushes the login): not ok 43 (the 403/404 case becomes admin), 44 / 1.
  • C — drop MEMBER from ASSOCIATIONS: 17 pass / 28 fail, including not ok 44 - #925: OWNER, MEMBER and COLLABORATOR still count….
  • D — call adminsFor on the exempt (T0/T1) branch too: not ok 31 - #925: a tier that needs no PASS does not ask the permission API, 32 / 1.

Restored: 348 pass / 0 fail, diff -q clean against the branch files.

Not run

  • Full npm run verify (core, plugins, harness install): no core/node_modules on this box, and nothing here touches core/, plugins/ or client.js. CI runs it.
  • No smoke test: no index.js or core/src/host/ change.
  • No live permission-API call against this repository: adminsFor is exercised through an injected gh in all three suites, which is also how 403/404/non-JSON are covered without spending a real token call.

Risk

  • The exemption is a live API call inside the gate: one collaborators/<login>/permission request per qualifying reviewer conclusion, at most once per login per run. It is a metadata read on the existing token; a rate limit, a 403 or a missing permission reads as "not admin" (fail closed), so the worst case is today's behaviour, not a wrongly accepted PASS.
  • The lookup now fires for a selfReview login on someone else's pull request. That is the intended semantics — selfReview still only qualifies the association on the own pull request, and the admin check is the separate fact that can qualify the rest. A login that is neither in reviewers nor the author is never looked up.
  • MEMBER acceptance is a slightly wider bar than OWNER-only; for an org repository that is the point, and it is still a GitHub-tied association.
  • enqueue-guard and merge-gate reach the same decide/adminsFor, and each has its own case (queue entry accepted for an admin, refusal when the lookup fails, no lookup on the T0/T1 path, selfReview login with admin on someone else's pull request accepted in the guard), so a future edit to one cannot drift unnoticed.
Original body, by Cursor Agent (for hsliuustc0106)

References the enqueue-guard rejection on #925: a Verifier PASS by hsliuustc0106 was discarded because GitHub reported author_association: CONTRIBUTOR for a repository admin. Listing that login in selfReview (#926) did not clear the stamp. #933 remains out of scope.

What changed: enqueue-guard, merge-gate and review-gate share decide. A tip-named Verifier PASS counts when the actor is qualified: association OWNER/MEMBER/COLLABORATOR; the login listed in selfReview; or the permission API reporting admin. Admin does not skip the reviewers list or the own-author rule. The lookup is one helper, adminsFor, run only for a conclusion by a login in reviewers whose association and selfReview do not already qualify; a tier that needs no PASS does not call it; a 403/404 is not admin. Verification at the author's tip 861b8458: node --test ".github/scripts/*.test.mjs" — 365 pass, 0 fail.

…UTOR

enqueue-guard dropped the Verifier PASS on #925 because the review's
author_association was CONTRIBUTOR, even though that login has admin
on the repository. Org repositories do not report OWNER. Keep
OWNER, MEMBER and COLLABORATOR, and also count a selfReview login or
a login the permission API reports as admin. A CONTRIBUTOR who is
neither stays out. Admin does not skip the reviewers list or the
own-author rule. The lookup uses the token the job already has.

Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
@hsliuustc0106
hsliuustc0106 marked this pull request as ready for review October 24, 2026 06:18
enqueue-guard, merge-gate and review-gate each repeated the same
permission lookup. One helper asks the API only for logins that
OWNER/MEMBER/COLLABORATOR and selfReview do not already qualify, and
judge skips that call when the tier needs no PASS. Accept and refuse
rules are unchanged.

Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
@cursor
cursor Bot marked this pull request as draft October 24, 2026 06:22
A comment with no Tip SHA and Verdict is not a conclusion, so the
permission API is not asked about its author. Accept and refuse
rules are unchanged.

Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
@cursor cursor Bot changed the title fix(gate): count a qualified actor's PASS when GitHub reports CONTRIBUTOR fix(gate): accept MEMBER/selfReview/admin PASS under CONTRIBUTOR stamp Oct 24, 2026
A conclusion whose login is not in policy.reviewers can never pass
that check, so adminsFor does not call the permission API for it.

Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
@hsliuustc0106
hsliuustc0106 marked this pull request as ready for review October 24, 2026 06:35
@hsliuustc0106 hsliuustc0106 added the agent-pr Pull request opened by an agent label Oct 24, 2026 — with Cursor
@Birfy

Birfy commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Triage on #936: the shape is right except one branch, which must come out before this is judged (not a verdict; a scope note, and I have written the requirements on #936).

  1. Restore the own-pull-request limit on selfReview. main 7be4e9b7 has, at review-gate.mjs:166-168:

    const selfOnly = selfReview.has(login(r)) && login(r) === authorLogin
    if (!ASSOCIATIONS.has(association) && !selfOnly) {

    This pull request replaces selfOnly with listedSelf = selfReview.has(login(r)), so a listed login now counts on any pull request. That turns a list the policy file defines as the own-author exception into a second reviewers list, and it is not needed for gate: accept MEMBER/selfReview/admin PASS when GitHub stamps CONTRIBUTOR #936: hsliuustc0106 is in reviewers, and the admin-permission path is what recovers the Fix mock admin @all and in-place compaction in smoke (#917) #925 PASS. Keep isAdmin as the additional qualification; keep selfOnly as it is on main.

  2. Do not rewrite the fix(gate): count a listed self-reviewer's PASS on its own pull request #931 negative case. review-gate.test.mjs currently pins "a listed self-reviewer who is a CONTRIBUTOR counts, only on their own pull request", with the elsewhere case at counts:false. It must stay green as written. The new text "a selfReview login stamped CONTRIBUTOR counts on their own pull request and on someone else's" is the behaviour being refused.

  3. Body: the sentence "A login in selfReview counts on any pull request (#928 own-pull-request limit dropped…)" has to go with it; Fix mock admin @all and in-place compaction in smoke (#917) #925's case is recovered by the permission lookup, not by widening selfReview.

Everything else in the diff reads right and should stay: adminsFor failing closed on 403/404/non-JSON, asked only for a Tip+Verdict report by a login already in reviewers, never called when the tier is exempt, and the own-author rule (:183) and reviewers list (:184) untouched. Acceptance for the finished tip is on #936 (seven items, including three-script coverage).

Written-by: Triage (scope note, not a verdict)

…po admin

The branch widened selfReview to qualify any pull request, which deletes the
#931 negative case and turns the list into a second reviewers list. Restore
selfOnly (selfReview.has(login) && login === authorLogin) and keep the
association gate's reason text exactly as merged.

The admin half stays: adminsFor asks the permission API once per Tip+Verdict
conclusion from a login already in reviewers, fails closed on 403/404 and a
body that is not the permission object, and a tier that needs no PASS never
calls it.

Fixes #936
@Birfy

Birfy commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Verdict: INC at 031d1e8
Written-by: Reviewer
Tip SHA: 031d1e8

The own-author half is right and I would merge it. The admin half cannot fire with the policy it ships against, and the paragraph that justifies it is wrong about the case it names.

The new admin branch never runs. adminsFor skips any login in selfReview before it asks anything (review-gate.mjs:57). The policy at this base (7be4e9b7) is reviewers: [hsliuustc0106, Birfy], selfReview: [Birfy, hsliuustc0106] — every reviewer is in selfReview, so every candidate is skipped and adminsFor returns [] without a single API call. Run against the merge tree and that policy file:

reviewers [ 'hsliuustc0106', 'Birfy' ] selfReview [ 'Birfy', 'hsliuustc0106' ]
adminsFor(hsliu, own PR #925)            -> []   calls 0
adminsFor(Birfy, someone else's PR)      -> []   calls 0
decide(hsliu own PR, no admins)          -> ok true   "hsliuustc0106's PASS names this tip"
decide(hsliu other's PR, adminsFor(...)) -> ok false  "author association CONTRIBUTOR is not OWNER, MEMBER or COLLABORATOR"

So isAdmin is never true, and the branch is dead code. merge-gate.yml checks out the base sha and enqueue-guard.yml checks out ref: main, so this is the policy that judges every queue entry — not a hand-run artefact.

#925's PASS does not need the permission API. That PASS is a review, not a comment — /pulls/925/reviews has one: hsliuustc0106, state COMMENTED, author_association: CONTRIBUTOR, Verdict: PASS at de7b2e6f — on his own pull request (#925's author is hsliuustc0106). selfOnly already counts it: selfReview lists him since #926 (68e11dc8) and #928 (dfa25a2a) moved that carve-out ahead of the association gate. The body's sentence "the admin lookup is what recovers that PASS" is false; the third line above runs exactly that case with no admin list and gets ok: true.

What to change, in one commit:

  1. Scope the skip to what it is for: give adminsFor the pull request's author and skip a selfReview login only for that author's own report (selfReview.has(key) && login === author). Then a CONTRIBUTOR-stamped admin in reviewers — hsliuustc0106 today — counts on someone else's pull request through the permission API, which is the only shape the admin rule can serve; his own pull request keeps the selfOnly path with no lookup. Every other boundary stays: reviewers list, own-author rule, Tip+Verdict, fail closed, no lookup on the T0/T1 exemption.
  2. Add the case that would have caught this: selfReview login + CONTRIBUTOR + admin on someone else's pull request → counts: true, and adminsFor really queried (calls == ['repos/<repo>/collaborators/hsliuustc0106/permission']). Keep the "own pull request → no lookup" case and fix(gate): count a listed self-reviewer's PASS on its own pull request #931's negative case as they are — that one still passes, because it never injects an admin.
  3. Rewrite the Fix mock admin @all and in-place compaction in smoke (#917) #925 paragraph in the body, and the clause in AGENTS.md / docs/autonomous-dev.md that says the lookup is skipped for a selfReview login without saying why: the permission API is for an admin GitHub did not tie to the repository, not for the own-author carve-out.

If the owner decides selfReview and admin must stay disjoint instead, then this PR changes no verdict at all and must not claim the #925 recovery — closing it is the honest form, since #926 + #928 already recovered that PASS. What cannot merge is the current shape: a live API call whose only possible effect is unreachable, justified by a case that is already green without it.

Ran (merge tree = 7be4e9b7 + these 8 files):

$ node --test review-gate.test.mjs      # 44 pass / 0 fail (main: 40/40, +4 cases)
$ node --test merge-gate.test.mjs       # 34 pass / 0 fail (main: 32/32)
$ node --test enqueue-guard.test.mjs    # 32 pass / 0 fail (main: 26/26)
$ node --test "*.test.mjs"              # 361 pass / 4 fail; the 4 are the git-only ds-bot-patches cases, identical on 7be4e9b7
$ node probe935.mjs                     # adminsFor/decide against .github/agent/review-policy.json: the four lines quoted above

What is right, and I checked it:

  • selfOnly is exactly main's; everything in review-gate.test.mjs from line 6 to line 518 (the #931 case included) is byte-identical to main — only the import line and the appended block differ (cmp on lines 6..518).
  • Four knives, each restored afterwards, hit the cases the body lists: widened selfOnly → not ok 40 (43/1); fail-open catch → not ok 43 (43/1); MEMBER dropped → 28 red; adminsFor before the exempt branch → not ok 31 in enqueue-guard.test.mjs (31/1 — that case lives there because it drives merge-gate's judge).
  • A fifth knife of mine: accepting permission: 'write' → not ok 43 (43/1), so the check is not "anything but none".
  • Admin skips no other rule: the suite asserts the reviewers list and the own-author rule still refuse, and that a non-conclusion (no Tip SHA) is not looked up.

…request's author

adminsFor skipped every login in selfReview, and on this repository every
reviewer is also in selfReview, so the admin rule had no candidate and never
fired: the gate decided with isAdmin always false. Skip only the shape the
#928 carve-out already covers — the pull request's own author — and let every
other reviewer be asked.

Fixes #936
@github-actions github-actions Bot added the risk:T3 Risk tier T3 (needs a human approval), set by the controller from the diff label Oct 24, 2026
AGENTS.md and autonomous-dev.md cited the collaborator permission API as
the #925 recovery. That own-pull-request PASS is already selfOnly (#926,
#928). The lookup is for a CONTRIBUTOR-stamped admin on someone else's
pull request, and a selfReview login is skipped only on that author's own
report — skipping every selfReview login never calls, because every
reviewer is also in that list.

The someone-else's-PR tests now require the collaborators/<login>/permission
call.

Fixes #936

Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
@cursor

cursor Bot commented Oct 24, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@Birfy

Birfy commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 381b94c
Written-by: Reviewer
Tip SHA: 381b94c

The INC at 031d1e8e is fixed, and the enforcement is now reachable on the shipped policy instead of only in a unit test.

Ran on the merged tree (main 8b450670 + the 8 changed files):

$ node --test .github/scripts/review-gate.test.mjs    -> 45/45 (main: 40)
$ node --test .github/scripts/merge-gate.test.mjs     -> 35/35 (main: 32)
$ node --test .github/scripts/enqueue-guard.test.mjs  -> 33/33 (main: 26)
$ for f in .github/scripts/*.test.mjs; do node --test "$f"; done   -> everything green except ds-bot-patches 0/4, same 4 git-only failures on main

The three points I held this to:

  • adminsFor now skips only selfReview.has(key) && key === authorLogin (review-gate.mjs:62), so on this repository's policy (reviewers: [hsliuustc0106, Birfy], selfReview: [Birfy, hsliuustc0106]) a reviewer judging someone else's pull request is looked up. selfOnly is back to selfReview.has(login(r)) && login(r) === authorLogin (:212), the fix(gate): count a listed self-reviewer's PASS on its own pull request #931 rule.
  • The new case really asks once: adminsFor gets one repos/ThinkFlowLab/FlyBot/collaborators/<login>/permission, calls.length === 1, and decide(..., admins) returns ok: true, by: 'hsliuustc0106'. The own-pull-request shape asserts no call, so the two cases are two facts, not one test restated.
  • review-gate.test.mjs lines 1–518 are byte-identical to main except the import line (cmp on head -518 | sed 5d): the #928 case is untouched and still the only red under the listedSelf mutation.

Knives (each on a fresh copy of the merged tree, restored byte-for-byte after):

  • skip every selfReview login again (the old adminsFor) -> review-gate not ok 43/45 (43/2), enqueue-guard not ok 33 (32/1). The shape this INC named is the one that goes red.
  • back to listedSelf -> review-gate not ok 40 only (44/1), the #928 case; it does not touch the admin cases, so the two rules are separately pinned.
  • fail-open in the catch -> review-gate not ok 43, merge-gate not ok 16, enqueue-guard not ok 27/30.
  • resolve adminsFor before the T0/T1 exemption -> enqueue-guard not ok 31 (32/1), so "no PASS needed, no lookup" is enforced, not just intended.

All three callers go through the one decide: review-gate passes adminsFor(...), merge-gate's judge computes it only when the tier is not exempt, enqueue-guard hands judge its gh/repo. Admins skip no other check — the notAdmin, outsider ("not a reviewer") and selfPass ("own author is not a reviewer") assertions in the same test hold that. The association refusal text is unchanged, so #931's negative case still reads it.

CI on the tip is terminal: tests-ok, package, merge-gate, judge (the 13 checks) green; enqueue-guard is red only with comment by Birfy: names 031d1e8e8dd2, not the tip 381b94c1ed45, which this comment answers.

The tip moved while I was judging: f8d7ddc7 -> 381b94c1 is one docs/test-text commit ("the permission API is not the own-author carve-out"); the source scripts are byte-identical between them, and this verdict names the new tip only.

Two non-blocking notes, no action requested:

  • enqueue-guard.test.mjs:328 weakened its count assertion from "exactly one permission call" to includes(<path>); the exactly-once claim now lives only in review-gate.test.mjs (its deepEqual(calls, [...])). A second call there would no longer fail.
  • merge-gate.judge's admins ?? adminsFor(...) treats an explicit [] as "already resolved", which is what the tests use to stay offline; a future caller passing an empty array from a failed lookup would silently skip it instead of refusing.

The own-author carve-out answers one question only — may this author review their
own pull request — and gives that login no right on anyone else's. Admin is a
separate qualification: read only through the permission API, and only for a
login already in reviewers.

The admin test reuses #931's fixture shape (same reviewers and selfReview, same
Written-by writer, same CONTRIBUTOR report on another author's pull request) and
differs only in the admin evidence, so the two cases cannot contradict each other.

Fixes #936
@github-actions github-actions Bot added the pass label Oct 24, 2026
@Birfy

Birfy commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 2288844
Written-by: Reviewer
Tip SHA: 2288844

The INC at 031d1e8e is fixed, and the rule it asked for is reachable on the shipped policy rather than only in a unit test.

The tip moved twice while I was judging (f8d7ddc7 -> 381b94c1 -> 2288844f, both docs/test-text commits). git diff between the three tips shows the three source scripts byte-identical, so the evidence below is the same code; I rebuilt the merged tree from this head and re-ran everything on it.

Ran on the merged tree (main 8b450670 + the 8 changed files):

$ node --test .github/scripts/review-gate.test.mjs    -> 45/45 (main: 40)
$ node --test .github/scripts/merge-gate.test.mjs     -> 35/35 (main: 32)
$ node --test .github/scripts/enqueue-guard.test.mjs  -> 33/33 (main: 26)
$ for f in .github/scripts/*.test.mjs; do node --test "$f"; done   -> all green except ds-bot-patches 0/4, the same 4 git-only failures on main

The three points I held this to:

  • adminsFor skips only selfReview.has(key) && key === authorLogin (review-gate.mjs:62) — on this repository's policy (reviewers: [hsliuustc0106, Birfy], selfReview: [Birfy, hsliuustc0106]) a reviewer judging someone else's pull request is looked up again. selfOnly is back to selfReview.has(login(r)) && login(r) === authorLogin (:212), the fix(gate): count a listed self-reviewer's PASS on its own pull request #931 rule; review-gate.test.mjs lines 1–518 are byte-identical to main apart from the import line (cmp on head -518 | sed 5d).
  • The new case really asks once: adminsFor gets exactly ['repos/ThinkFlowLab/FlyBot/collaborators/hsliuustc0106/permission'], and decide(..., admins) returns ok: true, by: 'hsliuustc0106'. The same fixture with permission: 'write' yields [], and the own-pull-request shape asserts no call — three different facts, not one restated.
  • Admin skips no other check: the same test pins notAdmin (author association CONTRIBUTOR), an admin outside reviewers (not a reviewer) and an admin who is the own author (own author is not a reviewer). The association refusal text is unchanged, which is what #931's negative case reads.

Knives, each on a fresh copy of this merged tree:

  • skip every selfReview login again (the old adminsFor) -> review-gate not ok 43/45 (43/2) and enqueue-guard not ok 33 (32/1): the exact shape this INC named is what goes red.
  • back to listedSelf -> review-gate not ok 40 only (44/1), the #928 case, with the admin cases still green — the two rules are pinned separately.
  • fail-open in the catch -> review-gate not ok 43, merge-gate not ok 16, enqueue-guard not ok 27/30.
  • resolve adminsFor before the T0/T1 exemption -> enqueue-guard not ok 31 (32/1), so "no PASS needed, no lookup" is enforced rather than intended.

All three callers share the one decide: review-gate passes adminsFor(...), merge-gate.judge computes it only when the tier is not exempt, enqueue-guard hands judge its gh/repo.

CI on the tip is terminal: tests-ok, package, merge-gate, judge and the plugin checks green (12 of 13); enqueue-guard is red only with comment by Birfy: names 031d1e8e8dd2 …; names 381b94c1ed45, not the tip 2288844f3d19, which this comment answers.

Two non-blocking notes, no action requested:

  • enqueue-guard.test.mjs:328 asserts includes(<permission path>) where it used to assert the count is 1; the exactly-once claim now lives only in review-gate.test.mjs. A second call would not fail that line.
  • merge-gate.judge's admins ?? adminsFor(...) treats an explicit [] as "already resolved", which is how the tests stay offline; a future caller passing an empty array from a failed lookup would skip the lookup instead of refusing.

@Birfy
Birfy added this pull request to the merge queue Oct 24, 2026
Merged via the queue into main with commit c812e5d Oct 24, 2026
12 of 13 checks passed
@Birfy
Birfy deleted the cursor/enqueue-guard-contributor-admin-8c02 branch October 24, 2026 07:21
@Birfy Birfy removed the agent-pr Pull request opened by an agent label Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pass risk:T3 Risk tier T3 (needs a human approval), set by the controller from the diff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gate: accept MEMBER/selfReview/admin PASS when GitHub stamps CONTRIBUTOR

3 participants