Skip to content

Propagate the 2026-09-04 unpinned-approval ruling into the governed surfaces: the enqueue hook, its self-test (RED in CI), AGENTS.md and the PM skill #15209

Description

@claude

Found by the os-dev seat while implementing #15187 (guard half in PR #15203). Not filed as a drive-by: it is the half of that ruling which lands in governed files, and PR #15203 cannot go green without it.

The 2026-09-04 maintainer ruling — verbatim and untranslated — 「你的门禁有问题,只需要有人工批准记录就行,不需要卡最新的提交。」 — retires the sha pin on the governed merge-queue approval predicate. #15203 lands that in scripts/pm/check-governed-queue-guard.mjs. Four governed surfaces still state the retired rule, and one of them is a CI gate that now reds.

1. .claude/hooks/guard-governed-enqueue.selftest.sh — RED in CI today

lint.yml discovers and runs every .claude/hooks/*.selftest.sh as the job Claude hook guard self-tests. Two of its cases pin the retired pin:

FAIL want=block got=allow  a STALE approval (approved an earlier head) never counts
FAIL missing "STALE approval"  a stale approval is reported as stale, not as absent

Measured by ablation on the branch of PR #15203 — the pre-ruling guard restored on disk, hook self-test re-run, then restored from HEAD:

tree hook self-test
pre-ruling guard 49 passed, 0 failed
with the ruling 47 passed, 2 failed

Both cases must flip direction: an authorised approval on an earlier head now ALLOWS.

2. .claude/hooks/guard-governed-enqueue.sh — wrong advice, and the deferred rename

Its runtime verdict is already correct without any edit — it imports the predicate, so it inherits the ruling (measured: an authorised approval on an older commit now returns state: approved, and the hook exits 0). Two things still need the edit:

  • The blocking message tells a seat: "A push after the approval unpins it — re-request, do not re-enqueue." That is now false and sends seats to do work the gate no longer asks for. The surrounding lines ("no APPROVED review pinned to its current head", "Wait for the APPROVED review to land on THIS head sha") are wrong in the same way.
  • The symbol pinnedApprovalVerdict names something it no longer does. PR fix(pm): an authorised APPROVED review satisfies the queue guard on any commit (2026-09-04 ruling) #15203 deliberately keeps the name, because this hook imports it and a rename there alone would break a seat-side guard into silent fail-open. The rename belongs in the same commit as this hook's call site — authorizedApprovalVerdict matches the file's own unauthorizedApprovers spelling. staleApprovers is retained by that PR as a printed reading (authorised approvals given on a commit that is no longer the head, which now also count), so this hook's detail line stays valid but becomes unreachable on the blocking path.

3. AGENTS.md — the governed-landing paragraph

Still reads: "Those four lift only for an authorized approval pinned to the current head (2026-08-27; guard 2026-08-28): APPROVED, by an account in GOVERNED_APPROVERS …, commit_id = that sha … and any later push expires it." The approver set half stands; the pin half and "any later push expires it" do not.

4. .claude/skills/pm-dispatch/SKILL.md

Still reads: 「APPROVED 且 commit_id = PR 当前 head 才算,批准后再推提交即过期转红」. Same correction. This one matters twice over: it is the text every later PM dispatch reads, so a stale rule there propagates into work nobody has started yet.

Landing note

All four paths are governed (.claude/**, AGENTS.md), so a PR carrying them is a governed-surface PR: no seat flips it ready, enqueues it or arms auto-merge. It needs the maintainer's own merge, or one authorised approval — which, once #15203 lands, no longer expires on a later push.

⚠️ Ordering: this and PR #15203 have to land together or this one first. #15203 alone leaves Claude hook guard self-tests red on main.


Generated by Claude Code

Activity

  1. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 5 — folded into the #15187 flight (claim 5536163108; session session_019RfFHiRCSs3JXLK4cwcfox; branch claude/issue-15187-queue-guard-unpinned-approval; worktree objectstack-issue-15187; mode:subagent, model: opus; Clause-②: no). Seat decision 06:11Z on the dev's open question: A — PR #15203 is extended into the four governed paths this card names and lands as ONE governed PR (Fixes #15187 + Fixes #15209; draft, both approvers requested by the seat, human merge), so main never carries a red hook self-test between two landings. Graded priority:p2, domain:skills, tooling, state pm:dispatched. Applied and read back after this comment.


    Generated by Claude Code

  2. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Folded into PR #15203 by seat decision, rather than landed separately.

    The reasoning that settled it: this issue and the guard change are one contract, not two — the enqueue hook IMPORTS the predicate, so splitting them across two PRs leaves a window in which main is red whichever one lands first (the guard alone reddens Claude hook guard self-tests; this one alone asserts a behaviour the guard on main does not yet have). Landing them together has no such window, and the pinnedApprovalVerdict rename travels with its only importer.

    All four items are in that PR:

    1. .claude/hooks/guard-governed-enqueue.selftest.sh — the two cases flipped to the ruled direction; the fixture governed-stale became governed-approved-on-an-earlier-commit. Self-test now reads 50 passed, 0 failed.
    2. .claude/hooks/guard-governed-enqueue.sh — the blocking message no longer says a push unpins the approval, and both renames landed here with their importer (pinnedApprovalVerdict to authorizedApprovalVerdict, staleApprovers to approvalsOnEarlierCommits).
    3. AGENTS.md — the governed-landing paragraph now states the ruled predicate, with the ruling quoted verbatim.
    4. .claude/skills/pm-dispatch/SKILL.md — the ACCEPT-fork clause likewise.

    PR #15203 is therefore a governed-surface PR and stays a DRAFT: no seat flips it ready, enqueues it or arms auto-merge. The seat requests os-zhuang and hotlong; the maintainer lands it.


    Generated by Claude Code

  3. claude commented on Sep 4, 2026

    @claude
    ContributorAuthor

    Reviewed under #15187's verdict: PR #15203 (head 0a8763f6) carries the four governed-path halves this card asked for — hook self-test (50 passed, 0 failed, sha-pin fixture flipped to ALLOW), hook prose with the pinnedApprovalVerdict → authorizedApprovalVerdict rename, AGENTS.md and SKILL.md stating the 2026-09-04 predicate. ACCEPT, governed terminal: draft PR, both authorized approvers requested, human landing. The PR body closes this card with #15187.

    Implemented-by: claude/issue-15187-queue-guard-unpinned-approval
    Reviewed-by: session_019RfFHiRCSs3JXLK4cwcfox


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions