Repository navigation
skills(pm-dispatch): the round-open harness reading names the load surface .claude/**, not three paths - #18656
Conversation
…rface `.claude/**`, not three paths
The step-2 round-open reading at SKILL.md:91 named three paths
(`.claude/{settings.json,agents/*.md,hooks/*}`) while the harness loads
the whole `.claude/**` tree, `.claude/skills/**` in full for every seat
session -- the charter told the seat to measure a set smaller than the
one that rots it. The line now names the directory surface and both
readings of `scripts/pm/check-harness-current.mjs` (latest touch and
content), so the set the charter prescribes is the set that is loaded
and the reading does not depend on which copy of the tool the seat holds.
Line-neutral: 812 / 812, the edited line 120 B -> 96 B, no other line
moved, widest table row unchanged (342).
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
维护者速读(终稿)skills 席 改了什么 — 为什么改 — harness 实际载入的是整个 风险与代价(含回滚) — 规则层受管面,只改一行定义;不加标签、不加义务、不动工具。卡上的第二条建议(逐轮目录字节比对)没有采纳::89–:90 已经对照上一开轮标记比对三章程的触碰,而 harness 载入的其余部分重读无用(:92 是全部处方),逐轮账本是一项塞不进这一行的新义务;若要,落点在 :90 的标记内容或工具的 since-marker 开关。回滚 = revert 一个提交,零副作用。 席位意见 — 荐合。它把章程与已落地的工具对齐,消除的是一个已经发生过的漏读;不新增规则。 你要做的(一个动作) — 认可「集合 = 载入面本身」即 Approve 本 PR(或直接人工合并);席位随后按裁定 C 落地。若你想要逐轮目录比对也进章程,Request changes,本席另起。 Generated by Claude Code |
…for (objectstack-ai#18666) Fixes objectstack-ai#18454 Clause-②: no `skip-changeset` — text under `.claude/skills/pm-dispatch/**` only; nothing published moves. Governed `.claude/**`: draft, four-piece, the maintainer's approval lands it. PR objectstack-ai#18656 (SKILL.md :91) is ahead in the SKILL.md serial queue; :89–:92 are untouched here. ## What changed — one rule, three citations rewritten in place, one tell added The card's own correction stands: this is a SKILL-text gap, not a tooling defect. No script changed. Each citation of a repo-scoped instrument now says which repository the instrument answers for, in the spelling that makes the answer checkable. | site | before (bytes) | after (bytes) | |:--|:--|:--| | `SKILL.md` :201 | `- 落点不明留分诊首触,⛔ 不猜。` (44) | deleted — restated verbatim at `references/lanes/ui.md:15` and by 〈域车道〉 :242 「说不出修复碰哪个文件就还不可标」 | | `SKILL.md` :206 (new, 〈多仓协调〉, directly under the `scripts/pm/**` 住 objectstack line) | — | `- 工装只答被告知的仓,缺 `--repo`/`PM_SWEEP_REPO` 即答 objectstack;⛔ 不答手上那张卡的仓。` (112) | | `SKILL.md` :539 〈派发〉 | `- 门禁清单取 `dispatch-gates.mjs --commands` 逐条跑,退出码先落盘,`--ran` 对账;⛔ 不抓人读输出。` (119) | `- 门禁清单取 `dispatch-gates --commands --repo 仓` 逐条跑,退出码先落盘,`--ran` 对账;被拒即手推。` (119) | | `SKILL.md` :628 〈复核〉 | `- 入队资格 = 每个 check 为 success 或预期 skip(名单:check-expected-skips.mjs),⛔ 不是 required 子集。` (119) | `- 入队资格:每 check 绿或预期 skip,⛔ 非必查子集;名单 check-expected-skips.mjs 只判 objectstack。` (116) | | `SKILL.md` :645 〈入队与落地〉 | `- `Clause-②: yes` 认领同笔卡上挂标;PR 开出读 `check-clause2-carriers --pair PR-NUMBER` 为 0 再请审。` (118) | `- `Clause-②: yes` 认领同笔卡上挂标;开 PR `PM_SWEEP_REPO=仓 check-clause2-carriers --pair N` 0 才请审。` (118) | | `references/contract-review.md` :41 | `- ② 双载体已清,逐对机读 `node scripts/pm/check-clause2-carriers.mjs --pair PR-NUMBER`。` (99) | `- ② 双载体已清,逐对机读 `PM_SWEEP_REPO=仓 node scripts/pm/check-clause2-carriers.mjs --pair N`。` (109) | | `references/platform-readings.md` :212 | `- 读 `total_count` 得 None、打印成 total: None,与真空集只差一个字符,而请求根本没跑。` (108) | `- 读 `total_count` 得 None、打印成 total: None,与空集只差一字符;缺键即拒绝,判别式是状态码。` (118) | Fragments dropped to pay the byte cap, disclosed: :539 loses 「⛔ 不抓人读输出」 — the machine modes the line names (`--commands`, `--ran`) are that prohibition's mechanism, and the harvest-by-eye history lives in the script header; :628 spells 「⛔ 非必查子集」 as `core-rules.md` :124 already does and spells success as 绿 (the same twin's word) so the literal `check-expected-skips.mjs` that the gate's self-test pins stays on the line; :645 compresses 「PR 开出读 … 为 0 再请审」 to 「开 PR … 0 才请审」; :212 trades 「而请求根本没跑」 for the tell. Every line ≤ 120 B; every touched file line-neutral. ## Measured behaviour — from an objectstack checkout, 2026-09-17 12:55–13:05Z, tree `d6137fd6c` (re-derived at `4bbddc5a9`) | instrument | told the repo by | run about `objectstack-ai/objectui` from this checkout | tell on the output | |:--|:--|:--|:--| | `dispatch-gates.mjs --commands` / `--tier` | `--repo OWNER/NAME` asserts the checkout | **REFUSES**, exit 2: `REFUSING — asked for 'objectstack-ai/objectui', but this checkout is 'objectstack-ai/objectstack'`, and names the remedy (hand-derive from the sibling's own manifest and workflows). Without `--repo`: exit 0, 18 objectstack commands on stdout, the provenance banner on **stderr only** — a `--commands > gates.list` harvest is a well-formed list about the wrong repo | the refusal; or the stderr banner `gate list derived from the tree of 'objectstack-ai/objectstack'` | | `check-expected-skips.mjs --pr N` | `PM_SWEEP_REPO` retargets the **board only**; the roster is always this checkout's workflows (cannot be told) | **ANSWERS**, exit 4 on objectui PR objectstack-ai#9604: `Test (coverage)`, `Test (coverage shard …)`, `dependabot` reported as 3 skips outside the roster — a verdict against the wrong roster (here a false red; a shared job name would make it a false green). Without the env: default board objectstack, PR objectstack-ai#9604 → HTTP 404 → exit 3 NOT MEASURED | first line `board: objectstack-ai/objectui (PM_SWEEP_REPO); roster: this checkout's workflows.` — board ≠ checkout ⇒ verdict void | | `check-clause2-carriers.mjs --pair N` | `PM_SWEEP_REPO` (reads no tree file) | **WORKS**: `every row below is read from objectstack-ai/objectui (source: PM_SWEEP_REPO)`; objectstack-ai#9604 is closed so the pair is not formed (exit 2, explicitly not a clearance). Without the env: `(source: default — set PM_SWEEP_REPO to target another repo)` | the provenance line names board and source | | container proxy, `GET /search/issues?q=repo:objectstack-ai/objectui+is:issue+kanban` | not tellable — session-bound | **refusal in a success-looking body**: HTTP **403**; body is valid JSON with keys `message`, `documentation_url` and **no** `total_count`; a `.get('total_count')` read prints None/0. Repo-scoped control `GET /repos/objectstack-ai/objectui/issues?per_page=1` → 200, one row | the status is 403 (never 200); the key is **absent**, not zero; `documentation_url` points at docs.anthropic.com (the 415 discriminator at platform-readings :118 generalises) | One rule reaches all four: an instrument answers for the repository it is told (`--repo`, `PM_SWEEP_REPO`), defaults to objectstack, and never for the card in your hand; a citation without the repo argument is a clearance for objectstack only. Written once at :206; each citation carries its own half. ### Triage's ask — which repo do the round-open step-1/2 readings answer when the seat works objectui from an objectstack checkout - Step 1, `git-history.mjs touch --path=.claude/skills/pm-dispatch/SKILL.md --ref=origin/main`: from the objectstack worktree → `7f4aa9e5c`, proven, exit 0. From cwd `/home/user/objectui` → exit 1, `no commit on 'origin/main' touches .claude/skills/pm-dispatch/SKILL.md` (the tool also auto-deepened that clone to `--deepen=4096`; object store only, no tree edit). It answers for the cwd's repo; the charter exists only in objectstack, so run from objectstack it answers the one repo that has it, and a sibling cwd answers not-found, never a wrong sha. - Step 2, `check-harness-current.mjs`: default `--shared` is the cwd's git common dir → `CURRENT -- every harness-loaded path on origin/main is in /home/user/objectstack HEAD d6137fd`, exit 0; with `--shared /home/user/objectui` → UNDECIDED, exit 2 (graft boundary at the time). It answers for the checkout it is pointed at and names it in the verdict line. - No SKILL line added: both readings are self-labelling (the verdict names the checkout; the charter path has one home), and the lines that own them (:89–:92) are the serial-queue block of PR objectstack-ai#18656. ## Four-axis reasons for the placement - **实际业务需求** — measured, not recalled: the one instrument that ANSWERS a sibling board is `check-expected-skips` (exit 4 against a foreign roster), and `--commands` without `--repo` prints a well-formed list at exit 0 with the banner on stderr. Both failures are read AT the citation, so the repair sits at :539 / :628 / :645 / contract-review :41; the principle sits once in 〈多仓协调〉 beside the line that says the toolchain lives in objectstack. - **项目长远合理性** — no script change (the card's own correction); the citation becomes the checkable spelling, contract-first: the argument is the contract, the provenance line is the receipt. - **防 AI 写代码犯错** — SKILL.md :168's order: delete the error-permitting construct, make the correct form the only spelling, only then add a check. The argument-less spellings were the construct; the cited spellings now carry `--repo 仓` / `PM_SWEEP_REPO=仓`, the non-retargetable one says 只判 objectstack, and the proxy row says 缺键即拒绝 — an absent key can no longer be read as a zero. - **创业阶段不扩散需求** — no per-repo rosters, no new gate, no ceiling raise; line-neutral, paid by deleting one line restated twice elsewhere. ## Gates - `pnpm check:pm-expected-skips` exit 0 on `4bbddc5a9` (99 cases; the round-1 head `7e78dc6ad` failed its pin `the SKILL.md enqueue bar names this file` because :628 lacked `.mjs`). Run explicitly: the derivation does not name it for a SKILL.md change, and `--ran` files it as 'Outside this card's derivation (1)' — filed as objectstack-ai#18673. - `pnpm check:pm-skill-ratchet` exit 0 — `SKILL.md` 812 lines (ceiling 812; headroom 0), widest table row 342 (pin 342); `contract-review.md` 60/60; `platform-readings.md` 466/466. `pnpm check:skill-frame-sync` exit 0 (one coherent copy; 74 markdown files scanned). `pnpm check:pm-skill-id-lint` exit 0 (27 files clean). `pnpm check:nul-bytes` exit 0. - Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from the worktree (no hand-fed paths): 19 commands, each captured redirect-then-`$?`: Re-run on `4bbddc5a9`: 19/19 exit 0 (doc-formula-expressions after the same prerequisite build through the lock, waited 0s). `check-closing-keyword-parity.mjs` 0 · `check-closing-keyword-parity.mjs --self-test` 0 · `check-comment-mask-corpus.mjs` 0 · `check-governed-queue-guard.mjs --self-test` 0 · `check-harness-current.mjs --self-test` 0 · `@objectstack/lint check:doc-formula-expressions` 0 (first run exit 3 PREREQUISITE NOT MET — unbuilt `@objectstack/formula`/`@objectstack/lint`; built through `os-verify-lock.sh` after one queue-timeout (exit 99, holder: a sibling's `check:pm-dispatch-gates`), then 0) · `check:agent-test-spelling` 0 · `check:doc-authoring` 0 · `check:driver-memory-census` 0 · `check:nul-bytes` 0 · `check:pm-governed-merges` 0 · `check:pm-governed-prose` 0 · `check:pm-half-states` 0 · `check:pm-skill-id-lint` 0 · `check:pm-skill-ratchet` 0 · `check:refd-timer-probe` 0 · `check:required-contexts` 0 · `check:skill-frame-sync` 0 · `check:watch-hint-literal` 0. - `--ran` reconciliation: 19 derived, 19 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. - `check-governed-merges.mjs --test` on the three paths: exit 3 GOVERNED, as expected. - Tier: `--tier --repo objectstack-ai/objectstack` on the touched files → MANDATORY, clause ① from `SKILL.md`. ## Acceptance notes - noted, not filed: `git-history.mjs touch` run from a sibling cwd fetches `--deepen` up to 4096 into that clone before answering not-found — an operator-visible side effect on a checkout the tool does not own (its header says deepening is an operator action). Carrier: none; recorded here for the skills seat. - noted, not filed: `true-green.md` :15–:16 and `platform-readings.md` :392 already state the `dispatch-gates.mjs` cross-repo behaviour for the dev side; this PR adds the PM-side citation and the rule, and leaves those readings in place. ## 维护者速读(草稿) **改了什么**:pm-dispatch 技能文本在四处引用住在 objectstack 的工装(门禁派生 `dispatch-gates`、预期 skip 名单 `check-expected-skips`、条款②配对检查 `check-clause2-carriers`)时,现在每处都写明它答的是哪个仓、拼写里带上仓参数;〈多仓协调〉加一条总则「工装只答被告知的仓,缺参数即答 objectstack,⛔ 不答手上那张卡的仓」;平台读数表里代理拒绝那一行补上判别式(缺键即拒绝,看状态码)。零脚本改动,三个文件行数全部中性。 **为什么改**:一位在 objectui 上工作的席位实测:三件工装三种行为,其中预期 skip 名单会拿 objectstack 的花名册去判 objectui 的板,交出一份它判不了的放行;技能文本里没有任何一行提醒。本 PR 逐件复测并把结论写在被引用的那一行上。 **风险与代价(含回滚)**:纯文本改动。付账方式是删掉 SKILL.md 一行在 `lanes/ui.md` 与〈域车道〉均已重述的规则;:539 为容纳 `--repo` 去掉了「⛔ 不抓人读输出」的措辞(机读模式已由 `--commands`/`--ran` 拼写承载)。回滚 = revert 单个 commit。 **席位意见**:(留给席位) **你要做的(一个动作)**:批准本 PR,或点名要改的那一行。 --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: os-dev <noreply@anthropic.com>
…rries its reading or is marked 未验证 — the 派发令 evidentiary duty extended to 裁决 (objectstack-ai#18679) Fixes objectstack-ai#18506 Clause-②: no `skip-changeset` — the only touched path is `.claude/skills/pm-dispatch/SKILL.md` (governed `.claude/**`, internal agent tooling; it ships in no package's `files[]`). ## The defect, in one paragraph A ruling is consumed exactly like a dispatch order — quoted verbatim into the brief, ⛔ not re-litigable, treated by the dev as settled — yet the charter's evidentiary discipline binds dispatch orders only: 「派发令里关于代码的危害断言必须有读数(点名 call site / 路径 / 迁移)」, 「搬自任何工件(含己文)本轮未亲测计数/零命中/文件面:重测或注「未验证」点名来源」, 「PM 测不了的危害 ⇒ 写成给 dev 的问题,⛔ 不写成栅栏」. Nothing required a ruling's code-level assertions to carry a reading, and nothing marked them unverified when they did not. The artefact with more authority had fewer evidentiary requirements than the one that merely relays it — the asymmetry is the defect. This PR extends the same two options (carry the reading, or mark that clause 「未验证」) to 裁决, so the dev reading a ruling knows which clause to falsify first. ⛔ It is not 「measure before ruling」, adds no gate or script, and re-grades no ruling. ## The evidence: six for six on one split (card comment 5701534399) | ruling | kind of claim | outcome | |:--|:--|:--| | objectui#8938 | asserted a code fact (「every one folds in the restrictive direction」) | broke — `_packageId` presence admits an affordance at 14 of 38 read sites | | objectui#8826 | asserted a code location (`ObjectKanban.tsx`'s `handleDragEnd`) | broke — it lives in `KanbanImpl.tsx` | | objectui#8209 | asserted a call shape (`field.format ?? 'compact'`) | broke — `temporalText(type, value, locale)` takes no `field`; no `format` key on the published type | | objectui#8167 | asserted a mount topology (「each host mount supplies it」) | broke — zero mount sites; registry-resolved | | objectui#7771 | cited standing rules (「一个标签存在当且仅当有具名读者」) | held | | objectui#9537 | cited standing rules (「门禁内部参数与扫描器盲区修复(加强,非削弱)」; ADR-0049) | held — every code fact it named had been READ; its one slip (`:866-870` for `:878`) was inherited from the card body | The rulings that reasoned from rules held; the four that asserted implementation facts broke. The exposure is confined to the moment a ruling names a file, a symbol, a line or a call shape — the same boundary the charter already draws for dispatch orders, which is why the line below carries a rule-citing exemption rather than a measurement duty. ## The change — one line in, one line out, SKILL.md 812 / 812 **In** — `.claude/skills/pm-dispatch/SKILL.md` :731 (work tree), section 升级与决策, between 「卡上裁决评论是详注,仍是执行与复核的第一落点。」 and 「出决策箱须引同趟取回过的裁决 id …」: ```text - 裁决点名文件/符号/行/调用形状的条款,逐条带读数或注「未验证」;只引规则的条款免。 ``` 118 B (cap 120). Reads: a ruling clause that names a file, a symbol, a line or a call shape carries, per clause, the reading behind it or is marked 「未验证」; a clause that only cites rules owes nothing. 读数 is the corpus's defined term (:166–:167: UTC 取数时刻 + ref/tip; AGENTS.md: a count plus the tree it was taken against), and 「未验证」 is the dispatch-order rule's own mark, so the mark travels verbatim with the clause when the ruling is quoted into a 派发令. **Out** — the 分诊座位职责 line at origin/main :309, 95 B: ```text - 代裁只由维护者召唤的总监席产出,⛔ 永不凭自述、⛔ 无子代理裁决。 ``` Every clause of it is already stated in the ruling section this PR adds to: 「总监席是唯一裁决者:决策箱、代裁与一类自裁只出自本席,⛔ 无子代理无自述。」 (:769) covers 代裁 ⊂ {决策箱、代裁、一类自裁}, 只出自本席, 无自述, 无子代理; 「项目总监席:维护者在所在仓召唤 …」 (:765) covers 维护者召唤; :75 「裁决不在本席,归维护者召唤的总监席」 and the red line :30 「唯一例外是已裁的代裁通道」 still tell the triage seat the same thing in its own sections. Deleting the second spelling is the corpus discipline (one rule per line, no rule stated twice), not a weakening — a control grep for the deleted spelling returns 0 and each restating line returns its line. Why no cheaper payment existed: measured on this tree, of SKILL.md's 598 adjacent bullet pairs ZERO merge under the 120-byte cap (smallest 124 B), and in the 升级与决策 section ZERO of 40 (smallest 158 B); re-wrap funding is refused by the ratchet rule. The whole-file diff vs `origin/main` is exactly these two hunks (`1 insertion(+), 1 deletion(-)`). ## Placement — SKILL.md, not core-rules.md, along the four axes - **实际业务需求** — the consumers are the seat writing a ruling and the dev reading it quoted into a brief; both read SKILL.md in full every session. core-rules.md :3 says 「本文不新增规则」 and :43 makes it 「本文的子集」, so it cannot be the sole home; a twin there would cost a second line in a file at 151 / 151 where ZERO of 122 adjacent pairs merge under the cap (smallest 175 B, measured on this tree) and no line restates another — an unrelated compression to fund it, which the ratchet's own precedent refuses. - **项目长远合理性** — the line sits in the block that defines the ruling record (:729 权威载体, :730 卡上裁决评论, :732 出决策箱须引裁决 id), where 裁决 is the generic term — maintainer ruling, 代裁, 一类自裁, PM 裁量, retriage 改判 — so it binds every seat that rules without naming one. :44 (一条规则在本文与核心条款一处改动,另一处同 PR 同改) is not triggered: the new line is new, and the three dispatch-order lines whose twin is core-rules :116 are byte-untouched. - **防 AI 写错** — the mark is mechanical (a clause either has a 读数 or the literal 「未验证」), per clause, and rides verbatim into the 派发令 under the existing 「裁决不可重裁 / 机制假设须实测」 partition; no seat has to judge whether it is a nice-to-have, and a 「不 expected」 built on an unread function is no longer a legal spelling. - **创业阶段不扩散** — one line, zero net lines, no gate, no script, no new label, no re-grade; the rule-citing exemption keeps the cheap reading and forecloses the expensive one. ## Measured: the neighbours do not contradict it, and the quoted lines are untouched Neighbouring text (work tree): :715 「② 把裁决挂在具名、可证伪的前提上,派发令要求 dev 先验前提再动手。」 (a premise-bearing ruling names its premise — compatible: the new line says which clauses of any ruling are the premises); :180 「裁决明令的动作实施中测出对向事实 ⇒ 照字面执行 …」 (what happens when a measurement contradicts a ruled action — unchanged); :427–:432 (the PM's pre-dispatch staleness re-read of a ruling's 动作面/卡引用面 — about the re-read, not about what the ruling carries); :401 「① 既有裁定/在案类规则/成文纪律机械决定方向,裁决评论点名所用权威」 (the class-1 gate is rule-citing by construction — the exemption agrees with it); `references/lanes/director.md` :11 (the ruling comment's fixed first line — unaffected). Byte-identity against `origin/main` `7f7b8557d` (diff of the extracted lines, empty): the three quoted dispatch-order lines (:541–:543 there, :540–:542 here); PR objectstack-ai#18656's :91; PR objectstack-ai#18666's :198–:209, :536–:542 (now :535–:541), :625–:631 (now :624–:630), :642–:648 (now :641–:647). The set of over-120 B lines is unchanged (23, all table rows plus the claim-template line); widest row 342 B, pin 342. Line numbers shift by −1 between the deleted line and the inserted one; no hunk overlaps either PR's hunks. ## Readings Head `cb1a949d0` (`origin/main` `f6c2eb7c8` merged — one unrelated driver-sql commit; the SKILL.md delta unchanged). Derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from the worktree (change set from the merge base, no hand-fed paths): 18 commands, list identical before and after the merge. Exit codes captured redirect-then-`$?`, reconciled with `--ran`: 「18 derived, 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3)」. | 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 — first run exit 3 = PREREQUISITE NOT MET (unbuilt `@objectstack/formula` / `@objectstack/lint`, 「Nothing was measured」); built both under `os-verify-lock.sh` (VERDICT command-exit 0, held 165 s, waited 0 s), then rerun | | `pnpm check:agent-test-spelling` | 0 | | `pnpm check:doc-authoring` | 0 | | `pnpm check:driver-memory-census` | 0 | | `pnpm check:nul-bytes` | 0 — 「OK (scanned 8798 text file(s) … no raw ASCII control bytes)」 | | `pnpm check:pm-governed-merges` | 0 | | `pnpm check:pm-governed-prose` | 0 | | `pnpm check:pm-half-states` | 0 | | `pnpm check:pm-skill-id-lint` | 0 | | `pnpm check:pm-skill-ratchet` | 0 — 「.claude/skills/pm-dispatch/SKILL.md is 812 lines (ceiling 812; headroom 0)」 · 「widest table row is 342 bytes (pin 342; headroom 0)」; core-rules.md untouched at 151 / 151 | | `pnpm check:refd-timer-probe` | 0 | | `pnpm check:skill-frame-sync` | 0 — 「the one declared copy of the decision frame is internally coherent (.claude/skills/pm-dispatch/SKILL.md …)」 | | `pnpm check:watch-hint-literal` | 0 | | `pnpm check:pm-expected-skips` (explicit, not derived; its self-test pins the `check-expected-skips.mjs` literal on the enqueue-bar line, untouched) | 0 — 「99 cases pass」 | | `pnpm check:skill-frame-freshness` (explicit, outside the derivation) | 0 — 「the decision frame in this tree is current with origin/main (fetched just now)」 | `node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/SKILL.md` → exit 3, 「GOVERNED — a human merge is the review record for this PR」 — recorded as the expected answer, not a failure. CI-owned and not run locally: the 52 artifact-roster families, the 11 wide-population families and `pnpm lint` (the derivation names them as outside the 18; none reads this file's content). No package build is owed by the diff (no package touched); the one build above was that gate's own prerequisite. ## Acceptance notes - noted, not filed: `references/lanes/director.md` :9 「裁决只存在于本卡:聊天原话、台账行、他卡引述 ⛔ 都不是裁决」 and SKILL.md :729 「一条裁决已记录的权威载体 = 总监席每场收工的摘要台账」 name different carriers as authoritative for a ruling. Observed only; not a defect class (a)–(c), and outside this card. 承接者:无. - noted, not filed: core-rules.md carries no twin of the new line (reasons above). If the maintainer wants one, it is one core-rules line at 151 / 151 with no duplicate to pay — a ruled raise or a compression of another rule, both the maintainer's call. 承接者:the `domain:skills` seat, at the maintainer's word. ## 维护者速读(草稿) **改了什么** — `pm-dispatch` 技能文件加一行、删一行,行数不变(812 / 812):裁决里凡点名文件、符号、行号或调用形状的条款,要么带上得出它的读数,要么该条注「未验证」;只引规则的条款不欠。删掉的是分诊节里一条与总监席节完全重复的「代裁只出自总监席」拼写。 **为什么改** — 派发令早有这条纪律(代码危害断言必须有读数,未亲测就注「未验证」),但裁决没有。裁决被 dev 当作已定事实照抄进派发令,不可重裁。一轮六张卡实测:引规则的两条裁决全对,断言实现事实的四条全错(文件位置、调用形状、挂载拓扑、折叠方向),每次都靠 dev 自己重新测量才发现。权威更高的工件反而举证要求更低,这是结构性缺口,不是三次失误。 **风险与代价(含回滚)** — 零新增门禁、零脚本、零标签;只约束裁决的写法,不要求先测再裁(引规则免费,只有点名代码事实的条款才欠读数或标记)。代价是出裁决的席位多写几个字。回滚 = revert 这一个 commit,两行原样回来。 **席位意见** — (留空,由席位填写) **你要做的(一个动作)** — 批准这个 PR(受管面 `.claude/**`,需要你的 approve 或亲手合并)。 --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ Co-authored-by: os-dev <noreply@anthropic.com>
Fixes #18636
Clause-②: no
The defect
The round-open step-2 reading at
.claude/skills/pm-dispatch/SKILL.md:91named three paths —.claude/{settings.json,agents/*.md,hooks/*}— while the harness loads the whole.claude/**tree from the primary checkout at session start and never reloads it,.claude/skills/**in full for every seat session (the load momentscripts/pm/check-skill-line-ratchet.mjs's header states). So the charter told the seat to measure a set SMALLER than the set that rots it. The tool half —scripts/pm/check-harness-current.mjs'sPATHSwidened to.claude/skills/**, plus a second CONTENT reading per path — landed in PR #18610; this PR is the charter half that #18544 comment 5706565860 said stays on this file, line-neutral. The card's own true positive: SKILL.md movedab1d359→7f4aa9e5cat a round open and the step-2 reading said nothing, because the seat's copy of the tool was the old one too. The charter has to name the right set on its own, whichever copy of the tool the seat holds.The line, before and after
:91only; nothing else in the file, no reference file, no script. Before (120 B, onorigin/main99fcb4ac1, byte-identical to thecf39b83c0the card was dispatched against):After (96 B):
Payment: in place, line-neutral — 812 / 812, 1 insertion / 1 deletion (
git diff HEAD~1 HEAD --stat), no second line, no re-wrap, no other line moved, the widest table row unchanged (342 B, pin 342). It reads as one block with:92, which is untouched: 「否 ⇒ 记座位贴,自然收班时换会话,⛔ 不中断批次;读数走scripts/pm/check-harness-current.mjs。」 — the 「否」 still answers:91's yes/no question, and:92still names the tool that takes the reading.Two things moved on the line:
.claude/{settings.json,agents/*.md,hooks/*}→.claude/**— the SET, the card's fix.maintakes two per watched path (PLACEMENT: is the commit that last touched the path onorigin/mainan ancestor of the shared HEAD; CONTENT: are the shared HEAD's bytes of the path equal toorigin/main's), and a path is STALE on either. The charter now names both, so a seat holding an old copy of the tool — the card's reading 3 — still knows what it is supposed to measure and can see when the tool's report is short of it.Why the set is the directory and not a longer list — measured, then the four axes
Measured on this tree at
14f751515(git ls-filesper pathspec):.claude/**selects 43 tracked files; the tool's four pathspecs select 1 (settings.json) + 1 (agents/*.md) + 10 (hooks/*) + 29 (skills/**) = 41. The two files outside the four pathspecs are.claude/launch.jsonand.claude/workflows/docs-accuracy-audit.js(both last touched5de93728a; their load moment is unmeasured, as the tool's header says of hooks and agents). Spelling the four pathspecs on the line would cost 131 B — over the 120-byte max-line budget — and would be the file-list spelling the tool's ownPATHScomment names as the failure shape: 「naming the charters one by one is how a predicate ends up guarding less than it claims (the population it covers must be the population that is loaded)」.四维分析(新记录,按章程用中文):
.claude/skills/**, so a seat seating on a stale checkout is never told its charter is stale #18544 评论 5706565860 记录了一个席位整班执行已离开origin/main的旧 :92 处方(车道停摆约 7 小时);本卡读数 2 是同一机制的第二次真阳性(SKILL.mdab1d359→7f4aa9e5c,step-2 读数一字未提)。harness 侧的载入面本来就是目录——check-skill-line-ratchet.mjs头部「read in full by every seat session」说的是.claude/skills/**,不是三个文件;写.claude/**就是写实测的载入面。PATHS,.claude/明天多一个子目录就再次落后——正是本卡的缺陷形态,只是换了个数字;目录写法不随PATHS腐烂。.claude/**与两种读数之后,旧工具只报两条路径时席位能在结构上看出集合不齐;现在的三路径写法让它无从察觉。不写「与上轮」:那是一条形态未定的席位侧账本义务,120 B 里写不清的义务恰是 AI 会写错的那种。main的工具里落地;不加参考文件、不加脚本、不加座位贴字段。目录写法多覆盖的两个文件(launch.json、workflows/*.js)载入时刻未测,按从紧原则不据此扩PATHS,记入验收备注交工具所在车道。The directory-diff suggestion — taken in one half, left in the other
The card proposes writing 「目录 diff 与上轮逐字节比对」 into step 2. Split along the same axes:
origin/main, which PR fix(pm): check-harness-current watches the skills tree, and compares content as well as placement #18610 landed and which:91now names (「与内容」). It is strictly more informative than the seat's byte-count habit the card describes: it names the differing files per path, needs no history, and fires on a checkout that was ALREADY stale at seating.:89–:90already compare the latest touch sha with the previous round-open marker and prescribe the re-read, so a second round-over-round comparison on:91would duplicate them. (ii) For the harness-loaded rest (settings.json, hooks, agents) a re-read is not a remedy — the harness never reloads — so:92is the whole prescription, and "moved this round" versus "already stale" changes nothing the seat does. (iii) A round-over-round ledger is a new seat-side obligation whose form (what to record, where, compared how) does not fit in the 24 bytes the line has left, and an under-specified obligation is the kind an AI seat gets wrong. Where it would go if the seat wants it: the round-open marker's contents on:90(record the tool's named stale paths beside the touch sha), or a since-marker flag on the tool — not:91.Ratchet reading
pnpm check:pm-skill-ratchet(self-test then gate) at14f751515, exit 0:The edited line went 120 B → 96 B, under the 120-byte max-line budget;
.claude/skills/pm-dispatch/SKILL.mdis the only path in the diff;grep -naPfor control bytes over the file: 0 hits.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackfrom the worktree with no hand-fed paths (change set from git: 1 path vs merge base99fcb4ac1, three-dot): 18 commands. Each run with the tool's own capture idiom (redirect, then$?, the printed command recorded byte for byte), then reconciled with--ran: 18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN (exit 0, 「a DERIVED zero — all 18 recorded an exit code and none of them is 3」).node scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjsnode scripts/pm/check-governed-queue-guard.mjs --self-testnode scripts/pm/check-harness-current.mjs --self-testpnpm --filter @objectstack/lint run check:doc-formula-expressionsPREREQUISITE NOT MET(unbuilt closure), not a measurement;pnpm exec turbo run build --filter=@objectstack/lintunderscripts/pm/os-verify-lock.sh(VERDICT command-exit 0, held 141 s, waited 0 s), rerun exit 0pnpm check:agent-test-spellingpnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:pm-governed-prosepnpm check:pm-half-statespnpm check:pm-skill-id-lintpnpm check:pm-skill-ratchetpnpm check:refd-timer-probepnpm check:skill-frame-syncpnpm check:watch-hint-literalNot derived, run anyway:
node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/SKILL.md→ exit 3, GOVERNED (1 of 1 path hits the register) — the expected answer for this path, recorded as such and not as a failure.pnpm check:pm-settings-deny-roster(the derivation flags its roster as living under.claude, so its silence is evidence in neither direction) → exit 0, 17 declared = 17 enforced.node scripts/check-skill-frame-freshness.mjs --no-fetch→ exit 0.dispatch-gates.mjs --tier --repo objectstack-ai/objectstack .claude/skills/pm-dispatch/SKILL.md→ the MANDATORY tier the claim comment'sContainer & modelline carries, derived from the file surface; this run was at that tier.pnpm lint(the repo-wide union) is CI's run; locally it narrows to zero files by measurement, not by skipping: (1) population read fromeslint.config.mjsitself — everyfiles:glob is{ts,tsx,mts,cts,js,jsx,mjs,cjs}, no markdown; (2)npx eslint --no-inline-config --format jsonon the one changed path → 1 file, 「File ignored because no matching configuration was supplied」, 0 errors, exit 0; (3) invariance: the diff touches no file eslint lints, so no untouched file's verdict can move. Not measured locally: the 52 artifact-roster families, the 11 wide-population families and the 14 pending-changeset families the derivation lists outside its runnable total — CI's, as the derivation says.Changeset
skip-changeset, measured on the fast track:.claude/**is repo-internal agent tooling, in no package'sfiles[]; nothing published moves.Acceptance notes
14f751515the tool's fourPATHSpathspecs select 41 of the 43 tracked files under.claude/; the two outside are.claude/launch.jsonand.claude/workflows/docs-accuracy-audit.js. Whether the harness loads either at session start is unmeasured (the same status the tool's header gives hooks and agents), so this is an observation, not a contract violation; 承接者: the tool's lane (domain:devx,scripts/pm/check-harness-current.mjs), if a load-moment measurement ever lands.check-doc-formula-expressionsrefuses with exit 3 on a fresh worktree until@objectstack/lint's closure is built; its refusal text names the exact fix and the derivation classifies 3 as NOT-MEASURED, so nothing is hidden — 承接者:无.维护者速读(草稿)
.claude/skills/pm-dispatch/SKILL.md第 91 行一行。开轮 step-2 的 harness 读数原来只点名三个路径.claude/{settings.json,agents/*.md,hooks/*},现在点名整个载入面.claude/**,并把工具在main上已落地的两种读数(最新触碰、内容)一起写进去。812 / 812 行不变,该行 120 B → 96 B,别处一字未动。.claude/**(.claude/skills/**每个席位都整份读),章程却让席位只量三个文件——量的集合比会腐烂的集合小。[finding] check-harness-current's PATHS omits.claude/skills/**, so a seat seating on a stale checkout is never told its charter is stale #18544 评论 5706565860 记录过一个席位整班执行旧规则、车道停摆约 7 小时;本卡又撞上一次(SKILL.md 动了,读数一字未提,因为席位手里的工具也是旧的)。工具那一半 PR fix(pm): check-harness-current watches the skills tree, and compares content as well as placement #18610 已合;这是章程那一半。14f751515),不牵连任何脚本。.claude/**,须维护者亲手或授权批准后由席位落地)。Generated by Claude Code