Skip to content

Commit ebb14ec

Browse files
authored
fix(ci): the ADR gate goes red while auto-merge is armed on a docs/adr/** PR (#8302)
* fix(ci): the ADR gate goes red while auto-merge is armed on a docs/adr/** PR Arming auto-merge is not merging, so no gate fired on it: an AI seat armed one on a live docs/adr/** PR at 11:15Z on 2026-08-12 while this gate was red. The next approving review would have turned the check green and GitHub would have merged the PR unattended, with every check reporting success — 「人工合并」 bypassed while nothing looked wrong. The gate now reads the PR's live auto-merge state and is red while it is armed, whether or not the PR is approved. The approved case is the only one in which the unattended merge actually happens; a rule firing only on the unapproved case would change no verdict, since that state is already red for the missing approval. Not a deadlock: anyone can disable auto-merge, and the new auto_merge_disabled trigger re-runs the gate so it clears itself. Refs #8012 * test(ci): pin the projection diagnosis, not just that it throws Mutation testing found the looser assertion passed on a mutant that deleted the Object.hasOwn guard: undefined falls through to the not-an-object branch and throws a message that also contains 'auto_merge'. Refs #8012 --------- Co-authored-by: os-zhuang <os-zhuang@users.noreply.github.com>
1 parent 2d8dba3 commit ebb14ec

2 files changed

Lines changed: 610 additions & 62 deletions

File tree

‎.github/workflows/adr-merge-approval.yml‎

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,23 @@ name: ADR Merge Approval
66
# A PR whose diff touches docs/adr/** must carry an APPROVED review before it
77
# is mergeable. The gate does NOT check WHO approved: any account with review
88
# rights on this repo — including an AI seat — satisfies it, which is the
9-
# accepted cost of the ruling and is stated in full in the two-clause table at
9+
# accepted cost of the ruling and is stated in full in the guarantee table at
1010
# the head of scripts/check-adr-merge-approval.mjs. The approval must be
1111
# current: a later CHANGES_REQUESTED or DISMISSED revokes it.
1212
#
13+
# Second clause (#8012): the PR must ALSO not be sitting on an armed
14+
# auto-merge. Arming is not merging — it is a standing instruction to merge
15+
# later — so no gate used to fire on it, and an AI seat armed one on a live
16+
# docs/adr/** PR at 11:15Z on 2026-08-12. The next approving review would have
17+
# turned this check green and GitHub would have merged the PR unattended, every
18+
# check reporting success. Armed is now RED whether or not the PR is approved:
19+
# the approved case is the only one in which the unattended merge actually
20+
# happens, so a rule that fired only on the unapproved case would change no
21+
# verdict at all. This is how 「人工合并」 becomes machine-enforced — the gate is
22+
# green only while the merge still requires a person to press the button.
23+
# It is not a deadlock: anyone can disable auto-merge, and doing so re-runs
24+
# this workflow (see the trigger below) and clears the check by itself.
25+
#
1326
# This supersedes the account-identity rule this workflow used to describe
1427
# (#6741 「adr 只能由维护者自己确认,人工合并,ai 不得擅自合并。」, enforced by
1528
# matching the maintainer's numeric account id). That proxy became
@@ -40,6 +53,21 @@ name: ADR Merge Approval
4053
on:
4154
pull_request:
4255
branches: [main]
56+
# `opened`/`synchronize`/`reopened` are GitHub's default set and are
57+
# RESTATED here because naming any `types:` replaces that default — dropping
58+
# one would mean no check run on that activity, and a required context that
59+
# never reports is permanent pending, not a skip (the same failure mode the
60+
# `paths:` note below describes).
61+
#
62+
# The two auto-merge activities are the #8012 addition, and they are what
63+
# make the arming clause observable at all:
64+
# auto_merge_enabled — arming is otherwise a silent state change that
65+
# fires no event this workflow listens to, so the gate would keep
66+
# reporting its last green while the PR sat armed.
67+
# auto_merge_disabled — the zero-friction exit, and the reason the arming
68+
# clause is not a deadlock: disarming re-runs the gate, which then goes
69+
# green on its own with no push, no re-review and no admin action.
70+
types: [opened, synchronize, reopened, auto_merge_enabled, auto_merge_disabled]
4371
# An approval does not fire `pull_request`, so without this trigger the
4472
# failed check would sit red after the approval lands until someone re-ran
4573
# it by hand. Subscribing to reviews makes the approval itself re-run the
@@ -63,7 +91,13 @@ concurrency:
6391

6492
permissions:
6593
contents: read
66-
# The gated path lists the PR's reviews via the REST API.
94+
# The gated path lists the PR's reviews and reads the PR itself (for
95+
# `auto_merge`) via the REST API. `read` covers both — the #8012 clause needs
96+
# no new permission and no new token scope. The PR object is read LIVE rather
97+
# than taken from the event payload: `auto_merge` is not documented as a
98+
# member of the pull_request object carried by `pull_request_review`, and a
99+
# projection that merely lacks the key would read as "not armed". The script
100+
# refuses such a payload instead; see its header.
67101
pull-requests: read
68102

69103
jobs:
@@ -97,8 +131,9 @@ jobs:
97131
# A checkout, setup-node, and one `node` call over `git diff` — no
98132
# install, no build. The self-test runs first (repo convention), then
99133
# the gate. GITHUB_TOKEN is only read on the gated path (a docs/adr/**
100-
# diff needs the PR's review list); the clean path does zero lookups.
101-
- name: Require an APPROVED review on docs/adr/** diffs
134+
# diff needs the PR's review list and arming state); the clean path does
135+
# zero lookups.
136+
- name: Require an APPROVED review and no armed auto-merge on docs/adr/** diffs
102137
env:
103138
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
104139
run: node scripts/check-adr-merge-approval.mjs --self-test && node scripts/check-adr-merge-approval.mjs

0 commit comments

Comments
 (0)