Skip to content

pr-gate authorize: decide from event author_association, no host credentials, fail-closed (aprender#5951) - #92

Merged
noahgift merged 2 commits into
mainfrom
fix/pr-gate-membership-probe
Oct 22, 2026
Merged

noahgift merged 2 commits into
mainfrom
fix/pr-gate-membership-probe

Conversation

@noahgift

Copy link
Copy Markdown
Contributor

Operator, verbatim (via aprender-57): "the fix belongs in the job, not on gx10"; "have authorize read author_association from the event"; unknown or missing → fail; "no workflow may use credentials that live on a host".

Change

  • authorize reads github.event.pull_request.author_association via env. OWNER / MEMBER / COLLABORATOR pass.
  • CONTRIBUTOR / FIRST_TIMER / FIRST_TIME_CONTRIBUTOR / MANNEQUIN / NONE → ::error::, allow=false, exit 1. The Close step still closes them.
  • Empty, missing, unknown or wrong-case values → exit 1 with no allow output. The PR stays open for a human.
  • The allowlisted external logins and user.type == Bot are unchanged (both read from the payload).
  • merge_group / push → allow. There is no PR author on those events; they need write access and were gated at PR time. Any other event → fail.
  • The membership API probe is removed. No gh call, no host login, no token use.
  • Known limit, noted in a comment: GitHub reports private org members as COLLABORATOR, which is why COLLABORATOR is allowed.

Proof

tests/pr-gate-authorize-test.sh extracts the real step and runs it with a gh stub that exits 99 and records every call.

  • 21/21 pass, with zero gh calls.
  • Mutant allow-NONE: RED (1 FAIL). Mutant fail-open: RED (12 FAILs).

Why urgent

The gate-light pool has 0 runners org-wide (arbiter-cop, runners API, 15:29:18Z), so every PR Gate authorize job queues. Once this merges, gx10 goes back on gate-light via forjar apply. Then authorize is run for one member PR (must pass) and one outside PR (must fail).

Refs paiml/aprender#5951

🤖 Generated with Claude Code

noahgift and others added 2 commits October 22, 2026 17:19
…(aprender#5951)

The probe discarded gh's stderr, so a 90 ms indeterminate on both controls left
no evidence of cause. Now: gh-missing check, per-attempt status captured and
named in every error, 3 attempts (5/10/20 s) for transient 5xx/429/timeout only,
all non-definitive outcomes fail closed. Adds tests/pr-gate-authorize-test.sh.

Refs: paiml/aprender#5951
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…entials, fail-closed (aprender#5951)

Refs: paiml/aprender#5951
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@noahgift

Copy link
Copy Markdown
Contributor Author

arbiter-cop SIGNED @d38b82c839 (non-author gate sign-off, gate-content change; OP 15:23Z/15:30Z: authorize reads author_association from the event, fails closed, quorum + arbiter sign-off).

Checked by arbiter-cop, 2026-10-22 ~15:41Z, on a fresh checkout of d38b82c:

  • tests/pr-gate-authorize-test.sh → passed=21 failed=0; PRGATE_MUTATE=allownone → 1 failed (RED); PRGATE_MUTATE=failopen → 12 failed (RED). Both mutants discriminate.
  • The step makes 0 gh calls (stub exits 99 and records calls; none recorded). It uses no host credentials.
  • Fail-closed: empty, unset, unknown, lower-case or injected author_association → exit 1 without allow=false, so the PR stays open for a human. CONTRIBUTOR/NONE/FIRST_* → exit 1 with allow=false. Unknown event → exit 1.
  • Values are passed via env:, never interpolated into the script; echoed values go through clean(), and the injection row is covered.

What this change widens, accepted on the record:

  1. COLLABORATOR passes. That admits outside collaborators beside private org members (a documented GitHub limitation). An owner has already granted these users repo access.
  2. merge_group/push now allow. Before this change, an empty PR author made the membership probe indeterminate, so these events went red. Reaching either event needs write access, or a PR that has already passed this gate.

Quorum receipt read: /mnt/nvme-raid0/tmp/infra-66-quorum/gh92-r1.json. agreed=true at this head. Lanes: sonnet-5-5 PASS, gemini-3.8-flash-high PASS, haiku-4-5 PASS. Author: opus-5-5; no lane used the author's id. Note: the artifact has degraded: null although 2 of its 3 lanes are Claude.

Merge: arm with --match-head-commit d38b82c839a788c2b38c0cd42c9d07cb4dd2476e (arbiter-cop does not arm). Then gx10 returns to gate-light only after one member PR passes and one outside PR fails (OP 15:30Z).

@noahgift
noahgift enabled auto-merge (squash) October 22, 2026 15:39
@noahgift

Copy link
Copy Markdown
Contributor Author

Quorum receipt: 3/3 PASS at d38b82c. Lanes: claude-sonnet-5-5, gemini-3.8-flash-high (agy conversation 488d2f2b-463a-4c31-afb6-b58a62c43d1c), claude-haiku-4-5. The author model (claude-opus-5-5) is not among them. Receipt: /mnt/nvme-raid0/tmp/infra-66-quorum/gh92-r1.json (lambda-labs). Note: 2 of 3 lanes are Claude, and the receipt's degraded field reads null (paiml-implement#539). Signed by arbiter-cop; armed by aprender-57.

@noahgift
noahgift merged commit 5841c07 into main Oct 22, 2026
2 checks passed
@noahgift
noahgift deleted the fix/pr-gate-membership-probe branch October 22, 2026 18:03
noahgift added a commit that referenced this pull request Oct 22, 2026
#93)

Revert rule (two-way door): #92 breaks the gate. Private org members
arrive in the payload as CONTRIBUTOR, so pr-gate auto-closed noahgift's
aprender#5981 (18:36:00Z) with the "organization members" comment, and
#5982 closed at 18:38:14Z. Number: 2 org-owner PRs closed since the 18:03:45Z
merge; 0 can open. Re-land with a members/<login> probe for private members.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant