You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[governance] ADR maintainer approval is unsatisfiable on any PR the maintainer authored — GitHub forbids self-approval, so the gate is permanently red exactly when the human IS driving #8161
Filed by the domain:devx PM seat (#6023) after the maintainer stated in session on 2026-08-12 ~18:2xZ that they judge the #7960 blockage to be a CI-rule defect rather than a PR defect. Measured on origin/main; #8012 anticipated this ("probably wants its own card once graded") and this is that card. Unassigned — recording only.
The deadlock, mechanically
scripts/check-adr-merge-approval.mjs has exactly one pass condition. From decide():
latestMaintainerReviewState(reviews) walks the PR's reviews, keeps only those whose author matches MAINTAINER_APPROVERSby numeric account id, keeps only the state-setting kinds (APPROVED / CHANGES_REQUESTED / DISMISSED), and returns the last one;
the gate returns ok: trueonly when that value is APPROVED. There is no other exit that passes.
One entry. And scripts/check-required-contexts.mjs:194 records that ADR maintainer approval is a required context, put there deliberately after #6942/#6962 landed while it sat outside the required set.
Now compose that with a GitHub platform rule: an account cannot submit a review on a pull request it authored. So for any PR whose user.login is hotlong:
the only account whose approval the gate accepts is the one account GitHub will not let approve;
therefore latestMaintainerReviewState can never return APPROVED;
therefore a required context is permanently red;
therefore the PR cannot be merged through the normal path, or enter the merge queue, no matter what anyone does on it.
Live instance: PR #7960 (ADR-0001 tombstone, #7866). user.login is hotlong. The maintainer has marked it ready, has armed auto-merge, and reports that nothing they try lands it. That is not a misuse — it is the only possible outcome of the rule as written.
Why this is a rule defect and not "the gate doing its job"
The ruling the gate implements (#6741) is 「adr 只能由维护者自己确认,人工合并,ai 不得擅自合并。」 The gate does not measure that. It measures a proxy: "an APPROVED review exists from account 50353452". The proxy was sound only while maintainer-account ≠ author-account — historically true, because ADR PRs were authored by os-zhuang / os-help / os-project-manager. Cloud dev sessions in this environment now author under hotlong, so the proxy's premise is gone.
⚠️The proxy is not merely broken — it is inverted. Under the current single shared identity:
case
what the gate should do
what it does
PR authored by hotlong (the human is the one driving it)
pass once the human confirms
can never pass — self-approval is impossible
PR authored by some other account, with an AI seat holding the hotlong credential
refuse — an AI must not confirm on the maintainer's behalf
passes — the seat can submit the approving review itself
So the gate is closed on the case it was meant to admit and open on the case it was meant to stop. #8012 recorded the second row already; this card is the first. Both rows have one root cause: the human maintainer and the agent fleet are the same GitHub account, and an account-identity gate is not implementable under that condition.
Shapes a fix could take — not choosing, this is a governance surface
1. Fix the identity (closes both rows, and #8012 with them). Give the agent fleet its own GitHub account; reserve hotlong for the human. Then ADR PRs are authored by the bot, the human's approval is both possible and meaningful, and the existing gate becomes correct as written with no code change. Highest cost (org/token work, outside CI), and the only shape that restores the gate's guarantee rather than re-routing around it.
2. Admit a second maintainer signal that self-approval does not block. Keep the approval path; additionally pass when the PR author is a maintainer and a maintainer comment carries an explicit confirmation token pinned to the head SHA (e.g. a literal marker plus ac278e6). The SHA pin is load-bearing: an unpinned token survives a force-push and would confirm code nobody looked at. Under shared credentials this adds no real assurance — but it takes none away either, since row 2 shows the approval path is already reachable by a seat. Cheapest unblock; ⚠️ it also makes the gate satisfiable by an AI seat by design rather than by accident, which should be stated out loud rather than discovered later.
3. Ruleset-side bypass for this one PR. Not a fix — the next ADR PR authored by a session lands in the identical hole. Listed only so it is not mistaken for one.
⛔ What must not be done, and why it is the tempting one: "the PR author is a maintainer ⇒ pass". Every session-authored ADR PR is authored by hotlong, so that predicate is true on precisely the population the gate exists to catch. It would weld the gate permanently open while still reporting green. Naming it because it is the one-line change an implementer will reach for first.
Establishment
Gate logic read at origin/main, not from memory: MAINTAINER_APPROVERS at :101, isMaintainer at :123 (id-first comparison), latestMaintainerReviewState at :140, the single ok: true return at :190.
Required-context status read from scripts/check-required-contexts.mjs:194.
.github/workflows/adr-merge-approval.yml triggers on pull_request, pull_request_review: [submitted, edited, dismissed] and merge_group — so the gate does re-run on an approval; the re-run is not the missing piece.
Filed by the
domain:devxPM seat (#6023) after the maintainer stated in session on 2026-08-12 ~18:2xZ that they judge the #7960 blockage to be a CI-rule defect rather than a PR defect. Measured onorigin/main; #8012 anticipated this ("probably wants its own card once graded") and this is that card. Unassigned — recording only.The deadlock, mechanically
scripts/check-adr-merge-approval.mjshas exactly one pass condition. Fromdecide():latestMaintainerReviewState(reviews)walks the PR's reviews, keeps only those whose author matchesMAINTAINER_APPROVERSby numeric account id, keeps only the state-setting kinds (APPROVED/CHANGES_REQUESTED/DISMISSED), and returns the last one;ok: trueonly when that value isAPPROVED. There is no other exit that passes.scripts/check-adr-merge-approval.mjs:101:One entry. And
scripts/check-required-contexts.mjs:194records thatADR maintainer approvalis a required context, put there deliberately after #6942/#6962 landed while it sat outside the required set.Now compose that with a GitHub platform rule: an account cannot submit a review on a pull request it authored. So for any PR whose
user.loginishotlong:latestMaintainerReviewStatecan never returnAPPROVED;Live instance: PR #7960 (ADR-0001 tombstone, #7866).
user.loginishotlong. The maintainer has marked it ready, has armed auto-merge, and reports that nothing they try lands it. That is not a misuse — it is the only possible outcome of the rule as written.Why this is a rule defect and not "the gate doing its job"
The ruling the gate implements (#6741) is 「adr 只能由维护者自己确认,人工合并,ai 不得擅自合并。」 The gate does not measure that. It measures a proxy: "an APPROVED review exists from account 50353452". The proxy was sound only while maintainer-account ≠ author-account — historically true, because ADR PRs were authored by
os-zhuang/os-help/os-project-manager. Cloud dev sessions in this environment now author underhotlong, so the proxy's premise is gone.hotlong(the human is the one driving it)hotlongcredentialSo the gate is closed on the case it was meant to admit and open on the case it was meant to stop. #8012 recorded the second row already; this card is the first. Both rows have one root cause: the human maintainer and the agent fleet are the same GitHub account, and an account-identity gate is not implementable under that condition.
Shapes a fix could take — not choosing, this is a governance surface
1. Fix the identity (closes both rows, and #8012 with them). Give the agent fleet its own GitHub account; reserve
hotlongfor the human. Then ADR PRs are authored by the bot, the human's approval is both possible and meaningful, and the existing gate becomes correct as written with no code change. Highest cost (org/token work, outside CI), and the only shape that restores the gate's guarantee rather than re-routing around it.2. Admit a second maintainer signal that self-approval does not block. Keep the approval path; additionally pass when the PR author is a maintainer and a maintainer comment carries an explicit confirmation token pinned to the head SHA (e.g. a literal marker plus⚠️ it also makes the gate satisfiable by an AI seat by design rather than by accident, which should be stated out loud rather than discovered later.
ac278e6). The SHA pin is load-bearing: an unpinned token survives a force-push and would confirm code nobody looked at. Under shared credentials this adds no real assurance — but it takes none away either, since row 2 shows the approval path is already reachable by a seat. Cheapest unblock;3. Ruleset-side bypass for this one PR. Not a fix — the next ADR PR authored by a session lands in the identical hole. Listed only so it is not mistaken for one.
⛔ What must not be done, and why it is the tempting one: "the PR author is a maintainer ⇒ pass". Every session-authored ADR PR is authored by
hotlong, so that predicate is true on precisely the population the gate exists to catch. It would weld the gate permanently open while still reporting green. Naming it because it is the one-line change an implementer will reach for first.Establishment
origin/main, not from memory:MAINTAINER_APPROVERSat:101,isMaintainerat:123(id-first comparison),latestMaintainerReviewStateat:140, the singleok: truereturn at:190.scripts/check-required-contexts.mjs:194..github/workflows/adr-merge-approval.ymltriggers onpull_request,pull_request_review: [submitted, edited, dismissed]andmerge_group— so the gate does re-run on an approval; the re-run is not the missing piece.ac278e6, authorhotlong,ADR maintainer approvalred.docs/adr/**PR #6741). Auto-merge is currently armed there byhotlongand was left alone as the maintainer's own disposition — it is inert regardless, since the required context cannot go green.