Skip to content

Commit 6427ee2

Browse files
os-justinclaude
andauthored
skills(pm-dispatch): a reading another agent acts on owes its UNIT and a counterfactual — the discipline guarded a ZERO only (#18809)
Fixes #18798 Clause-②: no Governed rules layer, one file, line-neutral: `.claude/skills/pm-dispatch/references/core-rules.md` 151 → 151. This PR **stays draft** — no ready flip, no reviewer request, no arming; the skills seat hangs the four-piece and the maintainer lands it. `skip-changeset`: `.claude/**` publishes nothing (fast-track class). ## The gap The reading discipline makes exactly **one** mechanical demand of an instrument — core-rules :45 「零命中须配同主体必中词,否则该零作废;同仪器的控制词双零是仪器坏,⛔ 不读作缺席。」 — and it guards a **ZERO**. Nothing in the protocol made a seat prove that a **non-zero** reading counted the thing it claimed to count, and the card's measured cost is eight instances in one shift (six on the card, two the triage seat measured on itself), five of them non-zero and every one of them already written into a card, a handover brief or a dispatch where another agent acted on it. The card's own boundary — ⛔ not a blanket "every number needs a control", because that tax falls hardest on the cheap probes that make the loop affordable — is what decides the scope: the demand binds a reading **another agent will act on**, and a seat's own use-and-discard probe owes nothing. ## The row, verbatim Added at `core-rules.md` :46, directly under the zero rule it generalises (119 bytes; the file's cap is 120): ```text - 他人据以行动的读数须带单位并答什么本来会让它不是这个值;递 dev 的恒标线索非答案。 ``` Three clauses: ① the reading states its **unit** (files? occurrences? lines? bytes?); ② it answers 「什么本来会让它不是这个值」 — the triage's second question, verbatim in substance; ③ a reading handed to a dev as a **lead** is always marked a lead, not an answer. The scope word 「他人据以行动的」 is the boundary in positive form and is the card's own criterion (「另一个 agent 会据此行动的地方」); a probe a seat uses and discards is not a reading this row binds. ## The eight instances, against the row's clauses | # | instrument | what it actually answered | caught by | |:--|:--|:--|:--| | 1 | `startswith("Blocked-by:")` over issue bodies | bodies only, line-initial, undecorated — the comment channel and `**Blocked-by:**` lines are invisible to it | ② — the honest answer names the corpus, and a `Blocked-by:` in a comment moves the number not at all (cost: two non-defects entered a handover brief as work items) | | 2 | `grep -E '^[-+][^-+]'` over a charter diff | the document's lines start with `- `, so its changed diff lines read `--` and the pattern excluded them | ② — answering it requires a known-changed line as a positive control (cost: "almost nothing changed", really 46 + 14 lines including a reversed rule) | | 3 | a starred control reading "this package's routes do i18n lookups" | every hit was a comment or a metadata key name | ② — if the routes did **not** do lookups the grep still reads non-zero, which disproves the reading; ① alone does not catch it (cost: an option costed an order of magnitude low) | | 4 | a `package_version_id` grep handed to a dev as a lead | the zero was true; the mechanism was elsewhere | ③ — neither ① nor ② catches this one: the reading is correct and still points the wrong way. This is the instance the lead clause exists for, and the R39 dispatch that said "this is a lead, not an answer" is what saved it | | 5 | `git grep -c … \| wc -l` to count call sites | it counts **FILES** | ① directly (unit: files, not call sites) and ② as well (a second call site in the same file moves nothing); reported 5, there are 9 | | 6 | a **paraphrased** calibration case (cloud#2020, the card's origin) | it passed for the wrong reason — the paraphrase dropped the sentence carrying the cue, so the case exercised a path the real corpus never takes | ② — "what would have made this case NOT pass" is answerable only against the verbatim excerpt it is not; the dev named the class itself: the same class as an ablation that passes because nothing was mutated | | 7 | the triage seat's own three-set endpoint diff, residual **0** | structurally blind to same-round in-and-out: a card that entered and left in the same round appears in neither column | ② — "what would have made the residual non-zero" forces enumerating the transitions the three sets can represent; ① does not catch it (the unit, cards, is right) | | 8 | the triage seat's own `#18791` grep — unit right, control lit, count true | it answered **the wrong question**: who has already written the bad value, not who is TEACHING it (the gate's own `fix` string, in the same output) | **NOT caught** — see below | Instance 8 is the boundary and it is stated as a measured disagreement with the triage's expectation, not smoothed over. The triage proposed ② as the question that catches the zero, the non-zero **and** the unit-correct-but-wrong-question shape. Measured against this row as written, it does not: ① is satisfied, ② is answerable ("an authored block carrying the bad value would have made it non-zero"), and the reading was true. What fails there is the QUESTION, which is exactly the shape of sibling card #18755 (「a lit control certifies the INSTRUMENT, not the QUESTION」) — a card the triage's own dedupe ruling keeps separate and this PR does not fold. One wording would reach it — binding ② to the CONCLUSION the reading is carried for ("what reading would have overturned the call this number is used to make") rather than to the instrument — and it was refused on the axes below: it costs a re-derivation of the decision per reading, and it would silently absorb #18755 while that card is open and graded on its own. ## ① + ② or ② alone — the four axes Written: **① + ②**, both, in one row. The card's original candidate (① alone) is refused outright by instance 8, and was already refused by the triage. - **实际业务需求** — measured on the eight instances, not on which question reads better. ① alone catches 1 of 8 (#5). ② alone catches 6 of 8 and misses #5's cheapest catch only in the sense that it takes a constructed counterfactual to get there. Neither covers the set; ① costs one word per reading and buys the one instance whose defect is purely a unit error, so dropping it saves no budget and loses the cheapest catch in the corpus. - **项目长远合理性** — they are different kinds of obligation and collapsing them hides the cheap one behind the expensive one: ① is a FORMAT demand that makes a number self-describing when the next seat re-reads the card weeks later; ② is an EVIDENCE demand about the instrument. A unit-less number in a landed card cannot be re-checked by anyone at all. - **防 AI 写代码犯错** — the deciding axis here. ② can be discharged with a plausible sentence, and an AI seat is very good at plausible sentences; ① is mechanically refusable by a reader (is there a unit word beside the number, or not?). Keeping the loud, checkable half is the contract-first choice, exactly as a strict schema beats a tolerant consumer. - **创业阶段不扩散需求** — the tax is bounded by the scope clause, not by dropping a question: readings that go into a card, a brief or a dispatch are a small fraction of the probes a round fires, and the card and the triage both drew that boundary. ⛔ No blanket demand on every number is written or implied. **What the one-row budget cut**, and where it should land: the explicit negative form of the boundary (「⛔ 自用即弃的探针不欠此税」) and the discharge form of ② (「答案取逐字工件或具名反例」 — the cloud#2020 rule, an instrument either measures the real artefact or carries a control that establishes what it is counting). Both are named here so the seat can queue them; the natural carrier is the SKILL.md twin this PR owes (below), where the reading discipline already spends two lines on the zero rule. ## The row paid, in this file `core-rules.md` :51 is **retired**. It carried three clauses: | clause | disposition | |:--|:--| | 「分诊查重零命中须控制词」 | **the payment** — a near-duplicate: :45 already states the zero rule generally and unconditionally, so the dedupe instance adds no mechanical demand. This is the duplication the card itself names | | 「子代理自死不等于维护者中止,需显式信号」 | **merged into :111**, the claim-reclamation rule it governs: 「- dev 自死不等于维护者中止,需显式信号;回收前先救工作树,有提交的活分支 ⛔ 永不回收。」 (119 B). `dev` is SKILL.md :174's own word for the subject | | 「立卡者只附查重词」 | **merged into :80**, the execution-versus-triage division of labour it belongs to: 「- 执行席 ⛔ 跳过分诊动作,读到标签当既成事实;立卡者不查重、只附查重词。」 (102 B). The added 「不查重」 is SKILL.md :176's own spelling, not new content | So the diff is three insertions and three deletions in one file: one row retired, two rows extended, one row added. No live rule was deleted. ⛔ Re-wrapping was not used as currency — the only content dropped is the duplicated clause. Two payment shapes were weighed and refused. An adjacent-pair merge: the ratchet's own comment on this file measures ZERO of its adjacent bullet pairs merging under the 120-byte cap (smallest 156 B), and re-measuring by hand reproduced it. A merge of the two preamble lines :2–:3: it frees a line, but every spelling that keeps 「本文不新增规则」 verbatim lands at 133 bytes or more, so it can only be bought by deleting the file's own self-description — worse content to spend than a duplicated clause. ## Mirror reading — must SKILL.md :163 move in lockstep? **By gate: no. By the protocol's own binding text: YES, and this PR does not carry that half.** - No gate compares the two documents. Instrument: `git grep -l 'core-rules'` over `scripts/ .github/ package.json` reads **2 files** — `scripts/pm/check-skill-line-ratchet.mjs` (counts lines only) and `scripts/pm/dispatch-gates.mjs` (derives gate families). Lit controls on the same instrument and corpus: `platform-readings` reads 7 files, `SKILL.md` reads 40 files; absent control (a nonsense token) reads 0, exit 1. A second instrument agrees: `git grep -n '核心条款'` over `scripts/ .github/` reads exactly **one** line — a COMMENT at `check-skill-line-ratchet.mjs:1126` quoting the rule — with 受管面 lit as control in the same corpus. - The binding text is unambiguous. SKILL.md :43 「核心条款住 `references/core-rules.md`,是本文的子集」, SKILL.md :44 「一条规则在本文与核心条款一处改动,另一处同 PR 同改」, and `core-rules.md` :3 「本文不新增规则」. A rule that exists in core-rules and not in SKILL.md breaks the declared subset relation. - Both reference PRs named in the dispatch carried the twin in the same PR: PR #18511 re-keyed SKILL.md :522 / :640 **and** core-rules :112; PR #18481 re-keyed SKILL.md :390 / :391 / :403 / :404 / :712 **and** core-rules :95 / :96 / :137, and declared in its own body the one item that has no core-rules twin. Two of two. - **⇒ The owed half, for the seat to queue:** SKILL.md's reading-discipline block (:163–:164) gains the same rule. That half is not in this diff because SKILL.md is serial behind PR #18666 / PR #18679, which is the seat's ruling, not a reading this PR takes. Until it lands, `core-rules.md` states one rule SKILL.md does not, and no gate will say so — which is itself a rule with no paired mechanical action, in a file about rules with no paired mechanical action. It is in the report's out-of-scope findings as a class (b) card for the seat to file. `check:skill-frame-sync` was run and is green, but it judges the four-axis DECISION FRAME's coherence and scans for undeclared copies of that frame — it is not a rules-row mirror and its green says nothing about the twin obligation above. ## Sibling cards, read and not folded - **#18755** (p2, 「a lit control certifies the INSTRUMENT, not the QUESTION」) — read in full. This row narrows its gap but does **not** close it, and that is the honest reading of instance 8 above: the row binds readings that reach a card, a brief or a dispatch, so a near-miss heading into a finding (its `check-validity-recogniser` case) would be inside the demand, while a seat's own discard probe stays outside it by design. The uncovered half is the shape #18755 names — a live instrument, every control satisfied, pointed at a question the instrument's DOMAIN cannot answer. That card is not addressed here. - **#18744** (p2, HELD) — read. It is three fact rows in `platform-readings.md` resting on a falsified rationale; different file, different instrument, nothing to fold. Not addressed here. - `platform-readings.md` :226–:227 are untouched (#18774 is on that file); `SKILL.md` :163 is untouched (serial, above). ## Verification, head `46b7bd6a46` - **Ratchet before / after.** At `main` (`331462d11f`, the branch base): 「✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/core-rules.md is 151 lines (ceiling 151; headroom 0).」 At this head: byte-identical line. Widest introduced row 119 B (:46 and :111); :80 is 102 B; no line in the file exceeds 120 B (measured over all 151 lines). - **Diff.** `git diff --stat 331462d HEAD` → `1 file changed, 3 insertions(+), 3 deletions(-)`. - **Derived gates.** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from the worktree with no hand-fed path list → 16 families. All 16 run in the foreground, each exit code captured by redirect then `$?`: ```text node scripts/check-closing-keyword-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 node scripts/check-comment-mask-corpus.mjs :: exit 0 node scripts/pm/check-governed-queue-guard.mjs --self-test :: exit 0 node scripts/pm/check-harness-current.mjs --self-test :: exit 0 pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 pnpm check:agent-test-spelling :: exit 0 pnpm check:doc-authoring :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:pm-governed-merges :: exit 0 pnpm check:pm-skill-id-lint :: exit 0 pnpm check:pm-skill-ratchet :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:skill-frame-sync :: exit 0 pnpm check:watch-hint-literal :: exit 0 ``` Reconciliation with `--ran`: 「✓ dispatch-gates --ran: 16 derived famil(ies) accounted for — 16 run, 0 NOT-MEASURED (a DERIVED zero — all 16 recorded an exit code and none of them is 3)」. `check:doc-formula-expressions` first answered **exit 3, PREREQUISITE NOT MET** (`@objectstack/formula` and `@objectstack/lint` unbuilt) — not a finding and not a measurement; both packages were built under `scripts/pm/os-verify-lock.sh` (「VERDICT command-exit 0 · held the lock 1s · waited 0s」) and the gate then answered exit 0. - **Two more, run because the derivation named their rosters rather than cleared them:** `pnpm check:pm-settings-deny-roster` (its roster sits under `.claude`, where this path is) exit 0, and `node scripts/check-skills-token-ratchet.mjs` exit 0 (「34 authored bundle file(s) within their ceilings」 — the published catalog is untouched). - **Repo-wide `pnpm lint`** (`eslint . --no-inline-config`): exit 0, no output. - **Control characters:** `pnpm check:nul-bytes` exit 0 (「scanned 8850 text file(s) … no raw ASCII control bytes」), plus a direct sweep of the edited file for the C0 range and DEL: no match, exit 1. - No ablation or reverse-verification leg: a prose rule with no runtime and no `dist`. The before/after measurement is the diff plus the ratchet pair above. ## Acceptance notes - **To file (class b), handed to the seat, not filed here:** the twin obligation SKILL.md :44 declares (「一条规则在本文与核心条款一处改动,另一处同 PR 同改」) and the subset relation SKILL.md :43 / core-rules :3 declare are enforced by **no gate** — the only occurrence of 核心条款 under `scripts/` is a comment quoting the rule. Declared coverage, measured false; this PR is itself the instance. Dedupe words: 「核心条款 同 PR 同改」 · 「core-rules SKILL.md 无镜像门禁」 · 「本文不新增规则」 · 「twin line SKILL.md:44」 · 「digest subset drift」. - noted, not filed: `core-rules.md` :5 「PM 不写任何文件」 and :23 「PM ⛔ 不写文件也不写代码」 state the same prohibition twice, one in 红线 and one in 全体座位的不变量; a future row needing payment in this file can merge :23 with :24 (whose 「⛔ 不得自审自合」 is already carried by :5) for one line, at 115 B. Handler: the next card that has to pay a row in this file. - noted, not filed: `core-rules.md` :43 「不取本地工作树」 and :44 「⛔ 不用共享检出树」 overlap; folding them frees bytes but not a line. Handler: none. ## 维护者速读(草稿) **改了什么**:读数纪律原来只有一条机械要求,而且只管「零命中」。这个 PR 在它正下方加一行,把要求延伸到**别人会据此行动的非零读数**:写下单位(是文件数?命中数?行数?),并回答「什么本来会让它不是这个值」;另外,递给 dev 当线索的读数一律标明「这是线索不是答案」。自己用完就丢的探针不欠这笔税 —— 卡面和分诊席都明确拒绝「每个数字都要配控制」,这一行也拒绝。付账在本文件内:第 51 行退休(它第三句只是把 :45 的零命中规则在查重场景重说一遍),它另外两句分别并进 :80 和 :111,行数 151 → 151。 **为什么改**:一个班次里量到八个实例,五个是非零读数,其中六个已经写进了卡、交接简报或派工单 —— 两条非缺陷被当工作项交接、一次「几乎没变」实际是 46+14 行含一条方向反转的规则、一张卡的方案被低估一个数量级、一条方向错的线索递给了 dev(只因为派工单写了「这是线索」才没造成损失)、`git grep -c | wc -l` 数的是文件报了 5 实际 9。这些不是「可能会出错」,是已经发生的事故链。 **风险与代价(含回滚)**:零代码、零脚本、零发布物,16 个派生门禁加仓级 lint 全绿。真实代价是每条进卡/进简报/进派工单的读数多写一句话;拒绝扩大到每个数字,就是为了不把便宜探针压死。回滚 = revert 本 PR 的一个 commit,文本回到今天的样子。⚠️ 一个已知缺口请你知情:按 SKILL.md :44 的规定,一条规则改一处、另一处必须同 PR 同改,而 SKILL.md 眼下排在 PR #18666 / #18679 后面串行,所以这一趟只落了核心条款这一半;另一半由席位排队补上。没有任何门禁会告诉你这件事 —— 这本身也写进了待立卡清单。 **席位意见**:(留空,席位定稿) **你要做的**:按四件套流程批准本 PR(规则层,等你的字)。合并后席位负责把 SKILL.md 那一半排进队列。 --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b3e437d commit 6427ee2

2 files changed

Lines changed: 5 additions & 5 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
162162
- ⛔ 不用共享检出的工作树核验 main,它的 HEAD 由别的 agent 摆布。
163163
- 零命中须配同主体必中词,否则该零作废;同主体 = 同语料/路径形/包界/引法/失效形态。
164164
- 同仪器的控制词双零是仪器坏,⛔ 不读作缺席:换法重画再报;哑火仪器与干净结果同值。
165+
- 他人据以行动的读数须带单位并答什么本来会让它不是这个值;递 dev 的恒标线索非答案。
166+
- ⛔ 自用即弃的探针不欠此税;「不是这个值」的答案取逐字工件或具名反例。
165167
- 仓不可达 ⛔ 不当查过了干净:在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
166168
- 板面/树/队列读数恒带 UTC 取数时刻;认领、派发令、复核、轮报与座位贴皆同。
167169
- 时间戳形如 `YYYY-MM-DDThh:mmZ`,树读数另带 ref 或 tip;无时间戳的读数按未取处理。
@@ -476,7 +478,6 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
476478
- 引当次 `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier <paths>` 输出⛔ 不凭记忆
477479
- 末行 Serial constraints cleared 是落在评论里的读数,同包在飞单不点名等于没查。
478480
- 本卡 pin 断言兄弟卡在改的行为 ⇒ 派发令注明,并在用例内预登记翻转触发词,⛔ 不修绿。
479-
- 文件面申报到区域级,每单必填;分支名必须带 issue 号。
480481
- 家族派发的折叠认领:共享分支按链首卡命名;每张成员卡各留认领评论并点名该分支。
481482
- 折叠的一 PR 正文多行 `Fixes #<n>` 每成员一行、逐卡 commit;完整串行约束检查落链首认领。
482483
- 限流不拆原子对:发不出评论就撤 assign 或不开始;配额尽则整对排队,永不留半个。
@@ -579,7 +580,6 @@ PM 的工作是循环:选卡 → 认领 → 派发 → 收集 → 复核 → 报
579580
- 停摆 ⇒ 立刻 SendMessage 附前台执行姿态句,⛔ 不等任何静默阈值(阈值是给没有回答的)。
580581
- 复位走梯度,每次比上一次更具体;第三次停摆判 unreliable,按接手协议重派。
581582
- 通知重放先算身份再读内容:与已验收那份同身份 ⇒ 记重放即结束。
582-
- 重放 ⛔ 不重新验收、不重复 ACCEPT;通知到达不读作还活着,不到达也不读作已死。
583583
- 直接验收兜底:(a) draft PR 在且 CI 全绿 + (b) 探活确认已死或 ≥2h 无推送 + (c) 报告未达。
584584
- 三条件全立 ⇒ 直接按 PR 验收,复核判据不减;先探活后翻 ready。
585585
- 舰队级死因(全账号断粮)下取 (a)+(c) 照常收口,PR body 就是报告。

‎.claude/skills/pm-dispatch/references/core-rules.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@
4343
- 判据取命令输出,⛔ 不取 API 字段字面值、不取本地工作树、不取看着相邻的日志。
4444
- 核验 main 用 fetch 后的 `origin/main`,⛔ 不用共享检出树;仓不可达 ⛔ 不当查过且干净。
4545
- 零命中须配同主体必中词,否则该零作废;同仪器的控制词双零是仪器坏,⛔ 不读作缺席。
46+
- 他人据以行动的读数须带单位并答什么本来会让它不是这个值;递 dev 的恒标线索非答案。
4647
- 板面、树与队列读数一律自带 UTC 取数时刻,无时间戳的读数按未取处理。
4748
- 失效修法按序取:先删掉容许出错的构造,再让正确形态成为唯一拼写,最后才加检查。
4849
- 自设定时器的文本以先重读状态开头,只写关键判据,⛔ 不写可直接执行的祈使句。
4950
- PM 写进 GitHub 的文本 ⛔ 不用尖括号占位符,携带易损片段的评论发出后回读。
5051
- 放行认承载门禁族的 job 结论 ⛔ 不认聚合读数,advisory 红着进 main 即止血并立单。
51-
- 子代理自死不等于维护者中止,需显式信号;立卡者只附查重词,分诊查重零命中须控制词。
5252
- 默认读序 git、payload、REST、MCP;REST 可达性逐会话探一次;派发前读配额不够就减批。
5353
## 多仓协调
5454
- 依赖方向固定:objectstack 提供唯一契约,objectui 与 cloud 消费它。
@@ -77,7 +77,7 @@
7777
- 换班报告默认零建议,只收原则错缺、可机械化项与平台事实变化三类。
7878
- 交接按收尾清单走完;强制接管 ⛔ 不取在飞卡,由原认领者跟完;归档自派会话不可移交。
7979
## 分诊座位职责
80-
- 执行席 ⛔ 跳过分诊动作,读到标签当既成事实。
80+
- 执行席 ⛔ 跳过分诊动作,读到标签当既成事实;立卡者不查重、只附查重词。
8181
- 两级盘点是小时轮读增量加每日全量对账,枚举须比对总数、不等即报不完整并点名缺口。
8282
- sweep 扫全裸卡、队列卡缺域或缺定级、域卡缺 pm 状态,排除跟踪、停放与协议载体卡。
8383
- 首触定级跑到清空 ⛔ 无总量预算、未跑完断点续跑;清得空最新优先,否则最老优先。
@@ -108,7 +108,7 @@
108108
- 取卡全序:插队卡、有下游依赖者的卡、板上项、p1、p2、p3、无级;同级缺陷卡先再卡龄。
109109
- 认领原子对:一次标签写入完成认领与状态对调,随后留 `Claim:` 开头的评论并重读全线程。
110110
- 更早的他会话认领即让行并交出已诊断的一切;认领逾一天且无合并证据即疑死。
111-
- 回收前先救工作树,有提交的活分支 ⛔ 永不回收。
111+
- dev 自死不等于维护者中止,需显式信号;回收前先救工作树,有提交的活分支 ⛔ 永不回收。
112112
- 一单一次派发,档位逐卡显式传参;语义面卡恒契约复审档施工,契约卡达档复核归派发席。
113113
- 派发词 ⛔ 不整段粘贴 issue 正文,只带增量,并要求子代理自查正文完整性。
114114
- 派发词分三区:裁决不可重裁、机制假设须实测、建议路线可换,⛔ 不把假设写成裁决。

0 commit comments

Comments
 (0)