Skip to content

fix(github.blockers): read required checks and reviews without admin rights (F1–F3) - #874

Merged
cursor[bot] merged 1 commit into
masterfrom
cursor/blockers-required-fix
Oct 25, 2026
Merged

cursor[bot] merged 1 commit into
masterfrom
cursor/blockers-required-fix

Conversation

@IanFrelinger

@IanFrelinger IanFrelinger commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Fixes the three brick findings from the accuracy study in #871 (docs/benchmarks/blockers-accuracy.md on that branch). Allowed through the 0.4.0 freeze by the coordinator. It merges after #866, and #872 rebases onto it afterwards.

What was wrong

Without admin rights, github.blockers 1.0.0 could not read branch protection. On the study's 44 real pull requests:

  • F1: it listed every failing or pending check as a blocker, required or not. That gave 16 overcalls on 10 PRs.
  • F2: it never named a missing required review. On 24 of 24 PRs it reported only "Required approvals" as unknown.
  • F3: it never named a required check that had not run. It missed all 66, or reported "blocked by a branch rule this read could not identify".

Fix (brick 1.1.0)

GitHubBlockerReader now reads the same requirements from sources that need no admin rights:

  • Protection summary: GET /repos/{o}/{r}/branches/{base}, field protection.required_status_checks. Enforcement off counts as none.
  • Active rulesets: GET /repos/{o}/{r}/rules/branches/{base}, using the required_status_checks contexts and the highest pull_request.required_approving_review_count. These are now also read with admin rights, because classic protection never covered them.
  • GraphQL: isRequired(pullRequestNumber:) per check on the head commit, plus the pull request's reviewDecision. Without a token, or on any error, the read falls back.

BlockerAnalysis changes:

  • A failing check that is not required is a note ("It is not required, so it does not block the merge.").
  • A required check with no run blocks by name.
  • When it was read, reviewDecision decides the review blocker. It also covers code-owner and last-push rules that no count shows.
  • A ruleset approval count is a lower bound, so the brick blocks on it ("0 of 1 required approving reviews."). Classic protection may require more, so approvals stay an unknown only when nothing settles them.
  • The old fallback, where GitHub's mergeable_state decides, now applies only if both public lists fail to read.

Tests

The new tests in GitHubBlockersBrickTests replay facts trimmed from the study's snapshot, through Octokit and the real egress guard with a stub transport:

GitHubBlockersBrickTests passed 38/38 on both net8.0 and net10.0 in CI.

Mutation checks

Each check ran scripts/mutation-check.sh in the devtest container, in CI, at commit 1201818e5, with filter GitHubBlockersBrickTests:

Mutation Change Verdict
f1-nonrequired-check-blocks if (advisory || (required is not null && !required.Contains(name))) → if (advisory) KILLED red=failed:3/38 green=passed:38/38
f2-review-decision-dropped pull?.ReviewDecision → null KILLED red=failed:1/38 green=passed:38/38
f2-ruleset-approvals-dropped ruleset required_approving_review_count → 0 KILLED red=failed:2/38 green=passed:38/38
f3-public-summary-not-read summaryChecks = adminChecks (public branch summary never read) KILLED red=failed:4/38 green=passed:38/38

A first F3 mutation (if (summaryChecks is null && …)) did not compile because of a nullable error, so its verdict was INVALID; I replaced it with the F3 mutation in the table.

Records touched

  • src/Ashlar.Bricks.GitHub/README.md (Known limits)
  • docs/EgressInventory.md, row EG-HTTP-08: the GraphQL request body now carries owner, repository and number.
  • CHANGELOG.md (Unreleased)
  • Knowledge graph regenerated.

scripts/ci/run-repo-gates.sh: all 41 gates pass locally.

Open in Web Open in Cursor 

…rights (F1–F3)

Co-authored-by: icfrelinger <icfrelinger@gmail.com>
cursor Bot pushed a commit that referenced this pull request Oct 25, 2026
… 109/109, no wrong claims

Co-authored-by: icfrelinger <icfrelinger@gmail.com>
@cursor
cursor Bot merged commit 043a37a into master Oct 25, 2026
34 checks passed
@cursor
cursor Bot deleted the cursor/blockers-required-fix branch October 25, 2026 18:09
cursor Bot pushed a commit that referenced this pull request Oct 25, 2026
…OG entry under 1.1.0

Co-authored-by: icfrelinger <icfrelinger@gmail.com>
cursor Bot pushed a commit that referenced this pull request Oct 25, 2026
…OG entry under 1.1.0

Co-authored-by: icfrelinger <icfrelinger@gmail.com>
cursor Bot pushed a commit that referenced this pull request Oct 25, 2026
…OG entry under 1.1.0

Co-authored-by: icfrelinger <icfrelinger@gmail.com>
cursor Bot pushed a commit that referenced this pull request Oct 25, 2026
… as data (#872)

* github.blockers: quote text read from GitHub so it reaches the answer as data

Check-run names (a fork's workflow names its own jobs), commit-status contexts, descriptions and
target URLs (any integration with status access) went into the Markdown answer raw. A name with
line breaks could start its own heading, add links or tracking images, or address the model that
reads the answer; the PR-blockers Action writes that Markdown to the job summary. A status
target_url of javascript:... became an [evidence] link.

Each such string now enters through BlockerAnalysis.Quote (one inline code span: one line, no
bidi/format characters, a backtick becomes ', at most 100 characters) and each such URL through
Link (absolute http(s) only, parentheses and spaces encoded; otherwise the commit URL).

Co-authored-by: icfrelinger <icfrelinger@gmail.com>

* github.blockers: backtick check names in #874's expectations; CHANGELOG entry under 1.1.0

Co-authored-by: icfrelinger <icfrelinger@gmail.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
cursor Bot pushed a commit that referenced this pull request Oct 26, 2026
… 109/109, no wrong claims

Co-authored-by: icfrelinger <icfrelinger@gmail.com>
cursor Bot pushed a commit that referenced this pull request Oct 26, 2026
… brick vs native) (#871)

* blockers-accuracy: targets (44 PRs, 5 repos) and snapshot capture

Co-authored-by: icfrelinger <icfrelinger@gmail.com>

* blockers-accuracy: rubric, runs, scores and published study (44 PRs, brick vs native)

Co-authored-by: icfrelinger <icfrelinger@gmail.com>

* blockers-accuracy: capture an extra brick build, brick-only runs, stable score output

Co-authored-by: icfrelinger <icfrelinger@gmail.com>

* blockers-accuracy: re-run after the github.blockers 1.1.0 fix (#874): 109/109, no wrong claims

Co-authored-by: icfrelinger <icfrelinger@gmail.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.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.

2 participants