Skip to content

Commit 2d5fe76

Browse files
docs(agents,pm-dispatch): a draft conversion drops only an unqueued PR's auto-merge; a queued governed pull-back goes to the maintainer (#20854)
Fixes #20847 Clause-②: no ## What changes Four sentences stop saying that a draft conversion, or draft plus an auto-merge disable, removes a PR from the merge queue. Each keeps the half that holds: on an **unqueued** PR, the conversion drops auto-merge. Prime Directive 14 now names the one removal act that is measured, the maintainer's manual removal, and says why a seat cannot do it. | site | before | after | |:--|:--|:--| | `AGENTS.md` Prime Directive 14, "Already armed or queued" (was `:285-:287`, now `:285-:288`) | Convert it back to **draft** AND disable auto-merge — draft is what removes queue membership, disabling alone drops only the arming — then confirm from the remote that it is in neither the queue nor `origin/main`. | Disable auto-merge AND convert it back to **draft**, which disarms an unqueued PR; no seat act, those two included, is measured to dequeue one (`platform-readings.md`, queue membership), so ask the maintainer at once to remove a queued PR by hand — then confirm from the remote that it is in neither the queue nor `origin/main`. | | `AGENTS.md` "State on your PR" (`:428-:429` → `:429-:430`) | the draft flag, which flipped back destroys auto-merge and queue membership at once | the draft flag, which flipped back destroys an unqueued PR's auto-merge at once | | `AGENTS.md` multi-agent §7 (`:532-:533` → `:533-:534`) | **flipping back to draft drops auto-merge and queue membership at once**, and neither returns by itself | **flipping back to draft drops an unqueued PR's auto-merge at once**, and it does not return by itself | | `.claude/skills/pm-dispatch/SKILL.md:202` | 已入队才读到本条 ⇒ 转 draft 与 disable 都做;出队以阳性探针答,ref 缺席只旁证。 | 已入队 ⇒ 转 draft 并 disable,即请维护者手动出队(席位无实测手段);出队以阳性探针答。 | Every clause that still holds is kept: "**Draft is no barrier by itself — the barrier is this directive**", "confirm from the remote that it is in neither the queue nor `origin/main`", "ready *first*, arm *second*", and the positive-probe reading of a dequeue. The rule text cites `platform-readings.md` and restates no reading. The readings themselves are in PR #20845 (issue #20764). **The grep.** `AGENTS.md` and every file under `.claude/**` were searched for other copies of the claim: `queue membership`, draft next to queue or auto-merge, 转 draft, 出队, 踢队, dequeue, evict. The four sites above are all of them. `references/platform-readings.md:41-:43` is PR #20845's, and this PR leaves it alone. `lanes/cli.md:17` only points at that file. ## Is there a seat act that removes a queued PR? (the claim's measurement) Every step was read-only. ⛔ No mutation was called and no PR was dequeued. 1. **Live GraphQL introspection was refused.** The request was `POST https://api.github.com/graphql` with `query { __type(name: "Mutation") { fields { name description } } }`. It returned **403** from this session's egress, with the message "GitHub GraphQL is not available from Claude Code sessions". The same message lists the pull-request routes the egress does serve: `ccr/auto_merge` (PUT/DELETE), `ccr/ready_for_review` and `ccr/convert_to_draft`. None of them dequeues. 2. **GitHub's published GraphQL schema does declare the mutation.** This is a document reading, not a live introspection. The source is `octokit/graphql-schema` `schema.graphql` on `main`, fetched 2026-09-30T12:18Z (1,223,842 bytes, sha256 prefix `3c62d0526d133cee`). It declares `dequeuePullRequest(input: DequeuePullRequestInput!)` with the description "Remove a pull request from the merge queue." Its input is `id: ID!`, the pull request id. 3. **REST has no dequeue endpoint.** In `github/rest-api-description`, `api.github.com.json` (info version 1.1.4, `main`), no path contains `queue`. The word "dequeue" appears only in the `pull_request.dequeued` webhook schemas. 4. **GitHub's own docs list the ways a PR leaves the queue.** The source is `github/docs` on `main` (`data/reusables/pull_requests/merge-queue-removal-reasons.md`). The list has four entries: CI failure, timeout, "User requesting a removal via the API or merge queue interface", and a branch-protection failure. Neither a draft conversion nor a close is on it. The how-to page (`merging-a-pull-request-with-a-merge-queue.md`) removes a PR through the web page's **Remove from queue** button and says the CLI cannot do it. 5. **The relay cannot carry it.** `scripts/pm/fleet-write/ops.mjs` at `05a7547c9` has 15 ops: comment, comment_edit, labels_add, labels_remove, assign, unassign, issue_patch, issue_create, pr_create, pr_request_reviewers, pr_ready, pr_draft, automerge_enable, automerge_disable and transfer. `ALLOWED_MUTATIONS` has five entries, and `dequeuePullRequest` is not one of them. ⇒ **No dequeue act is measured to be available to a seat.** The platform documents one (the GraphQL mutation), but neither this session's egress nor the relay can reach it. So the rule sends a queued pull-back to the maintainer and says why. The triage direction on the card reserves the next step for the triage seat: whether to file a tooling child for a relay dequeue op. That would be a new relay capability. ## Coordination with PR #20845 The new rule cites the queue-membership section of `platform-readings.md`. At `origin/main` (`05a7547c9`), line `:42` of that section still carries the old reading, and PR #20845 rewrites `:41-:43`. Until #20845 lands, the rule and line `:42` disagree. #20845 should land first, or with this PR. The citation names a file section, not a PR, so it is correct once both have landed, whatever the order. ## Line budget - `AGENTS.md`: 1107 → **1108** lines, against a ceiling of 1116 (headroom 9 → 8). The +1 is all in Prime Directive 14. The new sentence adds the maintainer route and the reason for it, and it needs four lines where the old one took three. The other two `AGENTS.md` sentences are net 0. - `pm-dispatch/SKILL.md`: 319 → **319** lines (ceiling 319, headroom 0). Line `:202` is edited in place and goes from 107 to 116 bytes. - Every added line is at most 120 bytes: 118 / 110 / 120 / 118 (Prime Directive 14), 83 / 87 ("State on your PR"), 88 / 88 (§7) and 116 (`SKILL.md`). `check:pm-skill-ratchet` reports no over-length line and leaves the table-row pins unchanged (768 and 342). ## Deviations from the dispatch 1. **`SKILL.md:202` drops two parts.** They are the clause 「ref 缺席只旁证」 and the 「才读到本条」 of the trigger. The 120-byte cap left 13 bytes of room on the line, and the maintainer route with its reason needs about 50. The dropped clause is kept in `platform-readings.md:44` (「ref 缺席只作旁证,⛔ 永不承载结论」), and the positive-probe half of the line stays. 2. **`AGENTS.md` is net +1, not ≤ 0.** None of the three sentences frees a whole line by deleting content. Freeing one would need a re-wrap of text this PR does not otherwise change. The pin is not exceeded. 3. **The introspection is a schema-document reading.** The live endpoint refused this session (item 1 above). ## Acceptance notes - **Same family, outside this PR's scope (the dispatch rules out any script edit).** `scripts/pm/check-governed-queue-guard.mjs` makes the same claim in both of its refusal texts, at `:1451-:1452` and `:1911-:1912` (at `05a7547c9`): "take the pull request out of the queue: convert it back to DRAFT (disarming auto-merge alone does NOT dequeue it)". Parking the PR in draft after the guard's red is still useful: an armed PR re-enters the queue, and a draft cannot be armed. The part in parentheses is the falsified premise. This is reported to the seat so it can join the card's family. No card is filed. - **An unmeasured candidate, for triage.** Closing the PR is an act a seat has, through the relay's `issue_patch` with `state: closed`. A code comment in `.github/workflows/cross-repo-issue-closer.yml:399-:401` says that closing drops queue membership. GitHub's list of removal reasons does not name it, and I know of no reading of it in this repo. It was not attempted. - **Changeset.** None. `.claude/**` and `AGENTS.md` are not in any package's `files[]`, so this PR publishes nothing. `skip-changeset` is for the seat to apply. - **Landing.** This is a Tier H governed surface (`AGENTS.md`). The PR stays a draft until an authorized approval. ## Verification (head `b095ba02e`) - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`, with no paths, derived 22 commands for this change set (2 paths against merge base `05a7547c9`). All 22 exited 0: - `check-closing-keyword-parity`, plus its `--self-test`; - `check-comment-mask-corpus`; - `check-governed-queue-guard --self-test` and `check-harness-current --self-test`; - `@objectstack/lint check:doc-formula-expressions`, run after building `@objectstack/lint` and its dependency closure under `os-verify-lock.sh` (`VERDICT command-exit 0`); - the `check:` family: `agent-test-spelling`, `cross-package-test-inputs`, `doc-authoring`, `docs-audit-scope`, `driver-memory-census`, `gitlink-declared`, `nul-bytes`, `pm-expected-skips`, `pm-governed-merges`, `pm-governed-prose`, `pm-skill-id-lint`, `pm-skill-ratchet`, `refd-timer-probe`, `required-contexts`, `skill-frame-sync` and `watch-hint-literal`. - `pnpm check:pm-settings-deny-roster` was also run, because its roster lives under `.claude`. It exited 0. - The `--ran` reconciliation read "22 derived, 22 run, 0 NOT-MEASURED, 0 UNRUN". All 22 commands recorded an exit code. - Readings from the gates: - `check:pm-skill-ratchet`: "AGENTS.md is 1108 lines (ceiling 1116; headroom 8)" and "pm-dispatch/SKILL.md is 319 lines (ceiling 319; headroom 0)". - `check:pm-skill-id-lint`: "34 file(s) clean". - `check:pm-governed-prose`: "2 instruction surface(s) name all 6 registered governed surfaces". - NOT MEASURED: - `check-required-contexts --verify-required-set`, which reads the workflow event and so runs only in CI; - the four CI type-check lanes. This diff touches no TypeScript. ## 维护者速读(草稿) - **改了什么**:`AGENTS.md` 三句、`pm-dispatch/SKILL.md` 一行。旧文说「转 draft 会把 PR 踢出合并队列」,现在都不再这么说。改后:转 draft 只在**未入队**时掉 auto-merge。已入队的受管 PR 要撤回时,两手照做,并**立即请你手动出队**。 - **为什么改**:实测里 4 个已入队后转 draft 的 PR 都没有因此出队,其中 2 个带着 draft 被队列直接合入,读数在 PR #20845。「转 draft 能出队」的说法写在第 14 条指令里,那是防止坏合并的撤回手段,而这个手段实际不起作用。**出队手段问题**:目前席位**没有**经实测可用的出队手段。GitHub 公开 schema 里有 GraphQL `dequeuePullRequest`,但本会话出口拒绝 GraphQL(403),中继也没有这个 op。所以规则暂时把已入队的撤回交给你手动出队。要不要给中继加出队 op,属于新能力,由分诊决定是否立子卡。子卡立了,你会看到裁决请求。 - **风险与代价(含回滚)**:只改规则文字,不改代码。代价是已入队的撤回要等你点「Remove from queue」。队列落地约需 15–30 分钟,所以需要你及时响应。PR #20845 应先合或同合,否则被引用的读数第 42 行暂时和本规则说法相反。回滚就是 revert 这一个 PR。 - **席位意见**: - **你要做的**:审阅后批准本 PR(或直接合并)。 --- _Generated by [Claude Code](https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1d20245 commit 2d5fe76

2 files changed

Lines changed: 9 additions & 8 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
199199
- 该账户改为把 PR assign 给它替代通知,另一账户照常请审;轮次报告点名说明走了兜底。
200200
- 请审走免碰 draft 位的 REST 专用路,ready/draft 走中继 op;MCP 兜底已拒;端点见 rest-channel。
201201
- ④ 轮次报告单列 awaiting a human merge。
202-
- 已入队才读到本条 ⇒ 转 draft 与 disable 都做;出队以阳性探针答,ref 缺席只旁证。
202+
- 已入队 ⇒ 转 draft 并 disable,即请维护者手动出队(席位无实测手段);出队以阳性探针答。
203203
- skills 车道自有 PR:纯代码面如 `scripts/pm/` 由本席按达档自审(清单不减)后落地。
204204
- 受管面两层:Tier S = `.claude/**` 全树,余皆 Tier H 等人批;S 经达档复核 PASS 后 ready → 入队。
205205
- 路径面干净的才转 ready → 入队;队列是唯一被认可的落地路径,⛔ 永不队列外合并。

‎AGENTS.md‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -282,9 +282,10 @@ localStorage / auth gotchas.
282282
governed-surface PR, under any account** — an authorized account is agent-operated too; "CI is green" carries no
283283
information about a governance change.
284284

285-
**Already armed or queued when you read this?** Convert it back to **draft** AND disable auto-merge — draft is
286-
what removes queue membership, disabling alone drops only the arming — then confirm from the remote that it is in
287-
neither the queue nor `origin/main`. **Draft is no barrier by itself — the barrier is this directive**, and a
285+
**Already armed or queued when you read this?** Disable auto-merge AND convert it back to **draft**, which disarms
286+
an unqueued PR; no seat act, those two included, is measured to dequeue one (`platform-readings.md`, queue
287+
membership), so ask the maintainer at once to remove a queued PR by hand — then confirm from the remote that it is
288+
in neither the queue nor `origin/main`. **Draft is no barrier by itself — the barrier is this directive**, and a
288289
spent approval or a standing record IS the review record, ⛔ not a relaxation. Behind it: the queue guard refuses a
289290
governed diff without its tier's record; CODEOWNERS routes review requests for `docs/adr/` only, so nothing summons
290291
the maintainer on the other Tier H surfaces; the post-merge audit (`scripts/pm/check-governed-merges.mjs`) lists
@@ -425,8 +426,8 @@ write; never a token in a remote URL, a `git config` value on disk or a log line
425426
it.** Under one shared identity every other participant's write arrives unsigned: the PM
426427
flipping your draft to ready and arming auto-merge, a bot re-labelling, the platform
427428
rewriting your body. A rewritten body is evidence about the body and of nothing else —
428-
⛔ never extend it to the draft flag, which flipped back destroys auto-merge and queue
429-
membership at once (§7's draft-flip re-arm note), invisibly. Read the timeline event's
429+
⛔ never extend it to the draft flag, which flipped back destroys an unqueued PR's
430+
auto-merge at once (§7's draft-flip re-arm note), invisibly. Read the timeline event's
430431
actor, or ask; undo only once you know who set it and why.
431432

432433
**Write the attribution footer in the form the surface keeps — blank line, rule, ONE footer line:**
@@ -529,8 +530,8 @@ Even inside your own worktree, operate defensively:
529530
known-flaky signature, then re-arm once, never reflexively; **collateral eviction is
530531
silent** (triage comments only on `failure`, so an entry cancelled because something
531532
*ahead* failed gets nothing) — neither on `main` nor in the queue means dropped, re-arm;
532-
**flipping back to draft drops auto-merge and queue membership at once**, and neither
533-
returns by itself — ready *first*, arm *second*. One non-fix: **a stale red does not
533+
**flipping back to draft drops an unqueued PR's auto-merge at once**, and it does not
534+
return by itself — ready *first*, arm *second*. One non-fix: **a stale red does not
534535
clear by re-running** — `rerun_failed_jobs` reuses the original run's commit and merge
535536
ref, so a fix that landed on `main` since is invisible to it; only a new commit
536537
(`git merge origin/main`) helps. Whether a direct `gh pr merge` is refused here is

0 commit comments

Comments
 (0)