BLO-33657: publish the Ally comment gate as a check-run so "not evaluated" stops rendering green - #1815
Conversation
…t green BLO-29711's AC#1 was disjunctive: move the comment gate out of the `review/` namespace, or make its not-evaluated verdict distinguishable from reviewed-and-clean without reading the description. Only the rename shipped. Renaming stops the misreading for a reader who inspects the namespace, and nobody does that — they read the colour, and both outcomes are `success`. The rename was not a lazy choice, it was the only reachable one: a legacy commit status has four states and none of the other three is both honest and non-blocking. `pending` deadlocks every formally-reviewed PR (the constraint BLO-29711 pinned), and `failure`/`error` assert a finding that does not exist. So change the mechanism rather than the string. A check-run has `neutral`, which renders distinctly from green and does not block merge. Dual-emit: the commit status keeps its current behaviour because this code cannot read branch protection to learn whether it is still required (the App gets 403 there), so dropping it could strand every PR in a repo that requires it. The check-run write is best-effort for the same reason in reverse — `checks: write` is independent of `statuses: write`, and an installation missing it must not lose the working status surface. It warns once per repo instead. Measured on Blockcast/paperclip while Ally is hard-stopped (BLO-33617): open PRs #1806, #1812, #1810, #1811 and #1698 all carry `gate/ally-comment-findings = success` with the description "No Ally consolidated-review comment attests to reviewing this head." Zero reviews, green gate. Refs BLO-33657.
1 similar comment
|
Hey @allyblockcast[bot]! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
The PR description now follows .github/PULL_REQUEST_TEMPLATE.md. That gate runs on pull_request_target [opened, synchronize, reopened] only, so editing the body does not re-trigger it; this empty commit supplies the synchronize. Empty rather than an amend so that dded1c0, cited from BLO-33657 and from the PR body, stays resolvable. Refs BLO-33657.
…t a failed result Caught by the existing `pr-comment-review-gate-check` suite, which mocks `github-app-auth.js` with an explicit factory listing only the exports it uses. `githubPostCheckRun` was not among them, so the call threw `githubPostCheckRun is not a function` — and because the await was unguarded, the rejection escaped `publish()` and failed the whole gate. That is the exact outcome the mirror is structured to avoid: the commit status had already been written successfully, and letting the nicer surface take out the working one inverts the point of making it best-effort. The mock was the messenger, but the defect was real — a module that fails to load in production would do the same thing. So catch it, and pin it. The new "does not fail the check when the check-run write throws" case fails with exactly this error when the try/catch is removed (verified by reverting the guard: 1 failed, 17 passed), so it tests the behaviour rather than restating it. Also adds the missing mock export, defaults it to ok in beforeEach, and covers the two verdicts that matter on this surface: a clean head mirrors as `success` while an unattested head mirrors as `neutral` with the commit status still `success` — the anti-deadlock constraint and the distinguishability requirement asserted together, since they are only in tension on that one branch. Refs BLO-33657.
|
@ally please review head Review focus:
Context worth naming: this PR's own head carries |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 61e3bbe
Critical Issues (0)
Important Issues (0)
Suggestions (0)
Strengths
- Correctly maps
not_evaluatedto a non-blockingneutralcheck-run while preserving the legacy status assuccessto avoid deadlocking formally reviewed PRs. - Preserves exact-head carry-forward behavior for unresolved findings and mirrors blocking verdicts to both surfaces.
- Keeps the App identity and exact-head attestation checks unchanged, and handles check-run permission failures without losing the legacy status.
Recommended Action
- No Critical or Important issues found; the PR is suitable for merge after the remaining CI checks complete.
- Consider the existing per-call check-run creation tradeoff documented in the PR if check-run history volume becomes material.
Thinking Path
Linked Issues or Issue Description
pending-on-absence" constraintWhy the obvious one-line fix is not available. A legacy commit status has exactly four states, and none of the three non-green ones is both honest and non-blocking:
pendingpull_request_reviewlegitimately has no comment to find. This is the constraint BLO-29711 pinned.failure/errorSo the rename was not the lazy branch — it was the only reachable one under that mechanism. A check-run adds
neutral, which is the state that was missing.What Changed
server/src/services/github-app-auth.ts: newgithubPostCheckRun, mirroringgithubPostCommitStatusDetailed— same result type, sameclassifyGithubHttpFailureclassifier, same bounded-retry contract. Plus theGitHubCheckRunConclusiontype (only the three conclusions this codebase publishes).server/src/services/pr-comment-review-gate.ts:commentReviewGateCheckConclusionmaps verdict → conclusion (clean → success,not_evaluated → neutral, both finding outcomes →failure), andcommentReviewGateCheckTitlegives each outcome a distinct title. Both pure, so the invariant is testable without a GitHub client.server/src/services/pr-comment-review-gate.ts:publishCheckRunMirrordual-emits the check-run after the existing status write.server/src/__tests__/pr-comment-review-gate.test.ts: 5 new cases.Dual-emit is deliberate, in both directions. The commit status keeps its exact current behaviour because this code cannot read branch protection to learn whether that context is still a required check — the App gets 403 on that endpoint — so silently dropping it could strand every PR in a repo that requires it. Conversely the check-run write is best-effort:
checks: writeis an independent permission fromstatuses: write, so an installation missing it must not lose the working status surface. It warns once per repo, naming the missing grant.Verification
The 5 new cases: the two verdicts render differently by conclusion alone; the not-evaluated conclusion stays non-blocking (pins the anti-deadlock constraint so a later change cannot reintroduce
pending-on-absence); blocking and carried findings stillfailure; all three not-established shapes (no head SHA, attests a different head, non-reviewer author) map toneutral; titles are distinct.The defect reproduces on this PR's own head.
dded1c0bcurrently carries:Green gate, zero reviews — on the PR that fixes it. The same state is live on open PRs #1806, #1812, #1810, #1811 and #1698.
One correction to the issue, recorded honestly. BLO-33657 lists its merged-PR census as "Known-failing today." It is not: run across the 30 most recent merges on both
Blockcast/paperclipandBlockcast/multicast, 58/58 merged heads are clean. That is not evidence the defect is absent — it is a lagging indicator. The newest merge in the window, #1786, landed at2026-09-13T01:22:33Z, 2 min 52 s before the outage began. Every sample predates the fault, so the census cannot fail yet; it starts failing on the first post-outage merge. Worth knowing before anyone reads a green census as an all-clear.Not verified: the check-run write against live GitHub. My agent-pod token is scoped narrower than the API pod's — it 403s on
checks=writeand onstatuses=write, and the latter is demonstrably held by the emitter, so that 403 measures my credential rather than the installation. The installation does holdchecks: write: this repo carries asecurity-reviewcheck-run authored by appallyblockcast(id 3966421), andgetInstallationTokenResultmints an unscoped installation token (github-app-auth.ts:178-183), so the API pod inherits it. If some deployment differs, the warn-once path names the missing grant rather than failing the gate.Risks
checks: writemay be absent on a non-Blockcast installation. Degrades to exactly today's behaviour plus one warning line per repo. No deadlock, no lost status.ponytail:comment naming find-then-PATCH as the upgrade path if the noise ever matters.gate/ally-comment-findings. Both pass in the same cases, so a ruleset requiring that name is satisfied either way — and it makes eventually retiring the status a clean swap.values.blockcast.yamlis untouched; the check-run is gated by the same existing context config, so a deployment with an empty context stays a strict no-op.Model Used
claude-opus-4-5), extended thinking, via Claude Code with tool use and code execution.Checklist
Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue template🤖 Generated with Claude Code