Repository navigation
docs(pm): base the REST draft-flip row on a probe, not the docs - #17597
Conversation
`rest-channel.md` 不可迁移 item 1 recorded the draft-to-ready flip as
REST-unsupported on documentation — its own words, 「与第 5 条同批核对官方
文档,未逐个实调」. It is measured now, and the measurement is stronger than
the doc implied: REST `PATCH /pulls/{n}` with `{"draft": false}` answers
HTTP 200 and changes nothing (read-back still `draft = true`, body
untouched). A success code therefore proves nothing and the read-back is
the only tell — the failure mode a documented *absence* does not warn
about, because a reader pictures an error they would notice.
The verdict is unchanged (GraphQL-only mutation; the egress proxy serves
only the pinned PR-review GraphQL set) and the 断粮出路 stays; only the
basis moves, and the working channel is now named rather than implied.
Two lines rewritten in place, net 0 lines (82/82 on the line ratchet),
each line within the 120-byte cap.
Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK
Co-authored-by: Claude <noreply@anthropic.com>
The parenthetical the previous commit deleted from item 1 was the only place in `rest-channel.md` disclosing that item 5 — `issue transfer` as GraphQL-only — also rests on a documentation check rather than a probe. Item 5 did not say so itself, so that disclosure would otherwise exist nowhere in the file. It now sits on item 5, where a reader of item 5 will actually meet it. One line, one token wider: line 61 goes 82 to 105 bytes, the file stays 82 lines at ceiling 82, and every other line — item 5's continuation included — is byte-identical. Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK Co-authored-by: Claude <noreply@anthropic.com>
维护者速读改了什么 —— 为什么改 —— 「文档说不支持」和「实测回 200 却什么都没做」是给读者的两条不同指令:前者让人等一个错误;后者说明只看状态码的席位会把 PR 记成 ready 然后走人,PR 却一直躺在 draft 里。本席当晚就被它咬过一次(#17582 的起因),抓住它的是读回,不是状态码——所以判据写成「读回才作数」。 风险与代价(含回滚) —— 纯事实表六行(+3/−3),无代码、无已发布内容、无 changeset;棘轮两行读数本席在 head 席位意见 —— 通过。两轮共四行本席逐字读过、逐行量过;第 5 条那一个 token 是本席在复审时补的区域放宽(REWORK 5628341593),不是 dev 越界。受管面( 你要做的 —— 一个动作:人工合并本 draft PR。看一眼措辞是否就是你要的口径——第 1 条「状态码不作数,读回才作数」,第 5 条「(核对文档,未实调)」。 skills 席,session Generated by Claude Code |
Fixes #17582
.claude/skills/pm-dispatch/references/rest-channel.md, 不可迁移 item 1 — three lines rewritten inplace. The verdict is unchanged (the draft-to-ready flip is a GraphQL-only mutation; the egress
proxy serves only the pinned PR-review GraphQL set). Only the basis moves: from documentation to
a dated probe. Net 0 lines, 82 / 82 on the ratchet, no line over the 120-byte cap.
The row, before and after
Before (bytes per line: 100 / 111 / 109):
After (bytes per line: 100 / 119 / 119):
Line 1 is byte-identical. Line 2 swaps the documentary field list — which the card's own triage ruled
must not be asserted on this evidence, since only
draftwas probed — for the dated measurement.Line 3 states the consequence that the old row did not: a success code proves nothing, so the
read-back is the criterion; and it names the channel that does work instead of implying it.
Patch round 1 — 不可迁移 item 5, one token wider. The parenthetical deleted above was the only
place in the file disclosing that item 5 (
issue transferas GraphQL-only) also rests on adocumentation check rather than a probe; item 5 did not say so itself. The skills seat widened the
declared region by one token rather than open a card, so the disclosure now sits on item 5, where a
reader of item 5 actually meets it — before (82 B), then after (105 B, cap 120):
Item 5's continuation line, and every other line in the file, stays byte-identical; the file stays
82 / 82 and the widest-table-row pin stays 0.
The source of the measurement
Not re-derived here — re-running a REST draft flip would touch PR state this branch did not set. The
reading is the filer's, quoted from the card body (
domain:specexecution seat,session_01MkQhmuuJAVDjmeWNixwDDH, measured on PR #17572 at 2026-09-11T00:12Z):Two readings carried on the card are deliberately not written into the row, because neither is
evidence about this endpoint: the GraphQL
markPullRequestReadyForReview403 is a structural refusalby the pinned-operation proxy (already what line 1 says), and the MCP rate-limit reading belongs to
the identity-versus-client question, which is open elsewhere and remains open. The working channel is
named on the seat's own corroboration — every flip this shift went through MCP
update_pull_requestand was read back.
The one judgement, on the four axes
The only judgement in this PR is the wording of the basis, and specifically whether to keep the
unmeasured field list (
title/body/state/base/maintainer_can_modify) or spend those bytes onnaming the working channel. 实际业务需求: the field list was never probed and the row said so —
the triage explicitly forbade widening the row to those fields on this evidence, so it is a list no
seat may cite; what a seat actually needs at the instant it reads this row is which call to make, and
update_pull_requestis measured (every flip this shift, read back each time). 项目长远合理性:the row now states exactly what was measured, dated, and nothing beyond it — no invented REST path,
no hedge. 防 AI 写错 is the axis that decides it: a documented absence invites the tolerant
reading "REST will refuse and I will see the error", which is precisely the inference the measurement
falsifies; writing 「状态码不作数,读回才作数」 tightens the criterion at the point of authorship
rather than leaving a 200 to be read as landing — the metadata-shaped failure here is a seat
recording a ready flip that never happened. 创业阶段不扩散: no new capability, no second line, no
ceiling raise, net 0 at 82 / 82 — and the unmeasured half is retired immediately rather than kept as
a hedged dual basis. ⇒ Recommendation, on axes three and four: the landed wording.
Verification
Gate families derived from the final diff in the worktree —
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; thescript derives its own changeset). Its stderr names the tree it answered for:
gate list derived from the tree of 'objectstack-ai/objectstack' at commit 29d00cc5, change set1 path(s) vs merge base 29d00cc53(three-dot). Every exit code below was captured before anypipe (
cmd > file 2>&1; EXIT=$?). Run at head776d4491, then re-run in full at the patchround 1 head
00297d13: the same 14 families, the same exit codes, and verdict lines byte-identicalto the first run. The derivation at
00297d13warns STALE TREE (the branch is 4 commits behindorigin/main, 3 family-defining files having moved). That was measured rather than waved past: thesame changeset derived from a clean tree at
origin/main82cb69fereturns an identical14-family list, so nothing landed in that window that this diff owes.
node scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjspnpm --filter @objectstack/lint run check:doc-formula-expressionspnpm check:agent-test-spellingpnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:pm-skill-id-lintpnpm check:pm-skill-ratchetpnpm check:refd-timer-probepnpm check:skill-frame-syncpnpm check:watch-hint-literalEach gate's own verdict line, for the ones this diff can move:
pnpm check:pm-skill-ratchet—✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/rest-channel.md is 82 lines (ceiling 82; headroom 0).and✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/rest-channel.md: widest table row is 0 bytes (pin 0; headroom 0).— byte-identical before and after the edit.pnpm check:pm-skill-id-lint—✓ check-skill-id-lint: 27 file(s) clean (pattern /#[0-9]{3,}/g).— identical before and after; the new text carries no internal id.pnpm check:nul-bytes—check-nul-bytes: OK (scanned 8349 text file(s) -- 8349 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).Plus a self-scan of the edited file outside the gate:grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'exits 1 (clean).pnpm check:doc-authoring—✓ doc authoring guard: 44 published skill files clean — no internal issue-id references.pnpm check:skill-frame-sync—✓ check-skill-frame-sync: the one declared copy of the decision frame is internally coherent.node scripts/check-closing-keyword-parity.mjs—check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 8356 tracked file(s), all registered).Reconciliation —
node scripts/pm/dispatch-gates.mjs --ran RANFILE --repo objectstack-ai/objectstack,with each family recorded as
command :: exit 0:One honest wrinkle, recorded rather than smoothed: family 4 first exited 3 —
check-doc-formula-expressions: PREREQUISITE NOT MET — the workspace package @objectstack/formula is not built … Nothing was measured. That is a NOT-MEASURED code, not a finding. It was cleared bybuilding the two prerequisites through the shared verify lock
(
bash scripts/pm/os-verify-lock.sh -c 'pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2',VERDICT command-exit 0 · held the lock 224s), afterwhich the family re-ran green with the verdict
✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 438 files / 1375 TS blocks judged clean.Path face —
node scripts/pm/check-governed-merges.mjs --branch claude/issue-17582-rest-draft-flip-silent-200,exit 3 (the GOVERNED verdict code):
⇒ this PR stays draft; no seat flips it ready, enqueues it, or arms auto-merge. The maintainer
merges.
Closing-keyword parity on this body, run against the exact file that became this description —
node scripts/check-closing-keyword-parity.mjs --body BODYFILE, exit 2 (the register's "at leastone parser binds"; 1 would mean the instrument did not run):
⇒ exactly one closing declaration — the one on line 1 of this body — reported once per parser⚠️ In the quotation above the closing verb is written as
that recognises it.
KEYWORD: the parsersread fenced text too, so quoting the real output verbatim would have made this description declare
the close four times over instead of once. That was measured, not assumed — the verbatim quotation
took the count to 8 declarations across the same 2 parsers, and this spelling takes it back to 2.
Every other card number in this body sits beside zero verbs, which is the safe spelling.
Changeset: none,
skip-changesetapplied..claude/**is on the fast track — nothing anypackage's
files[]ships moves in this diff, which touches exactly one instruction-surface markdownfile and no package source.
Clause-②: no
Acceptance notes
noted, not filed): the removedparenthetical 「与第 5 条同批核对官方文档,未逐个实调」 was the only place in the file that disclosed
item 5's basis (
issue transferas GraphQL-only) as documentary rather than probed, and item 5did not say so itself. It was left alone on the first pass because the declared file surface was
item 1 only; the skills seat then widened the region by one token, and the disclosure now rides on
item 5 itself. Nothing outstanding.
noted, not filed:onlydraftwas probed. The row deliberately makes no statement abouttitle/body/state/base/maintainer_can_modifyon this endpoint, in eitherdirection. A general claim about the endpoint would need its own probes.
platform-readings.mdand belongs to another card — out of scope: [finding] a RESTPATCH /pulls/{n}appends a second bare attribution footer — and the measured fix is to send NO footer at all, not to stop re-sending #17239, which remains open. TheMCP-quota half is out of scope: 共享身份的限流纪律不存在:一次限流信号约束的是「身份」不是「客户端」,而规矩只说了不要重试 —— 2026-09-10 全 fleet 停摆事故 #17374, which also remains open. Neither is touched here.
维护者速读(草稿)
改了什么 ——
rest-channel.md不可迁移第 1 条的三行原地重写:结论不变(draft 转 ready 是GraphQL-only mutation,出口代理只放钉住的 PR-review GraphQL 集),换掉的是判据。原来的判据是
「核对官方文档、未逐个实调」;现在是 2026-09-11 的实测:REST
PATCH /pulls/{n}带{"draft": false}回 HTTP 200 且什么都没改,读回仍是 draft。行数不变(82 / 82),每行不超120 字节。补丁轮追加一处:第 5 条(
issue transfer)行尾补进「(核对文档,未实调)」,82 →105 字节。
为什么改 —— 「文档说不支持」和「实测回 200 却什么都没做」是给读者的两条不同指令。前者让人以为
会看到一个错误;后者说明:只看状态码的席位会把 PR 记成 ready 然后走人,而 PR 一直躺在 draft 里,
直到有人发现。这次抓住它的是读回,不是状态码 —— 所以新行把「读回才作数」写成判据,并点名真正
可用的通道 MCP
update_pull_request。风险与代价(含回滚) —— 风险低:纯事实表一行,无代码、无已发布内容、无 changeset。代价是删掉了
原来那串未实测的字段清单(
title/body/state/base/maintainer_can_modify),这串本来就只有文档背书,分诊席也明确禁止据本次证据把结论推广到这些字段。那句被删的括号同时是文件里唯一提示
第 5 条也未实调的地方 —— 席位在补丁轮把区域放宽一个 token,这处披露已落到第 5 条自己身上,
读第 5 条的人现在直接读得到,没有丢失。回滚 = 两个 commit revert,无迁移、无下游。
席位意见 ——
你要做的 —— 这是受管面(
.claude/**),PR 保持 draft,由你人工合并。看一件事:措辞是否就是你要的口径 —— 第 1 条那句「状态码不作数,读回才作数」,以及第 5 条补进的「(核对文档,未实调)」。
Generated by Claude Code