4040 * `pull_request` → the same finding is an EARLY WARNING that exits 0.
4141 *
4242 * The PR run must not redden, and not for politeness. A governed PR sitting as
43- * a draft awaiting the maintainer's own merge is the CORRECT terminal state of
44- * the regime — that is what "人工合并即人工审核" means — so a check that is red
45- * on it is red on the healthy case, forever, which is precisely the poison the
46- * 2026-08-18 ruling named. The queue build, by contrast, is a state a governed
47- * PR should never be in at all; red there is red on the anomaly.
43+ * a draft while it waits for an authorized approval is the CORRECT resting
44+ * state of the regime — 「四件套留 draft 等人批,⛔ 不翻正式不入队;获授权批准后
45+ * 认领席落地。」 — so a check that is red on it is red on the healthy case,
46+ * forever, which is precisely the poison the 2026-08-18 ruling named. The queue
47+ * build, by contrast, is a state an UNAPPROVED governed PR should never be in
48+ * at all; red there is red on the anomaly.
49+ *
50+ * ⭐ AND WHAT HAPPENS AFTER THE APPROVAL IS THIS CHECK'S OWN PASS PATH, which
51+ * is why the queue leg is not only a refusal. Ruling C (#17971, maintainer
52+ * 2026-09-13, verbatim 「C. approve 后不管后续改动都由席位落地:」) puts the
53+ * landing in the CLAIMING SEAT's hands once an authorized APPROVED review is on
54+ * record — 「席位落地 = 过落地前检、清标、ready、auto-merge,踢出/变基同法。」 — so
55+ * the seat's ready → enqueue is the CORRECT next act there rather than a
56+ * violation, and an ejection or a rebase costs no second click.
4857 *
4958 * ⚠️ The consequence is worth stating out loud rather than discovering: this
50- * guard CANNOT stop a maintainer merging a governed PR by hand , and does not
59+ * guard CANNOT stop a maintainer merging a governed PR directly , and does not
5160 * try. A direct merge produces no `merge_group` event. Under this regime that
52- * is not a hole — the human merge IS the review record (measured: #11387 was
61+ * is not a hole — it is the OTHER landing, the one an UNAPPROVED governed PR
62+ * still has: 「受管面由维护者人合或授权批准后席位落地」 (measured: #11387 was
5363 * read as an incident for 13 minutes on exactly this confusion, until the
54- * maintainer answered 「是我合并的」). What this guard closes is the seat path:
55- * flip ready → enqueue → the queue is the entire review.
64+ * maintainer answered 「是我合并的」). What this guard closes is the seat path
65+ * taken with NEITHER of those: flip ready → enqueue → the queue is the entire
66+ * review.
5667 *
5768 * ## What satisfies it — an AUTHORIZED approval, on ANY commit
5869 *
@@ -533,7 +544,9 @@ export const REFERENCES_TIER_PREFIX = '.claude/skills/pm-dispatch/references/';
533544 * The two landing tiers. `rules` is the DEFAULT in every ambiguous case,
534545 * because the two are not symmetric: reading a rules-layer path as references
535546 * lands a maintainer-owned file on a seat's own review, while reading a
536- * references path as rules costs one hand merge.
547+ * references path as rules costs one authorized approval — the maintainer is
548+ * asked to look at a pull request they need not have, and the claiming seat
549+ * lands it from there.
537550 */
538551export const TIER_RULES = 'rules' ;
539552export const TIER_REFERENCES = 'references' ;
@@ -948,9 +961,10 @@ export function guardVerdict({ event, governed = [], unattributed = [], approval
948961 if ( entries . length === 0 && unattributed . length === 0 ) {
949962 return { ...base , conclusion : 'clear' , exitCode : EXIT_CLEAR , refusalKind : null } ;
950963 }
951- // The early-warning run never reddens: a governed PR awaiting the
952- // maintainer's own merge is the regime's healthy terminal state, and a check
953- // that is red on the healthy case is the retired gate rebuilt (see header).
964+ // The early-warning run never reddens: a governed PR parked in draft while it
965+ // waits for an authorized approval is the regime's healthy resting state, and
966+ // a check that is red on the healthy case is the retired gate rebuilt (see
967+ // header). What follows that approval is ruling C: the claiming seat lands it.
954968 if ( event !== EVENT_MERGE_GROUP ) {
955969 return { ...base , conclusion : 'warned' , exitCode : EXIT_CLEAR , refusalKind : null } ;
956970 }
@@ -1167,13 +1181,21 @@ export function renderGuardVerdict(verdict) {
11671181 if ( verdict . conclusion === 'warned' ) {
11681182 lines . push (
11691183 ' ⚠️ EARLY WARNING, not a failure — this run is on the pull request, and this check is deliberately' ,
1170- ' GREEN here. A governed PR held as a draft for the maintainer to merge by hand IS the regime\'s' ,
1171- ' healthy end state (「人工合并即人工审核」), and a check that reddens on the healthy case is the' ,
1172- ' permanently-red gate the 2026-08-18 ruling retired (红灯常态化本身有毒).' ,
1184+ ' GREEN here. A governed PR parked in draft while it waits for an authorized approval IS the' ,
1185+ ' regime\'s healthy resting state (「四件套留 draft 等人批,⛔ 不翻正式不入队」), and a check that' ,
1186+ ' reddens on the healthy case is the permanently-red gate the 2026-08-18 ruling retired' ,
1187+ ' (红灯常态化本身有毒).' ,
11731188 '' ,
1174- ' ⛔ What a seat must NOT do with this PR: flip it ready, enqueue it, or arm auto-merge' ,
1175- ' (AGENTS.md Prime Directive #14). One governed path governs the whole PR —' ,
1176- ' 「混合 diff 一条命中即整 PR 分叉」; proportion is not a question.' ,
1189+ ' ⛔ What a seat must NOT do with this PR while no authorized APPROVED review is on record:' ,
1190+ ' flip it ready, enqueue it, or arm auto-merge (AGENTS.md Prime Directive #14 — its four' ,
1191+ ' prohibitions lift for that approval and for nothing else). One governed path governs the' ,
1192+ ' whole PR — 「混合 diff 一条命中即整 PR 分叉」; proportion is not a question.' ,
1193+ '' ,
1194+ ` ✅ What a seat DOES do once an account in GOVERNED_APPROVERS (${ GOVERNED_APPROVERS . join ( ', ' ) } ) has APPROVED it,` ,
1195+ ' on ANY commit: the CLAIMING SEAT lands it — ruling C (#17971, maintainer 2026-09-13, verbatim' ,
1196+ ' 「C. approve 后不管后续改动都由席位落地:」), 「席位落地 = 过落地前检、清标、ready、' ,
1197+ ' auto-merge,踢出/变基同法。」 Unapproved, the maintainer\'s own direct merge (人工直合) is' ,
1198+ ' the only landing this pull request has.' ,
11771199 '' ,
11781200 ' If it IS enqueued anyway, the merge-queue run of this same check will REFUSE it unless every' ,
11791201 ' governed pull request above carries an APPROVED review by then.' ,
@@ -1238,14 +1260,18 @@ export function renderGuardVerdict(verdict) {
12381260 lines . push (
12391261 '' ,
12401262 ' What satisfies this check:' ,
1241- ' 1. ⭐ PREFERRED — take the pull request out of the queue: convert it back to DRAFT (disarming' ,
1242- ' auto-merge alone does NOT dequeue it), and leave the merge to the maintainer. A human merge' ,
1243- ' IS the review record for a governed surface; that is the regime, not a workaround of it.' ,
1244- ` 2. Or: obtain an APPROVED review by an authorized approver (GOVERNED_APPROVERS: ${ GOVERNED_APPROVERS . join ( ', ' ) } )` ,
1245- ' on each governed PR, then re-queue. It does NOT have to sit on the current head sha, and a later' ,
1246- ' push does not expire it (2026-09-04: 「只需要有人工批准记录就行,不需要卡最新的提交。」); the' ,
1247- ' authorized set is still the 2026-08-27 one (「os-zhuang hotlong 批准算数」). ⛔ An agent seat' ,
1248- ' never submits that approval, under any account — the post-merge audit reads the approver too.' ,
1263+ ' 1. ⭐ FIRST, whatever comes after it — take the pull request out of the queue: convert it back to' ,
1264+ ' DRAFT (disarming auto-merge alone does NOT dequeue it) and park it there. 「四件套留 draft 等人' ,
1265+ ' 批,⛔ 不翻正式不入队」 — parked outside the queue is the SAFE state, not a stalled one.' ,
1266+ ` 2. Then: obtain an APPROVED review by an authorized approver (GOVERNED_APPROVERS: ${ GOVERNED_APPROVERS . join ( ', ' ) } )` ,
1267+ ' on each governed PR, and the CLAIMING SEAT lands it from there — ruling C (#17971, maintainer' ,
1268+ ' 2026-09-13, verbatim 「C. approve 后不管后续改动都由席位落地:」), 「席位落地 = 过落地前' ,
1269+ ' 检、清标、ready、auto-merge,踢出/变基同法。」 The approval does NOT have to sit on the current' ,
1270+ ' head sha, and a later push does not expire it (2026-09-04: 「只需要有人工批准记录就行,不需' ,
1271+ ' 要卡最新的提交。」); the authorized set is still the 2026-08-27 one (「os-zhuang hotlong 批准' ,
1272+ ' 算数」). ⛔ An agent seat never submits that approval, under any account — the post-merge audit' ,
1273+ ' reads the approver too. ⛔ Unapproved, the maintainer\'s own direct merge (人工直合) is the only' ,
1274+ ' landing this pull request has.' ,
12491275 ) ;
12501276 if ( verdict . entries . some ( ( e ) => e . record !== undefined ) ) {
12511277 lines . push (
@@ -1930,10 +1956,11 @@ async function main() {
19301956 }
19311957 // BOTH legs are always evaluated and BOTH blocks are always printed, so no
19321958 // reading is lost whichever refuses. The governed refusal wins the exit code
1933- // when both fire, because its remedy is the stricter of the two (the
1934- // maintainer's own merge, Prime Directive #14) and it subsumes the carrier's
1935- // "take it out of the queue". ⛔ The carrier code is not swallowed silently —
1936- // its block states the refusal in full either way.
1959+ // when both fire, because its remedy is the stricter of the two (no landing at
1960+ // all without either an authorized approval or the maintainer's own direct
1961+ // merge, Prime Directive #14) and it subsumes the carrier's "take it out of
1962+ // the queue". ⛔ The carrier code is not swallowed silently — its block states
1963+ // the refusal in full either way.
19371964 return verdict . exitCode !== EXIT_CLEAR ? verdict . exitCode : carrier . exitCode ;
19381965}
19391966
@@ -2387,7 +2414,33 @@ export async function selfTest() {
23872414 assert ( 'a-refusal-names-the-exact-paths-that-matched' , refusalText . includes ( 'AGENTS.md' ) , refusalText ) ;
23882415 assert ( 'a-refusal-names-the-pull-request' , refusalText . includes ( '#9527' ) , refusalText ) ;
23892416 assert ( 'a-refusal-states-what-would-satisfy-it' , / W h a t s a t i s f i e s t h i s c h e c k / . test ( refusalText ) && / D R A F T / . test ( refusalText ) && / A P P R O V E D r e v i e w / . test ( refusalText ) , refusalText ) ;
2390- assert ( 'a-refusal-names-the-preferred-remedy-first-and-it-is-DEQUEUE-not-approve' , refusalText . indexOf ( 'DRAFT' ) < refusalText . indexOf ( 'obtain an APPROVED review' ) , refusalText ) ;
2417+ // ⭐ The remedy is an ORDER, not a menu, and the order is the landed rule's:
2418+ // out of the queue first, then the authorized approval, then the claiming
2419+ // seat lands it (ruling C, #17971). The pin reads all three positions rather
2420+ // than the first two, because a remedy that stopped at the approval would
2421+ // leave a seat waiting for a merge nobody is going to perform.
2422+ assert (
2423+ 'a-refusal-orders-the-remedy-DRAFT-then-the-authorized-APPROVAL-then-the-CLAIMING-SEAT-lands-it' ,
2424+ refusalText . indexOf ( 'DRAFT' ) < refusalText . indexOf ( 'obtain an APPROVED review' ) &&
2425+ refusalText . indexOf ( 'obtain an APPROVED review' ) < refusalText . indexOf ( 'CLAIMING SEAT lands it from there' ) ,
2426+ refusalText ,
2427+ ) ;
2428+ // ⛔ The pre-ruling-C remedy, pinned in the REFUSING direction. This file used
2429+ // to tell a seat to leave the merge to the maintainer and to call that merge
2430+ // the review record; ruling C moved the landing to the claiming seat, so the
2431+ // old wording now under-permits the text a seat is told to act on. Both
2432+ // spellings are asserted ABSENT — a negative is the only pin that catches a
2433+ // paragraph drifting back.
2434+ assert (
2435+ '⛔ a-refusal-never-tells-a-seat-to-leave-the-merge-to-the-maintainer-nor-calls-that-merge-the-record' ,
2436+ ! / l e a v e t h e m e r g e t o t h e m a i n t a i n e r / . test ( refusalText ) && ! / h u m a n m e r g e I S t h e r e v i e w r e c o r d / . test ( refusalText ) ,
2437+ refusalText ,
2438+ ) ;
2439+ assert (
2440+ 'a-refusal-quotes-ruling-C-verbatim-and-keeps-the-unapproved-direct-merge-as-the-other-landing' ,
2441+ refusalText . includes ( 'C. approve 后不管后续改动都由席位落地:' ) && refusalText . includes ( '人工直合' ) ,
2442+ refusalText ,
2443+ ) ;
23912444 assert ( 'a-refusal-forecloses-the-edit-the-check-remedy' , / N e i t h e r o f t h o s e i s " e d i t t h i s c h e c k " / . test ( refusalText ) , refusalText ) ;
23922445 assert ( 'a-refusal-carries-the-runnable-derivation-command' , refusalText . includes ( 'check-governed-merges.mjs --test' ) , refusalText ) ;
23932446 const clearText = renderGuardVerdict ( clearV ) ;
@@ -2396,6 +2449,25 @@ export async function selfTest() {
23962449 const warnText = renderGuardVerdict ( warnedV ) ;
23972450 assert ( 'the-warning-says-out-loud-that-it-is-deliberately-green' , / E A R L Y W A R N I N G / . test ( warnText ) && / G R E E N h e r e / . test ( warnText ) , warnText ) ;
23982451 assert ( 'the-warning-tells-a-seat-what-not-to-do' , / f l i p i t r e a d y , e n q u e u e i t , o r a r m a u t o - m e r g e / . test ( warnText ) , warnText ) ;
2452+ // ⭐ ... and says out loud that the prohibition is CONDITIONAL. Prime Directive
2453+ // #14's four lift for an authorized approval, and ruling C puts what follows
2454+ // in the claiming seat's hands; an unconditional ⛔ here would tell a seat to
2455+ // sit on a PR the rule says it should land.
2456+ assert (
2457+ 'and-the-prohibition-is-CONDITIONAL-on-there-being-no-authorized-approval-yet' ,
2458+ / w h i l e n o a u t h o r i z e d A P P R O V E D r e v i e w i s o n r e c o r d / . test ( warnText ) && / p r o h i b i t i o n s l i f t f o r t h a t a p p r o v a l / . test ( warnText ) ,
2459+ warnText ,
2460+ ) ;
2461+ assert (
2462+ 'and-the-warning-says-what-a-seat-DOES-do-after-that-approval-the-claiming-seat-lands-it' ,
2463+ / C L A I M I N G S E A T l a n d s i t / . test ( warnText ) && warnText . includes ( 'C. approve 后不管后续改动都由席位落地:' ) ,
2464+ warnText ,
2465+ ) ;
2466+ assert (
2467+ '⛔ and-it-no-longer-calls-a-maintainer-hand-merge-the-regimes-healthy-END-state' ,
2468+ ! / 人 工 合 并 即 人 工 审 核 / . test ( warnText ) && / 四 件 套 留 d r a f t 等 人 批 / . test ( warnText ) ,
2469+ warnText ,
2470+ ) ;
23992471 assert ( 'the-warning-forecasts-the-queue-refusal' , / w i l l R E F U S E i t / . test ( warnText ) , warnText ) ;
24002472 assert ( 'an-outstanding-changes-request-is-reported-even-though-it-does-not-flip-the-verdict' , / C H A N G E S _ R E Q U E S T E D f r o m : b / . test ( renderGuardVerdict ( run ( 'merge_group' , [ row ( 1 , [ 'AGENTS.md' ] ) ] , new Map ( [ [ 1 , approvalVerdict ( [ ...approved ( 'a' ) , { state : 'CHANGES_REQUESTED' , user : { login : 'b' } } ] ) ] ] ) ) ) ) ) ;
24012473 // Every refusal kind renders a distinct, actionable sentence — a shared
0 commit comments