fix(ci): accept the override from a PR review, and stop misdiagnosing an unreadable comments API
gate / gate (pull_request) Failing after 1m47s
gate / conformance (pull_request) Has been skipped

The override did not work on its first real test, and the reason was invisible
from the outside. Run 203 on PR #38: a well-formed `/approve-override` posted by
jakman85 in the PR comment box, 29 minutes newer than the head commit, and the
stage still blocked with:

    approval-override: COMMENTS_JSON is not a list

SETTINGS_API_TOKEN is minted [read:repository, read:admin], and Gitea puts
/issues/{n}/comments behind the *issue* scope — so Gitea answered with an error
OBJECT, `jq -e .` accepted it as valid JSON, and the stage fell through to 'no
approving review'. A permission failure wearing the costume of a missing
override. /pulls/{n}/files and /pulls/{n}/reviews are under read:repository,
which is why every other part of the same stage worked.

1. The diagnosis no longer lies. COMMENTS_JSON leaves the must-parse-or-block
   loop and is checked for being an ARRAY; when it is not, the stage prints
   Gitea's own .message, names the missing scope and says where to post instead.
   An unreadable source can only cause a false BLOCK, never a false pass, so a
   loud warning is the honest behaviour and blocking was not.
2. An override is accepted from a PR REVIEW body as well as a comment, so the
   hatch is not hostage to one API scope — a review of type 'Comment' works on
   the token CI has today. The gate names which source it used; PENDING
   (unsubmitted, author-only) reviews are dropped.
3. set-ci-token.sh requests read:issue, making the ordinary comment box work —
   on a RE-RUN only, since an existing token's scopes cannot be widened.

ci/tests/approval-override.test.sh: 23 -> 38 assertions, covering the reviews
source, PENDING reviews, an error object from the comments API, 'an unreadable
comments source must not suppress a valid review override', latest-wins across
both sources, and that the diagnostic quotes Gitea's message.

Also measured and recorded: run 203 shows gitea-settings PASS after the operator
ran the live PATCH, with block_admin_merge_override still true — the narrow
change held, the approval count moved and the gate's teeth did not.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
jakman85andClaude Opus 5 committed 2026-10-02 00:44:31 -05:00
1 parent 8ccfdab180
commit b946c475c6
8 files changed
+350 -84

No files matched your search

+11 -3
View File
@@ -40,13 +40,21 @@ missing credential is a lock-out, here a skip would be a bypass.
**The administrator override (2026-10-01).** The rule above was unsatisfiable for the one human
who runs this repo: Gitea refuses to let an author approve their own PR, and `jakman85` is the
only collaborator — so a sensitive-path PR the operator opened could never be merged, *including
the PR that would fix that.* An administrator may therefore waive the review by commenting on
the PR that would fix that.* An administrator may therefore waive the review by posting this on
the PR:
/approve-override <reason>
The directive must **start a line**, the **reason is mandatory**, the commenter's `admin`/`owner`
permission is re-checked **live** when the gate runs, and the comment must be **newer than the
**Post it as a PR *review* of type "Comment", not in the plain comment box.** CI's
`SETTINGS_API_TOKEN` is minted `[read:repository, read:admin]`, and `/issues/{n}/comments` sits
behind Gitea's *issue* scope — so an override typed into the comment box is **invisible to CI**,
and the stage reports it as a missing approval rather than as a permission problem (measured on
PR #38, 2026-10-02). `/pulls/{n}/reviews` is readable, so a review body works today. Both sources
are accepted and the gate names which it used; the comment box starts working once
`infra/gitea/set-ci-token.sh` is re-run to pick up `read:issue`.
The directive must **start a line**, the **reason is mandatory**, the poster's `admin`/`owner`
permission is re-checked **live** when the gate runs, and it must be **newer than the
head commit** — push again and you must override again. The overrider, the timestamp and the
reason are printed in the gate log and the job summary. For an administrator, "a change to the
gate cannot be gated by the gate" is therefore **advisory, not structural**; what is kept is an