fix(pm): the queue guard's narration and remedy state ruling C, not the pre-ruling landing - #18148
Conversation
…he pre-ruling landing The guard's decision already keys on an authorized APPROVED review, but the text a seat is told to consult still described the landing that preceded ruling C: the docblock called a draft awaiting the maintainer's own merge the regime's terminal state, and the printed remedy told a seat to "leave the merge to the maintainer" as the preferred way through. Ruling C (#17971, maintainer 2026-09-13) put the landing in the claiming seat's hands once an authorized approval is on record. The narration, the early-warning rendering and the refusal remedy now say that, in the charter's own words (landing-operations.md, SKILL.md), and the unapproved direct merge is kept as the other landing rather than as the only one. No decision branch changes. The self-test pins that read the remedy move with it, and three new pins assert the pre-ruling wording is absent from the printed text; 233 -> 238 cases. Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewHead: ① derived judgments (seat-measured on the fetched head
② semver: unchanged — ③ boundary flags: the dev's out-of-scope note — Implemented-by: Verdict: PASS — path face clean ⇒ this seat lands: ready through 维护者速读(终稿)受管面队列门禁的判定早就按「授权批准」走,但它打印给席位看的文字还停在旧规矩(「把合并留给维护者,人工合并即审核记录」)。这次只改叙述和打印文案,改成 ruling C 的原话:先 draft 等授权批准,批准后由认领席位 ready + auto-merge 入队;判定分支一条没动,自测 233 → 238。脚本层,本席直接落地。 Generated by Claude Code |
Fixes #18083
scripts/pm/check-governed-queue-guard.mjsdecides on an authorized APPROVED review and hassince the 2026-09-04 predicate landed. What had not moved was the text a seat is told to
consult: the docblock called a draft awaiting the maintainer's own merge the regime's terminal
state, and the printed remedy's preferred option told a seat to take the PR out of the queue
"and leave the merge to the maintainer". Since the charter chain landed (PR #18018
9489e2c0,PR #18038
c185d087, PR #18051137eb00e) the rule is ruling C — issue #17971, maintainer2026-09-13, verbatim and untranslated:
So the narration under-permitted: it told a seat not to do the thing the rule now says it
should do once an authorized approval is on record.
What moved
Wording only. ⛔ No decision branch changed —
entrySatisfied, the approval reduction, thetier split and every exit code are untouched. Anchored by content, not by line number.
now a draft waiting for an authorized approval, in the landed rule's own words from
.claude/skills/pm-dispatch/references/landing-operations.md: 「四件套留 draft 等人批,⛔ 不翻正式不入队;获授权批准后认领席落地。」verbatim, plus
SKILL.md's operational half: 「席位落地 = 过落地前检、清标、ready、auto-merge,踢出/变基同法。」The seat's ready → enqueue is now stated as the correct next act there rather than a violation.
the OTHER landing rather than the only one —
references/core-rules.md: 「受管面由维护者人合或授权批准后席位落地」.The docs(adr): ADR-0120 D4 amendment — the duplicate pre-flight is per index class #11387 measurement it cites is kept verbatim as history.
pull_requestleg's EARLY WARNING rendering. The ⛔ list (flip ready / enqueue / armauto-merge) is now explicitly conditional — "while no authorized APPROVED review is on
record", with AGENTS.md Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14's own "lift only for that approval" — and a new
✅ line says what a seat DOES do after the approval.
merge_grouprefusal remedy. Steps 1 → 2 are now an order rather than a menu:out of the queue first, then the authorized approval, and the CLAIMING SEAT lands it from
there. The unapproved direct merge (人工直合) is kept as the landing that PR still has.
asymmetry cost (was "costs one hand merge") and the exit-code precedence note.
The references tier is untouched and still says the in-seat review of record lands it: remedy
option 3 renders byte-for-byte as before, and both of its pins (offered on a references-only
refusal, withheld on a rules-layer one) still pass.
Acceptance readings
Taken on
a2e4cd7d9, in the worktree, against merge basea90a9f267.The two pre-ruling phrases, in the narration and the printed text: N → 0.
git grep -c -E 'leave the merge to the maintainer|human merge IS the review record'Both remaining hits are the new negative assertion itself — a comment and the regex literal in
⛔ a-refusal-never-tells-a-seat-to-leave-the-merge-to-the-maintainer-nor-calls-that-merge-the-record.A pin has to name what it forbids; neither is text the guard ever prints.
The landed phrase: 0 → 5 (
git grep -c 'CLAIMING SEAT lands\|claiming seat lands').Every hit of the pre-ruling wording enumerated (
git grep -n -E 'hand merge|human merge|leave the merge|人工合'),6 before → 4 after, and each remaining one accounted for:
:44「人工合并即人工审核」 (docblock) — moved.:52"the human merge IS the review record" (docblock) — moved.:536"costs one hand merge" (tier-default comment) — moved, now "costs one authorized approval".:1171「人工合并即人工审核」 (EARLY WARNING rendering) — moved.:1242"leave the merge to the maintainer. A human merge" (remedy) — moved.:2010(was:1983) the#9319replay fixture's name, quoting PR docs(pm): label writes re-read current labels in the same action — stale snapshots are invalid snapshots (#9226) #9238's own body: "a .claude/skills PR whose own body said 'awaiting a human merge'" — stays. It is a historical measurement naming what that PR said in 2026; rewriting it would falsify the fixture.:2429,:2436,:2468— new, the three negative pins (two English phrases, one 人工合并即人工审核).Self-test:
node scripts/pm/check-governed-queue-guard.mjs --self-test:: exit 0,233 cases before → 238 after (5 added, none removed, no battery floor lowered).
Diff surface:
git diff --stat a90a9f267names exactly one file,scripts/pm/check-governed-queue-guard.mjs, 104 insertions / 32 deletions.origin/mainadvanced under this worktree during the run (
a90a9f267→739ab526d, a shared-ref hazardAGENTS.md names), so the anchor above is the merge base, not the moving ref.
Governed? No —
node scripts/pm/check-governed-merges.mjs --test scripts/pm/check-governed-queue-guard.mjs:: exit 0, "NOT governed — ordinary queue landing applies". This PR is opened as a draft and the
seat lands it; nothing here flips ready or arms anything on its own.
Reverse verification — the new pins can actually fail
One-shot, from the committed state, with a
trap … EXIT INT TERMrestoring absolute paths.Predicted direction: turns red.
f7938efe94a20f34a3c1e6f07e2aaca393f16a47.observation, anchored on both texts: landed anchor 1 → 0, injected stale phrase 0 → 1;
blob
87b912dbc8acaf5af5d02fb86cc06eb2bcffd986≠ HEAD blob, so it reached disk.--self-test:: exit 1,✗ 2 of 238 case(s) failed— exactlya-refusal-orders-the-remedy-DRAFT-then-the-authorized-APPROVAL-then-the-CLAIMING-SEAT-lands-itand
⛔ a-refusal-never-tells-a-seat-to-leave-the-merge-to-the-maintainer-nor-calls-that-merge-the-record.git checkout HEAD -- FILE→ blob back tof7938efe…(byte-identical),git diff HEADempty,git status --porcelainempty, anchor restored 1, injected 0.--self-test:: exit 0, 238 cases pass.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths, three-dot against merge base
a90a9f267) — 33 commands, all run in theforeground, every exit code captured before any pipe, all
exit 0. Reconciled:Including
node scripts/pm/check-governed-queue-guard.mjs --self-test:: exit 0 andpnpm check:nul-bytes:: exit 0.pnpm check:pm-governed-merges:: exit 0 was run too, thoughthe derivation does not place it for this path — it is a
--self-test-only checker-healthfamily here, so its green grades that checker's fixtures, not this diff.
Control characters:
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'over the changed file — no hits.No changeset:
scripts/pm/**ships in no package'sfiles[], so this publishes nothing — theskip-changesetlabel carries it.Acceptance notes
Two observations outside this card's one-file surface, noted and not filed (this seat's write
budget for the round is the branch push, this PR, the label and one report comment):
scripts/pm/check-governed-merges.mjs:140 andscripts/check-required-contexts.mjs:288both still say "a human merge IS the review record" for a governed PR. Neither is wrong —
it is one of the two terminals the charter names 「终局两条:人工直合即审核记录;授权批准 ⇒ 席位落地。」 —
but neither mentions the second one. That is an omission rather than a contradiction, so it is
not the class this card is. Issue [finding] check-governed-queue-guard.mjs still prints the pre-ruling-C remedy — 「leave the merge to the maintainer; a human merge IS the review record」 — while the rule it enforces is now 「approved, then the claiming seat lands」 #18083 fences the first file off from this PR by name.
.claude/hooks/guard-governed-enqueue.sh:548 was checked and is already ruling-C shaped(its steps read draft → wait for the approval → "Then enqueue"), so it needed nothing.
Generated by Claude Code
Generated by Claude Code