Commit 6ef3312
docs(pm): platform-readings retires the rows that still describe MCP update_pull_request as a live channel (#18307)
Fixes #18280
## What
`.claude/skills/pm-dispatch/references/platform-readings.md` carried
four rows that prescribed or described MCP `update_pull_request` as a
live channel. PR #18276 put `mcp__github__update_pull_request` into lock
1's `permissions.deny`, so no seat can call it — measured on this
branch's base `53dd5aaa`: `grep -c mcp__github__update_pull_request
.claude/settings.json` = 1. Those rows therefore pointed at a channel
the harness refuses.
One file, four rows, nothing else. 466 → 465 lines; every edited line ≤
120 bytes.
| base row | disposition | row on head | bytes |
|---|---|---|---|
| :43 — `update_pull_request` 不管传不传都发送 `draft` 位 ⇒ 任何调用必须显式带 `draft:
true`。 | **retired** — folded into the re-keyed :44 | — | 109 → 0 |
| :44 — reviewers/title/body/labels 的单字段调用同坑,曾把治理面 draft 发布进合并队列。 |
**re-keyed**, absorbing :43's surviving mechanism | :43 —
`update_pull_request` 单字段调用也发送 `draft` 位,曾把治理面 draft 发进队列 ⇒ 锁 1 已拒。 |
101 → 115 |
| :46 — 它不碰 draft 位;门开席位优先走它,MCP-only 席才用 update 加显式 `draft: true`。 |
**re-keyed** — true half kept, dead MCP-only branch dropped | :45 — 它不碰
draft 位 ⇒ 请审恒走它;MCP 兜底已拒。 | 99 → 61 |
| :47 — undraft 两条通道:席位凭据走 `POST .../pulls/{n}/ccr/ready_for_review`,MCP
传 `draft: false`。 | **re-keyed** — the CCR route is now the only one |
:46 — undraft 单通道:席位凭据走 `POST .../pulls/{n}/ccr/ready_for_review`;MCP
兜底已拒。 | 106 → 97 |
| :345 — MCP `update_pull_request` 包装器删掉 PR 正文的页脚块。 | **re-keyed** as
history of a denied channel, NOT deleted | :344 — MCP
`update_pull_request` 包装器删掉 PR 正文的页脚块;该通道锁 1 已拒,读作历史。 | 68 → 103 |
### :44's status — asked by the card, answered here
:44 is a reading about the past (an incident), not a prescription, so it
survives as history. But it survives only by absorbing :43: its subject
「同坑」 dereferenced :43's pit, so deleting :43 alone would have left :44
pointing at nothing. The merged row carries all three surviving facts in
one line — the mechanism (any single-field call still sends the `draft`
bit), the incident it caused (a governed-surface draft went into the
merge queue), and the current status (锁 1 已拒). That is what pays the
ratchet: two rows about a denied tool become one.
### Why :345 is re-keyed and not deleted
Deleting it would break rows this card may not touch: :347 hangs off
:346 (the bare REST route), but :348 「同一 MCP 包装器上有反例」 and :349 「送无页脚正文经
MCP 编辑回读仍无页脚」 dereference :345's wrapper, and :355 names :345's
observation as 「唯一不合此判据的观察」. The row keeps its measured content and is
marked as a fact about a denied channel.
## Why this shape (four axes; the one design choice per row: delete vs.
re-key as denied history)
- **实际业务需求** — measured, not assumed: no code, gate or sibling file
reads these row texts (`git grep` for 「同坑」, 「两条通道」, 「MCP-only」, 「包装器删掉」
across the tree outside `node_modules` finds no consumer but the file
itself; the one other 「同坑」 hit, :316, is a different reading). The live
routes the rows named — `POST /pulls/{n}/requested_reviewers` and `POST
.../ccr/ready_for_review` — do have consumers, which is exactly the half
kept in each re-keyed row.
- **项目长远合理性** — one channel per action, no dialect. A prescription that
routes a seat to a denied tool is a workaround written down; the
register keeps the measured facts and drops the routes.
- **防 AI 写代码犯错** — this is the axis that decided the deletions. A
register line that says 「MCP-only 席才用 update 加显式 `draft: true`」 is a
declaration the runtime does not honour: the seat that follows it finds
the tool absent from its roster and, per :222 of this same file, must
read that absence as the deny working. Removing the line removes the
trap rather than asking the reader to tolerate it.
- **创业阶段不扩散需求** — immediate retirement, no staged window and no dual
spelling: the rows go with the deny that made them false, and no
compatibility note is left behind for a channel nobody may use.
The same axes said re-key, not delete, wherever a row still carries a
measured fact with a live dereference (:44's incident, :46's 「它不碰 draft
位」, :47's CCR route, :345's antecedent for three rows below it).
## Gates
Derived by `node scripts/pm/dispatch-gates.mjs --commands
.claude/skills/pm-dispatch/references/platform-readings.md` — 16
commands, every one run, each exit captured by redirect before any pipe.
The derivation is byte-identical when re-derived from the committed
tree.
| command | exit |
|---|---|
| `node scripts/check-closing-keyword-parity.mjs` | 0 |
| `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 |
| `node scripts/check-comment-mask-corpus.mjs` | 0 |
| `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
3 on the fresh worktree (PREREQUISITE NOT MET — `@objectstack/formula`
and `@objectstack/lint` unbuilt; nothing was measured), then 0 after
`turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint` under `scripts/pm/os-verify-lock.sh`
(VERDICT command-exit 0, 4 tasks) |
| `pnpm check:agent-test-spelling` | 0 |
| `pnpm check:doc-authoring` | 0 |
| `pnpm check:driver-memory-census` | 0 |
| `pnpm check:nul-bytes` | 0 |
| `pnpm check:pm-governed-merges` | 0 |
| `pnpm check:pm-skill-id-lint` | 0 |
| `pnpm check:pm-skill-ratchet` | 0 — 「platform-readings.md is 465 lines
(ceiling 466; headroom 1)」, table-row pin 0/0, every 120-byte line cap
held |
| `pnpm check:refd-timer-probe` | 0 |
| `pnpm check:required-contexts` | 0 |
| `pnpm check:skill-frame-sync` | 0 |
| `pnpm check:watch-hint-literal` | 0 |
`node scripts/pm/dispatch-gates.mjs --ran ran.list` → exit 0: 「16
derived famil(ies) accounted for — 16 run, 0 NOT-MEASURED」. The battery
`pnpm check:pm-dispatch-gates` is NOT derived for this path and was not
run.
Beyond the derivation: the control-byte sweep `grep -naP
'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` over the edited file matches nothing
(exit 1). ESLint is measured, not assumed: `pnpm exec eslint --format
json` over the file reports 1 file, `errorCount` 0, message 「File
ignored because no matching configuration was supplied」 —
`eslint.config.mjs` populates only
`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}`, so Markdown is outside ESLint's
population entirely and this diff cannot move the verdict on any
untouched file (no type-aware rule can reach a file outside the
population). Repo-wide `pnpm lint` is CI's run. No package
`build`/`test`/`typecheck` is owed: the diff touches no package, no
`exports` and no published `files[]` content.
## Reverse verification
`grep -n update_pull_request` on the file: **3 hits before** (:43 the
prescription, :66 `update_pull_request_branch` — a different tool,
untouched, and :345 the wrapper) and **3 hits after** (:43 the merged
history row, :65 `update_pull_request_branch`, :344 the re-keyed wrapper
row). The card's expected count of 5 did not hold on the base: :46 and
:47 name the tool as 「update」 and 「MCP」 without the identifier, so they
never matched that grep — recorded as a premise check, not a deviation.
Ablation, both legs hash-proved on the committed tree (HEAD blob
`e863ac59`, base blob `d9bd0fda`):
1. Mutation leg — `git checkout 53dd5aa -- .../platform-readings.md`
restored the four false rows; `git hash-object` on disk = `d9bd0fda`
(equal to the base blob, so the mutation really landed), anchor greps
1/1/1 for the three retired texts and 0 for the new merged row, file at
466 lines. Reading: `pnpm check:pm-skill-ratchet` exit **0**, 「465」
becomes 「466 lines (ceiling 466; headroom 0)」.
2. Restore leg — `git checkout HEAD --` on the file by ABSOLUTE path;
`git hash-object` = `e863ac59` (equal to the HEAD blob), `git diff HEAD`
empty (0 lines), file back at 465. Re-reading: ratchet exit **0**,
headroom 1.
The script carried an EXIT/INT/TERM trap whose handler restores the file
from HEAD by absolute path, and the restore is proved by the hash
comparison plus the empty `git diff HEAD`, not by an exit code.
**The observed direction is neither red-to-green nor a diagnostic count
change, and is reported as measured:** no gate reads these row texts, so
no ablation of them can turn one red by construction. What the two legs
do establish is that the mutation reached disk and that the only gate
that reads the file at all — the line ratchet — is green on both trees,
at 466/466 and at 465/466. Recording that as a green ablation would be a
fit, not a finding.
## Changeset
`skip-changeset`: the whole diff is under `.claude/**`, the fast lane —
nothing under any released package's `files[]` moves, so no
`.changeset/*.md`.
## 维护者速读(草稿)
**改了什么**:pm-dispatch 的平台读数表(`references/platform-readings.md`)里有四行仍把 MCP
`update_pull_request` 当成能用的通道。本 PR
把其中的处方删掉、把仍成立的实测事实改写成「这个通道已被拒」的历史记录:两行合并成一行(:43+:44),另外三行原地改写(:46、:47、:345)。文件
466 → 465 行,只动这一个文件。
**为什么改**:PR #18276 已把该 MCP 工具加进 `.claude/settings.json` 的 deny
清单,席位根本调不到它。留着这些行等于让 AI 席位照着一条运行时必拒的路走——而同一张表的另一行又告诉它「工具从工具表消失就是 deny
生效」,两边打架。声明与强制对齐,一处说清楚就够。
**风险与代价(含回滚)**:纯文档面,无代码、无发布内容、无 changeset;风险是把某条仍有人引用的读数删过头,所以 :345
只改写不删除(下面三行还在引用它)。回滚 = revert 本 PR 的唯一提交。
**席位意见**:
**你要做的**:这是 `references/` 层(受管面的 references 档),按分层规则由 skills 席位做
contract-tier 审查后落地;维护者无需操作。
## Acceptance notes
- noted, not filed: :348 「同一 MCP 包装器上有反例」 and :349 「送无页脚正文经 MCP
编辑回读仍无页脚」 still describe the same denied wrapper as a live edit channel,
and :355 leans on :345's observation. They are outside this card's row
surface and left byte-identical. 承接者:the skills seat's next register
card on the PR-body footer block — the same block is already being
worked at :350–:352.
- noted, not filed: the card named :347 as one of the three rows citing
the MCP wrapper. Read on the base, :347 「同路送无页脚正文存回恰一条」 dereferences
:346 (裸 REST `PATCH /pulls`), not the MCP wrapper — the third MCP row in
that block is :349. Nothing edited either way; recorded so the follow-up
card names the right rows. 承接者:同上。
- noted, not filed: the ratchet now reports headroom 1 on this file (465
of ceiling 466). Lowering the ceiling is always legitimate and is how
this map records a paydown, but the ceiling lives in
`scripts/pm/check-skill-line-ratchet.mjs`, outside this card's declared
single-file surface, so it was NOT touched. 承接者:the skills seat on its
next touch of the ceiling map (the declared cross-file move into this
file re-prices green either way: +11 against a net source decrease of 20
today, +10 at a 465 ceiling).
- noted, not filed: the retired :46 clause was the only line telling a
seat with no REST channel (an 「MCP-only 席」) what to do about a review
request; under the deny such a seat has no channel at all.
`rest-channel.md` :39 already spells the house answer for that shape
(无通道 ⇒ 交有通道席位), so no new rule was invented here. 承接者:none.
- This PR touches a governed surface (`.claude/**`), all of it under
`.claude/skills/pm-dispatch/references/`: left DRAFT, no reviewer
requested, no ccr route touched, no auto-merge armed.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr)_
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>1 parent 52c1a8c commit 6ef3312
1 file changed
Lines changed: 4 additions & 5 deletions
Lines changed: 4 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
43 | | - | |
44 | | - | |
| 43 | + | |
45 | 44 | | |
46 | | - | |
47 | | - | |
| 45 | + | |
| 46 | + | |
48 | 47 | | |
49 | 48 | | |
50 | 49 | | |
| |||
342 | 341 | | |
343 | 342 | | |
344 | 343 | | |
345 | | - | |
| 344 | + | |
346 | 345 | | |
347 | 346 | | |
348 | 347 | | |
| |||
0 commit comments