Skip to content

fix(pm): the queue guard's refusal remedies send a queued PR to the maintainer's manual removal, never claiming a DRAFT conversion dequeues it - #20894

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20855-queue-guard-dequeue-remedy
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20855-queue-guard-dequeue-remedy

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #20855

Clause-②: no

What changed

scripts/pm/check-governed-queue-guard.mjs prints two refusal remedies: the governed-surface leg's (renderGuardVerdict) and the SIZE leg's (renderSizeVerdict). Both refusals fire only on the merge_group leg (guardVerdict returns warned and sizeGuardVerdict returns not-applicable on pull_request), so the pull request they name IS in the queue. Both told a seat to "take the pull request out of the queue: convert it back to DRAFT (disarming auto-merge alone does NOT dequeue it) and park it there". No seat act is measured to dequeue a queued pull request, and that includes a draft conversion (.claude/skills/pm-dispatch/references/platform-readings.md, queue membership).

Step one of both remedies now says what AGENTS.md Prime Directive #14 has said since 2d5fe76f:

  • the pull request is in the queue, and no seat act is measured to take it out, disabling auto-merge and converting it to DRAFT included;
  • ask the maintainer AT ONCE to remove it from the queue by hand;
  • disable auto-merge AND convert it back to DRAFT, which disarms it so it does not re-enter once removed;
  • confirm from the remote that it is in neither the queue nor origin/main.

The rest of each remedy is unchanged: the approval route, both landings, and the bypass-rules option. The quote 「四件套留 draft 等人批,⛔ 不翻正式不入队」 is kept, now as the place a governed PR waits once it is out of the queue. No dequeue act is named: no GraphQL mutation and no relay op. Only printed text and its self-test move. No verdict, exit code or read changes.

The pins

  • Ordering pin (moved). The governed remedy's ordering pin now checks three positions in order: the pull-back (ask the maintainer AT ONCE), then obtain an APPROVED review, then CLAIMING SEAT lands it from there. It also refuses a missing phrase. The old pin compared raw indexOf results, and a missing phrase answers -1, which sorts first.
  • New battery (floor 4). ⛔ the queued pull-back: the remedy names no dequeue act a seat has; SELF_TEST_BATTERY_FLOOR goes from 23 to 24. The battery collapses whitespace in each text before reading it, so a phrase wrapped across two array entries still counts as one. Its four cases:
    1. every governed refusal kind (unapproved, unreadable, unattributed) carries the pull-back;
    2. every size refusal kind (oversized, unreadable, no-pull) carries the same pull-back;
    3. a control: the draft-dequeue detector fires on each of four fixtures, and every spelling it lists fires on at least one fixture;
    4. the negative pin: no rendering (the six refusals and the pull_request-leg warning) matches any spelling of "a DRAFT conversion dequeues it".
  • Self-test: 292 → 296 cases, all passing.

Ablation (one-time; no permanent test file)

Both legs ran through node scripts/ablation-replace.mjs in WRAP mode, from the committed head 7f89016f9. Each leg ran inside a script with an EXIT/INT/TERM restore trap on the absolute path.

  • Leg A: the old sentence restored. The size remedy's new step one was replaced by the sentence at 4edb61449: anchor 1 → 0, blob 3f59569d5403 → 8bd07044a078, on-disk count of alone does NOT dequeue it 1 → 2. The self-test printed 2 of 296 case(s) failed: every-size-refusal-kind-carries-the-SAME-pull-back, and NO-rendering-claims-a-DRAFT-conversion-dequeues-a-queued-PR on the three size renderings. Restore: blob == HEAD 3f59569d5403, git diff HEAD empty.
  • Leg B: the negative pin alone. The old parenthesis was appended to the governed remedy, and the new text was left intact: 1 of 296 case(s) failed, the negative pin only, on the three governed renderings. Restore: blob == HEAD, git diff HEAD empty.
  • No build or dist/ is involved, because the self-test runs this script from source.

Gates, at head 7f89016f9

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) derives the same 31 commands the dispatch named. All 31 exit 0, and --ran reconciles them: 31 derived famil(ies) accounted for — 31 run, 0 NOT-MEASURED.

  • The 16 direct-node commands: check-ci-filter-parity, check-closing-keyword-parity (+ --self-test), check-comment-mask-corpus (7658 files, 0 disagree), check-declaration-mirrors (+ --self-test), check-scripts-symbol-anchors (+ --self-test), check-self-test-wired (+ --self-test), check-self-test-workflow-commands (+ --self-test), check-whole-set-label-write (+ --self-test), pm/bare-root-worklist --self-test, and pm/check-governed-queue-guard --self-test (296 cases pass).
  • pnpm check:agent-test-spelling, bash32-floor, cli-command-ids, closing-target-claim, cross-package-test-inputs, driver-memory-census, entry-guard, gitlink-declared, nul-bytes, parse-guard, pnpm-filter-targets, ratchet-remedy-authority, refd-timer-probe and watch-hint-literal.
  • pnpm check:pm-dispatch-gates ran detached: dispatch-gates self-test: 1976 cases pass, 882.7 s, exit 0.
  • NOT MEASURED locally, left to CI: the guard's own event-payload run (it is CI-measured only), 3 families that take a value from the workflow, and 1 CI job scheduled by these paths.
  • Lint, narrowed: eslint scripts/pm/check-governed-queue-guard.mjs --no-inline-config --format json read 1 file with 0 errors and 0 warnings. The effective config for this file has no parserOptions.project, and its two rules (no-restricted-imports, comment-swallow/no-code-inside-block-comment) are per-file, so this diff cannot move the verdict on any untouched file.
  • A control-byte scan of the file came back clean (grep -naP exit 1).

Publishing

scripts/pm/** ships in no package's files[], so this PR has no changeset.

Acceptance notes

  • scripts/pm/check-governed-merges.mjs:773-774 is a header comment, not printed text, and it still says "Disarming alone does not dequeue: converting the PR back to draft is what removes it from the merge queue." That file is outside this card's file surface, so it is not edited here. Carrier: none named.

Generated by Claude Code

…he maintainer's manual removal

No seat act is measured to dequeue a queued pull request, a DRAFT
conversion included, so neither refusal tells a seat to take the PR out
of the queue by converting it; both now carry the landed rule's pull-back.

Claude-Session: https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg
Co-authored-by: Claude <[email protected]>
…equeue claim in any rendering

A new self-test battery reads every refusal kind on both legs flattened:
each carries the maintainer's manual removal, the disarm and the remote
confirmation, and no rendering matches a spelling of "a DRAFT conversion
dequeues it" (with a control that the detector fires on each spelling).
The governed remedy's ordering pin now reads the pull-back position and
refuses a missing phrase instead of sorting -1 first.

Claude-Session: https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg
Co-authored-by: Claude <[email protected]>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 30, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 16:36
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit da93a8b Sep 30, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20855-queue-guard-dequeue-remedy branch September 30, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants