Repository navigation
docs(pm): eleven platform readings into the fact table — 23 rows, 3 in-place corrections, ceiling 425 to 448 - #17746
Conversation
… in-place corrections) Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK Co-authored-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…r the standing exception Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK Co-authored-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK Co-authored-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…atform-readings-rows
…he labels-and-type rider row Claude-Session: https://claude.ai/code/session_01MCLBsUgfykL74aU716rzVK Co-authored-by: Claude <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
维护者速读(终稿)改了什么 — 给 PM 派发技能的平台事实表 为什么改 — 这些都是各席位这两天实测踩到的平台事实,写进表后下一个席位不用再踩一次;抬上限走的是 SKILL.md 里唯一的常设例外(该文件增量抬上限免决策卡),条件——逐条核实、去重计数、一事一行、不计重排——本席在 ACCEPT 评论里逐条对过。 风险与代价(含回滚) — 纯参考文本,不改任何门禁行为;每行 ≤ 120 字节,棘轮、id-lint、nul-bytes、dispatch-gates 全家族 39 条本席按 dev 报告与自身读数核过全绿;本 head 上 CI 还有 1 个 job 在跑,合并前等它绿。回滚 = revert 本 PR。 席位意见 — 席内按契约复审档复核通过,建议合并。去重计数:候选 33 / 落地 26 / 已有 7 / 拒收 0。 你要做的 — 受管面:请人工合并 PR #17746(本席不翻 ready、不入队、不挂 auto-merge)。 Generated by Claude Code |
…ape its own clean class The header declared the issue-body footer cell UNMEASURED and held the body-mode append in `mutated` on that ground, while the governed fact table has carried the measurement since PR #17746 and the skills seat re-measures it on every seat-post refresh. Since PR #18758 `$?` already answered 0 for both footer shapes, so the tool said two things about one set of bytes: "everything sent is on the platform" in `$?` and "MUTATED" on stderr, with `--json` reporting `body_mutated: true` beside `body_landed: true`. `footerReAnchoring`'s two measured shapes are now two declared classes — `footer-appended` and the new `footer-re-anchored` — for either act, so `classifyReadBack` takes no `mode`: the comparison is exact bytes, which cannot tell one act's write from another's, and the gate existed only for the cell that is now measured. `sentBodyLanded` reads the class alone. Every exit-4 control is unchanged: a byte lost before the rule, a byte changed at equal length, a chewed footer, a rewritten link, a newline from nowhere, a truncation ending in the block. What is unmeasured is named instead: the WRITE CHANNEL. Two `--body` refreshes of the same shape on the same endpoint the same day read +58 on this seat's channel and IDENTICAL on the triage seat's, and no act here can vary that. Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu Co-authored-by: Claude <noreply@anthropic.com>
…rned fact table, each paid in place (objectstack-ai#18689) Part of objectstack-ai#18426 — the carrier of a seven-card batch, all landing rows in one governed file. `Clause-②: no` One file: `.claude/skills/pm-dispatch/references/platform-readings.md`, held at **466 / 466** (headroom 0, widest line 120 B). No ceiling was raised. Every adopted row is paid by a compression or a dedup **in the same file**, listed beside the sentence it shortened. Rows :10–:12, :212 and :431 are untouched — verified by content, and none of them appears in any hunk of the diff. Each reading was adopted or rejected **on its own**, as the cards ask. ## Verdicts | card | reading | verdict | row line | bytes | paid by | |---|---|---|---|---|---| | objectstack-ai#18426 ① | zod v4 defines the parse methods per INSTANCE, so prototype spying throws; the seam is the barrel population | **ADOPTED** | `:277` + `:278` | 97 B + 116 B | `:172` — the conditional-request row absorbs the payload-precedence row (its 「只有 body 精确」 is restated at `:178`–`:179`) · `:185` — the payload-staleness pair | | objectstack-ai#18426 ② | the REST write identity is per-TOKEN, not per-channel; read back your own write | **REJECTED** | already at `:126`–`:132` | — | — | | objectstack-ai#18426 ③ | `os-verify-lock.sh` is objectstack's and absent in objectui | **ADOPTED** | `:393` | 112 B | `:207` — the `created`-order pagination pair | | objectstack-ai#18426 ④ (comment) | ref deletion is refused while every other write on the token succeeds | **REJECTED** | already at `:374`–`:377` and `:430` | — | — | | objectstack-ai#18426 ⑤ (comment) | a suspended author's comment is counted but not served | **REJECTED** | already at `:250` (cause at `:164`–`:167`) | — | — | | objectstack-ai#18446 | a label strip re-triggers the label-sensitive checks ⇒ pre-check ③ is read after the last state write | **ADOPTED** | `:85` | 118 B | `:289` — the missing-command pair | | objectstack-ai#18458 | `gh-readonly-queue/*` ref names embed the stacked-on sha ⇒ queue ORDER from `ls-remote` | **REJECTED** | already at `:70`–`:73` | — | — | | objectstack-ai#18467 (main) | a full rule-plus-footer BLOCK to an issue body is re-anchored, not doubled | **REJECTED** | already at `:410` + `:357` | — | — | | objectstack-ai#18467 (operating half) | that re-anchor is what `post-stamped`'s `body` mode reports as `mutated` | **ADOPTED** | `:357` (new row under it) | 119 B | `:318` — the MCP-truncation pair | | objectstack-ai#18467 (addendum) | bare REST `POST /issues` with a footerless body synthesises exactly one, +58 | **ADOPTED** | `:410`, refined in place | 120 B | **zero** — folded into the existing create row | | objectstack-ai#18498 row 1 | `grep -z` with a literal newline is an OR — a false POSITIVE; `grep -Pz` with the backslash-n escape discriminates | **ADOPTED** | `:291` | 118 B | `:451` — the usage-wall pair | | objectstack-ai#18498 row 2 | (withdrawn on the card as a duplicate) | not adopted | — | — | — | | objectstack-ai#18521 | `unstable` can come from a commit STATUS the `/check-runs` enumeration cannot see | **ADOPTED** | `:28` | 115 B | `:303` — the two CI-log reading rows | | objectstack-ai#18522 | the footer has two spellings, so a bare-only strip strands the session-URL one mid-body | **ADOPTED** | `:347` | 106 B | `:331` — the fence caveat folds into the trap row beside it | **8 readings adopted, 9 lines, 8 payments** (the create-channel cell costs nothing — it refines a row that was already there). Net line change: **0**. ### Every payment, and what it shortened | payment | before | after | fact lost | |---|---|---|---| | `:172` | 「文档载明未实测:条件请求答 `304` 不计 core 池。」 + 「公开仓零配额读法两档,payload 档优先 —— 只有 body 精确。」 | one row | 「只有 body 精确」 — restated in full at `:178`–`:179` (`assignees` stale, claim-bearing fields never read from this channel) | | `:185` | 「⇒ 前 15 项那条判别式…」 + 「失效方向是据它对别人的工作下没有认领的判词。」 | one row | none | | `:207` | 「`created` 序承重…」 + 「以两趟的重叠证覆盖…」 | one row | none | | `:289` | 「⛔ 一般规则:任何可能不存在的命令…」 + 「PR 与查重类核验改走 MCP GitHub 工具或 git。」 | one row | none | | `:303` | 「`get_check_run` 对本仓 CI job 回空 `output.text`。」 + 「`get_job_logs` 无论 `tail_lines` 只回占满日志尾部的 post-step service-container teardown。」 | one row | 「post-step」 and 「占满」 — the position is still given by 「尾部的」 | | `:318` | 「GitHub 存储字节完好,raw REST 取回一字不差。」 + 「⇒ ⛔ 永不单凭 MCP 读判截断…」 | one row | none | | `:331` | 「⛔ 围栏不防护:照删、留空围栏。」 + 「另一坑:感叹号紧跟左方括号即触发(TS 非空断言下标),无需尖括号。」 | one row | 「TS」 and 「下标」 from the parenthetical — the mechanism is already spelled out by 「感叹号紧跟左方括号」 | | `:451` | 「盲区:窗口起点是本地推断的近似值。」 + 「撞墙报文形如 `limit reached, resets at HH:MM`…」 | one row | none | ## Why the five rejections — the four axes All five reject for **one** reason: the reading is **already in the fact table**, measured and phrased. That makes this a single class, so the axes are argued once and the per-card evidence follows. - **实际业务需求** — the need each card names is real, and it is already served. A seat asking 「who authors my REST writes」 lands on `:129` (「类只认每次写回读的 `user.login`/`user.type`」); a seat asking 「where is my PR in the queue」 lands on `:70`–`:73`, which carries the parent-tip chain recipe **and** a caution the card does not have (`:72`: the embedded parent is a presumed base, measured to reach `main` ten-plus minutes later). Adding a second row would not add a reader path; it would add a second place to disagree. - **项目长远合理性** — a fact table with two rows for one fact is the workaround shape this repo refuses. When one copy is later corrected, the other stays, and the next seat reads whichever it reaches first. One row, one reader path. - **防 AI 写代码犯错** — this is the axis that decides it. A duplicated row is a **lenient** surface: it lets a future edit narrow one copy while the other keeps the old claim, and nothing goes red. The strict form is one row, which the ratchet already prices. Note the direction of the rejection: none of the five loses a fact — each is being kept, in the line that already carries it. - **创业阶段不扩散需求** — at 466 / 466 every row is paid by deleting or compressing another. Paying real compression for a fact that is already written down spends the budget twice and shrinks the table's coverage by exactly the sentence that was compressed to make room. Per-card evidence: - **objectstack-ai#18426 ②** — `:126` names both token classes (`claude[bot]` and the account), `:127` says the class is per Claude Code account and can flip mid-session, `:129` says the class is only known by reading back each write, `:132` says never assume `claude[bot]`. `:128` already lists two measured flips. The card's third measurement (`os-sam` vs `hotlong`) confirms a rule the table states; it does not change it. - **objectstack-ai#18426 ④** — `:375` records the `git push --delete` disconnect with the misleading tail, `:374` and `:376` record the consequence (probe branches accumulate), and `:430` records the second form, the proxy 403, with the same disposition and an explicit 「⛔ 不重试」. - **objectstack-ai#18426 ⑤** — `:250` is the reading: 「`comments` 计数会多读:实测 1 而列表与 timeline 双 0 ⇒ 线程空否读那两条, ⛔ 不读计数」. The cause is at `:164`–`:167`.⚠️ Worth the seat's eye: the triage grading on objectstack-ai#18426 asked specifically for this one to be written in, and it already was. - **objectstack-ai#18458** — `:70` gives the name form and says the embedded sha is the previous entry's tip; `:71` gives the walk and says lexical order is meaningless; `:73` says it is zero-quota and answers the position that the timeline cannot. `:61` keeps the positive-only caution the card asks to preserve. - **objectstack-ai#18467 (main)** — `:410`: 「issue 正文 `PATCH` 识别按整块:送全块或不送页脚都存回恰一条,已有页脚归一末尾不 复制」 is the not-doubled half; `:357` is the blank-line re-anchor and the instruction to compare by first differing offset rather than by length. What was **missing** is the operating consequence, and that is the row adopted at `:357`. ## Measurements taken for this PR - **objectstack-ai#18498's restatement sweep.** The dispatch asks where else the `-z` advice is restated, so the same edit can ride the same PR. Measured repo-wide, exit code captured before any pipe: `git grep -F 'grep -z'` → **exit 1**, `git grep -F -- '-Pz'` → **exit 1**, `git grep -F '行向'` → **exit 1**. Lit control in the same batch: `git grep -F '拼接后'` → **exit 0**, hitting `platform-readings.md:290`, and `grep -c grep` on that file → 13. ⇒ **the `-z` advice is restated nowhere in this repo** — it lives in the dispatching seat's own prose, handed down per round. Nothing in SKILL.md or core-rules needed the co-edit, so nothing is reported for re-scoping. The row is placed under pit ⑥ (`:290`), which is where this repo's wrapped-phrase discipline actually lives. - **objectstack-ai#18446's second half is NOT line-neutral in `contract-review.md`.** That file is at 60 / 60. Its landing pre-check block `:40`–`:46` runs 99–120 B per line, and the cheapest adjacent merge inside it is **213 B** — no insertion fits without a raise. ⇒ per the dispatch's own instruction for that case, the row at `:85` carries the sequence: 「③ 在入队前末次状态写之后读」. `contract-review.md` is untouched, still 60 / 60. - **objectstack-ai#18446 is recorded in its generalised form**, per the card's own addendum: a label strip and a ready flip are both state writes. `:84` already recorded the ready-flip re-run with a different consequence (enqueue timing); the new row states the consequence for pre-check ③. ## Gates `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived the list from the tree (no hand-fed paths); `--ran` reconciles: **18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN — a DERIVED zero, all 18 carry an exit code and none of them is 3.** Every code captured by redirect-then-`$?`, never through a pipe. | 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 | | `node scripts/pm/check-harness-current.mjs --self-test` | 0 | | `pnpm --filter @objectstack/lint run check:doc-formula-expressions` | 0 | | `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-half-states` | 0 | | `pnpm check:pm-skill-id-lint` | 0 | | `pnpm check:pm-skill-ratchet` | 0 | | `pnpm check:refd-timer-probe` | 0 | | `pnpm check:required-contexts` | 0 | | `pnpm check:skill-frame-sync` | 0 | | `pnpm check:watch-hint-literal` | 0 | Named explicitly by the dispatch, both inside that list: - `pnpm check:pm-skill-ratchet` → **exit 0**; its own verdict lines: `platform-readings.md is 466 lines (ceiling 466; headroom 0)` and `platform-readings.md: widest table row is 0 bytes (pin 0; headroom 0)`; `contract-review.md is 60 lines (ceiling 60; headroom 0)`. - `pnpm check:skill-frame-sync` → **exit 0**: `the one declared copy of the decision frame is internally coherent`, 4 axes, 74 markdown files scanned for undeclared copies. Governed-surface predicate, as the dispatch asks it be recorded: - `node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/references/platform-readings.md` → **exit 3 = GOVERNED** (not a failure): `1 of 1 path(s) hit the register`, `.claude/** ×1`. First eight of those ran at exit 3 = `PREREQUISITE NOT MET` on a fresh worktree with no `node_modules`; that is not a measurement, so `pnpm install` ran and every one was re-run. `check:doc-formula-expressions` then still answered 3 (`@objectstack/formula` and `@objectstack/lint` unbuilt); its own prescribed fix was run through the shared verify lock — `os-verify-lock: VERDICT command-exit 0` — and the gate re-ran at 0. Manual byte discipline beyond the gates: `grep -naP` for control characters over the edited file → **exit 1** (none). ## Acceptance notes Noted while measuring, not filed, and not touched here: - `platform-readings.md:412` states the issue-body `PATCH` cell has two empties (MCP with a bare footer; a single-field `issue_write` on `title`/`labels`). Still true after this PR — the adopted create-channel cell is a different verb. Successor: the next seat that writes an issue body through MCP. - `scripts/pm/post-stamped.mjs`'s docblock says 「whether the platform synthesises a footer for a footer-less ISSUE BODY is unmeasured」, which objectstack-ai#18467 shows is stale prose — the table has carried that cell since PR objectstack-ai#17746, and this PR adds the create half. The tool and the table now disagree in writing. ⛔ Out of scope here: the file is not on this PR's declared surface, and the card itself declines to judge whether one observation is enough to extend the tool's declared normalisation set. Successor: whoever next touches `post-stamped.mjs`. - objectstack-ai#18461 is deliberately absent from this batch (its row ② contradicts objectstack-ai#18421's reading; that decision is the maintainer's). Nothing here touches it. ## 维护者速读(草稿) **改了什么** —— 只改一个文件:PM 车道的平台读数事实表。八条今天实测出来的 GitHub/工具行为写进去,五条因为 表里已经有了而没写。文件行数一行没涨(466 / 466),每加一行都在同一个文件里删掉或压掉一行来付账。 **为什么改** —— 这些读数原本只存在于各个 issue 的讨论里。没有载体的读数,下一个席位会从零重新推一遍;这次 的批次本身就是证据:五条里有五条已经在表里了,而填报席没查表就填了卡。 **风险与代价(含回滚)** —— 风险在八处压缩:两行合成一行,可能丢掉措辞。逐条列在正文的「Every payment」表里, 三处明确写了丢掉哪几个字以及那个事实在哪一行还能读到,其余五处无损。回滚是 `git revert`,单文件、无生成物、 无下游消费者。 **席位意见** —— (留空,由席位在复核时填) **你要做的** —— 不需要做什么。这是事实层(`references/**`),按裁决 objectstack-ai#17950 由技能席在自己的契约档复核后经队列 落地;⛔ 没有席位把它翻 ready、入队或挂 auto-merge。 ## Closing lines Part of objectstack-ai#18426 — readings ① and ③ adopted; readings ②, ④ and ⑤ rejected as already recorded, so the card stays open for the seat to confirm those three duplication claims before it is retired. Fixes objectstack-ai#18446 Part of objectstack-ai#18458 — the entire reading is already recorded at the queue-ref rows; no row was added, and the card stays open for the seat to confirm that before it is retired. Part of objectstack-ai#18467 — the operating consequence and the create-channel cell are adopted; the re-anchor itself is rejected as already recorded, so the card stays open for the seat to confirm that one. Fixes objectstack-ai#18498 Fixes objectstack-ai#18521 Fixes objectstack-ai#18522 --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #17680
Fixes #17239
Fixes #17563
Fixes #17677
Fixes #17710
Fixes #17754
One folded family plus one review-round rider, one file. Twenty-four new rows and four in-place
corrections in
.claude/skills/pm-dispatch/references/platform-readings.md, plus the oneCEILINGSentry the standing one-file exception prescribes. Nothing else moves — notrest-channel.md, not SKILL.md, notcheck-clause2-carriers.mjs, notcheck-half-states.mjs.Dedupe counts (the standing exception's own condition)
Candidates 33 / landed 26 readings / already present 7 (no edit) / refused 0.
Landed 26 readings = 24 new lines + 4 in-place corrections. Two readings needed two lines each
under the 120-byte per-line cap, which is why 26 readings are 28 line-level changes.
Check Changesetgate reads the clause-2 declaration tolerantlyPATCH /pullsappends a bare footer, +58 B, keeping the sent onePOST /pullscreate leg, +90 B when the tail is not the blockPATCHand issue-bodyPATCHcarry a full block throughContent-Type: application/jsondocumentation_urlis Claude Code's, not GitHub'sissue_writeUPDATE eats a trailing rule+footer block and reports successPATCH /issuesrecognition is BLOCK-shapedneeds:contract-reviewis a merge gate in its own rightmergeable_statesequences across a ready flipmerge_methodis inertfailuresuperseded by a later failure with a different diagnosischeck-governed-mergescheck-half-states.mjsdoes full-repo I/O even for--helpnohupinside a backgrounded tool call reports a false exit 0event=merge_groupruns; a quiet tip lagslabelsandtypein ONE bare-REST issuePATCHanswers 500 and writes nothing; the same two fields as two writes both answer 200mergeable_stateis not the literaldraftThe four in-place corrections, each because a new cell contradicts the row rather than
extending it: the success-sequence row that keyed on the removal event being followed by the
merge event within about a second; the issue-body
PATCHrow whose "always exactly one" istrue of a full block and of no footer but false for a bare footer with no rule line; the
write-entities prescription, true of body prose and false inside an inline code span; and the
row asserting a draft PR always answers
draftformergeable_state, which seven draft PRsacross two repos falsify — they answer
clean,blockedorunknownlike any other PR.Line budget
platform-readings.mdlinesCEILINGSentry for that fileEvery new or changed line measures at or under the 120-byte per-line cap (measured with
awkover the file: zero lines exceed 120 bytes). The raise is recorded as ONE
ruledRaisesrecordon the cross-file-move declaration, quoting the standing one-file exception verbatim, dated
2026-09-12, delta 24 — the rider raised that record's delta rather than adding a second, since
this is one increment. The declaration's ordinary-ruled-raise total moves 100 to 124 while its
own
wasstays at 314. No other ceiling moves; no re-wrap was used as payment.Gates
39 commands derived on the final diff with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(the derivation's own stderr names the repo and the commit,and derives the change set three-dot from the merge base). Every one captured its exit code
before any pipe. All green, and the reconciliation confirms it:
dispatch-gates --ran— 39 derived, 39 run, 0 NOT-MEASURED, 0 UNRUN, and the zero is aDERIVED one: all 39 recorded an exit code and none of them is 3.
pnpm --filter @objectstack/lint run check:doc-formula-expressionsfirst exited 3 —PREREQUISITE NOT MET (two workspace packages unbuilt). That is NOT a finding and not a
measurement; after
turbo run build --filter=@objectstack/formula --filter=@objectstack/lintunder the shared verify lock it exits 0. It is also a live instance of the A5 reading
this PR lands.
pnpm check:pm-dispatch-gatesexceeds the container's 600 s foreground cap and was rundetached with a recorded PID, waited on in the foreground; verdict
dispatch-gates self-test: 1678 cases pass, exit 0.pnpm check:pm-skill-ratchet—platform-readings.md is 449 lines (ceiling 449; headroom 0).Path face:
node scripts/pm/check-governed-merges.mjs --pr 17746printsGOVERNED — a human merge is the review record for this PRand exits 3 — the expected verdict, and itself themeasured instance behind one of the landed rows.
维护者速读(草稿)
改了什么。 只动两个文件。
platform-readings.md加 24 行读数、就地更正 4 行;棘轮脚本里只改这一个文件的
CEILINGS数(425 → 449)并按脚本头部规定的形状补一条ruledRaises记录。没有别的天花板移动,没有别的 skills 文件被碰。
为什么改。 六张卡都是同一类:平台行为被实测到了,而事实表里没有、或写得比代码窄。
写得比代码窄比缺席更险 —— 上一轮就有席位据一条过窄的规则去修一个本来没坏的东西。
其中三条跨席位后果最重:认领标记的拼写决定后继席位能不能看见本车道已被占;
REST 写侧少一个 header 就整个静默不写;标签与类型挤进同一次写会 500 且零落库。
风险与代价(含回滚)。 只是参考事实表,没有运行时代码、没有发布物,故挂
skip-changeset。风险面是「写进去的读数是不是真的」,这由每行自带的实测标注与本 PR 的去重表承担。
回滚成本一个 revert:天花板与表的行数是同一笔提交里的一对,revert 后仍自洽。
席位意见。 (待席位定稿)
你要做的。 这是治理面(
.claude/**),⛔ 没有任何 agent 席位可以合它。请人工确认后手合;若不同意某一行的措辞,指出行号即可,不必整体退回。
Authored in Claude Code session
session_01MCLBsUgfykL74aU716rzVK— durable attribution keptin prose here, because this body is re-sent footerless on purpose (see the landed row on that
cell) and the block below is the platform's own.
Generated by Claude Code