Skip to content

docs(pm-skill): flip the gate asymmetry — adding a required gate/hook/ratchet joins the human floor; census-marked retirement becomes class-1 (ruling F) - #18481

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-18473-gate-asymmetry-flip
Sep 16, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/issue-18473-gate-asymmetry-flip

Conversation

@hotlong

@hotlong hotlong commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Fixes #18473

Maintainer ruling F (verbatim 「F」, 2026-09-16 ~14:4xZ) on the direct-channel question 「我们的项目是不是门禁用的太重了。有这个必要吗?」. The card body's "Why" and "Deliverable" sections are the spec; this PR is the two re-keys, line-neutral, every touched line ≤120 bytes.

Surface: .claude/skills/pm-dispatch/SKILL.md (812 → 812 lines) and .claude/skills/pm-dispatch/references/core-rules.md (151 → 151 lines). Governed rules layer: this PR stays draft; no ready flip, no review request, no arming — the director does the four-piece after ACCEPT and the maintainer merges. skip-changeset: .claude/** publishes nothing (fast-track class).

Serialisation: #18468's PR #18476 (SKILL.md:92) is still an open draft; #18434 (SKILL.md:523) remains queued. Neither line is touched here — both are hash-identical to origin/main (285f1046… / 557a3bc1…). origin/main had not moved from the branch base 85c6d76e at the pre-PR os-regen-merge.sh run (Already up to date), so nothing was merged under this branch yet; the merge is re-run before enqueue per the claim.

What changed — every line, before / after, byte counts

Line numbers are on origin/main at 85c6d76e (the card body's :391–392 had drifted by one; on this base the 人工地板 pair is :390–391 and the class-1 floor line :403 reads ③, as the dispatch says).

1. Adding joins the human floor (SKILL.md :390–391, re-wrapped in place)

:390 before (118 B):
- 人工地板同含:安全/权限边界;门禁削弱(降阈值、删必查项、抬 ratchet 上限、跳过测试)。
:390 after (120 B):
- 人工地板同含:安全/权限边界;门禁削弱(降阈/删必查/抬上限/跳测);新增运行时第三方依赖;

:391 before (100 B):
- 人工地板同含:花费/配额/舰队形态/默认模型档位;新增运行时第三方依赖。
:391 after (119 B):
- 花费/配额/舰队形态/默认模型档位;新增必需门禁/hook/棘轮(卡具名所守契约与预期捕获)。

How the added words were paid, in place: the four weakening examples are compressed (降阈/删必查/抬上限/跳测, 、 → /), 「抬 ratchet 上限」 becomes 「抬上限」 (the spelling :392 and :785 already use), and :391 drops its repeated 「人工地板同含:」 prefix because :390 now ends in ; and :391 continues it. Every existing floor item survives verbatim (安全/权限边界 · 门禁削弱 · 花费/配额/舰队形态/默认模型档位 · 新增运行时第三方依赖). The new item is 新增必需门禁/hook/棘轮 with the card requirement (the card names the contract it guards and the catch it expects). The ⛔ "no longer lands zero-baseline on a seat's own warrant" half is carried by floor membership itself — :389 「代裁人工地板(恒交维护者)」 — and did not fit as separate words; stated here so the reader knows it is implied, not written.

1b. 具名不升级类 scoped to an EXISTING gate's internals (SKILL.md :712)

:712 before (110 B):
- 纯文档措辞更正(无契约声明改动);门禁内部参数与扫描器盲区修复(加强,非削弱)。
:712 after (117 B):
- 纯文档措辞更正(无契约声明改动);既有门禁内部参数与盲区修复(加强,非削弱,非新增)。

「既有」 plus the explicit 「非新增」 close the ride: a new gate cannot claim this class. Paid by dropping 「扫描器」 (the subject 门禁 carries the scanner sense).

2. Retiring becomes class-1 (SKILL.md :403–404, same list)

:403 before (110 B):
- ③ 四类地板零移动:安全/权限边界、已发布契约语义、产品能力取舍、门禁强度。
:403 after (108 B):
- ③ 四类地板零移动:安全/权限边界、已发布契约语义、产品能力取舍、门禁强度;

:404 before (77 B):
- 任一地板触碰 ⇒ 照常现场呈报;地板是排除项不是权重。
:404 after (119 B):
- 门禁普查标 retire 的退役除外,总监/技能席引该行裁;触地板即呈报,地板是排除项非权重。

:403 has 10 B of slack, so the carve-out could not sit inside it; per the ruling it is paid by tightening the adjacent line of the same list: :403 now ends in ; and ③ continues into :404, which opens with the carve-out (retiring a gate the gate census marks retire is not 门禁强度 movement; the director or the skills seat rules it citing that census row) and keeps both of its own clauses (触地板即呈报 · 地板是排除项非权重; the 「照常现场」 sense is carried by :407 「三类(地板)照旧现场呈报」). The census's three-part meaning of retire (process class, zero real catches, guards no published contract) lives in the census document and is not restated. The census file docs/audits/gate-census-*.md is #18472's deliverable and is not on origin/main yet — the rule names it as 门禁普查 (a forward reference; no gate reads that path).

3. core-rules.md twins, re-keyed identically (一条规则两处同改)

:95 before (111 B):
- 人工地板恒交维护者:功能新增、ADR、协议与公开契约变化、破坏性或难回滚动作。
:95 after (119 B):
- 人工地板恒交维护者:新功能、ADR、协议/公开契约变化、破坏性/难回滚动作、门禁削弱。

:96 before (116 B):
- 人工地板同样含安全与权限边界、门禁削弱、花费配额与模型档位、新增运行时依赖。
:96 after (120 B):
- 地板含安全/权限边界、花费配额与模型档位、新增运行时依赖、新增必需门禁/hook/棘轮。

:137 before (119 B):
- 记账事不升级(无产品可见行为变化):去重并卡、台账整理、纯文档措辞、门禁盲区加强。
:137 after (119 B):
- 记账不升级(无产品可见行为变化):去重并卡、理台账、纯文档措辞、既有门禁盲区加强。

Declared deviation: core-rules' human-floor list is the two-line block :95–96 with 4 B of slack in :96, and the identical re-key needs 33 B, so the block was re-wrapped across both lines exactly as the SKILL.md pair was — 门禁削弱 moved up to :95, 功能新增 → 新功能, 与/或 → /. Every item of both lines survives; :95 is the twin of SKILL.md :389, which the ruling did not touch, so this is the one place the payment crossed into a neighbour line. :137 pays 「既有」 with 记账事 → 记账 and 台账整理 → 理台账, keeping the class boundary phrase 「无产品可见行为变化」 byte-identical to SKILL.md :711. core-rules has no twin of the class-1 floor line (no 自裁/零移动 line exists there), so item 2 has no core-rules half. No other reference file twins any changed phrase (grep -rn 门禁削弱|门禁强度|加强,非削弱|门禁盲区加强|四类地板 over .claude/ docs/ AGENTS.md skills/ hits only the two files).

Positive greps, with controls (origin/main = 85c6d76e)

grep now control on origin/main
grep -c '新增必需门禁/hook/棘轮' SKILL.md 1 0
grep -c '新增必需门禁/hook/棘轮' core-rules.md 1 0
grep -A1 '③ 四类地板零移动' SKILL.md | grep -c '门禁普查标 retire' 1 0
grep -c '既有门禁内部参数' SKILL.md 1 0
grep -c '既有门禁盲区加强' core-rules.md 1 0
grep -c '加强,非削弱)' SKILL.md (old spelling, no 非新增) 0 1
git grep -n '新增' SKILL.md | grep -c 人工地板 (the card's own grep) 2 2

The card's own grep is control-insensitive: :389 (功能新增) and the runtime-dependency item already paired 新增 with 人工地板 on origin/main, so it reads 2 both before and after; the first five rows are the discriminating evidence.

Gates — run on the final head 45aeee5d, exit codes captured before any pipe

node scripts/pm/dispatch-gates.mjs --commands (no paths; changeset from the merge-base) derived 17 families — byte-identical to the list derived from the two paths the dispatch named. --ran reconciliation on the exit-coded record: 17 derived, 17 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero — all 17 recorded an exit code and none is 3).

command exit
pnpm check:pm-skill-ratchet — SKILL.md is 812 lines (ceiling 812; headroom 0) · core-rules.md is 151 lines (ceiling 151; headroom 0) 0
pnpm check:pm-skill-id-lint — 27 file(s) clean 0
pnpm check:skill-frame-sync — the one declared copy of the decision frame is internally coherent 0
pnpm check:skill-frame-freshness (outside the derived union, run because the dispatch named it) — current with origin/main 0
pnpm check:pm-governed-prose — 2 instruction surface(s) name all 5 registered governed surfaces 0
pnpm check:pm-governed-merges 0
pnpm check:pm-half-states 0
pnpm check:nul-bytes — scanned 8734 text file(s) … no raw ASCII control bytes 0
node scripts/pm/check-governed-queue-guard.mjs --self-test 0
node scripts/check-closing-keyword-parity.mjs and --self-test 0 / 0
node scripts/check-comment-mask-corpus.mjs 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions (first run exited 3 = declared PREREQUISITE NOT MET, formula/lint unbuilt; built both under os-verify-lock.sh, re-run) 0
pnpm check:agent-test-spelling · check:doc-authoring · check:driver-memory-census · check:refd-timer-probe · check:watch-hint-literal 0 each

Mechanism assumptions from the dispatch, measured: the ratchet and id-lint are the gates that bite (both green at headroom 0); check:skill-frame-sync / -freshness are untouched by these lines (both green, frame structure unchanged); check-governed-prose.mjs pins only backtick code spans in the governed-surface enumeration region, not the 人工地板 block (green, and its region does not include these lines).

Acceptance notes

维护者速读(草稿)

改了什么。 两处规则各改一行组,行数不变(SKILL.md 812/812,core-rules.md 151/151),每行 ≤120 字节:① 人工地板新增一项「新增必需门禁/hook/棘轮」——席位要立决策卡、写明它守的契约与预期能抓到什么,不再凭席位自授零基线落地;「门禁内部参数与盲区修复(加强)」这一免升级类改为只覆盖既有门禁,并写明「非新增」。② 一类自裁的「门禁强度」地板加一条例外:门禁普查标为 retire 的门禁(流程类、零真实捕获、不守任何已发布契约),退役不算动地板,由总监席或技能席引普查行裁定。core-rules.md 的对应行同改。

为什么改。 维护者裁决 「F」(2026-09-16,对「我们的项目是不是门禁用的太重了。有这个必要吗?」的回答)。现行规则把「削弱门禁」放在人工地板上,而新增门禁是免费的:182 个 check-*.mjs、164 个 check:* 脚本,上周 597 次合并里 42% 是门禁形状。这个不对称让舰队只会加门禁、拆不了门禁;本 PR 把方向翻过来:加要过维护者,拆(普查判定为 retire 的)由席位裁。

风险与代价(含回滚)。 纯规则文本,不碰任何脚本或门禁本身;付账方式是压缩既有措辞(削弱四例缩写、去掉重复前缀、core-rules 两行重排),没有删任何一条规则。代价是这几行的可读性略降。回滚 = revert 本 PR 一次即可(两个文件、8 行)。依赖:例外条款引用的门禁普查文件 docs/audits/gate-census-*.md 由 #18472 落地,落地前例外无行可引,但规则文本本身不依赖该文件存在。

席位意见。 (留空,席位定稿成评论。)

你要做的。 这是受管规则层(.claude/**):PR 保持 draft;总监席 ACCEPT 后做四件套,合并由你点击。看一眼上面「every line, before / after」八行是否合你的意;尤其 core-rules.md :95 为付账被重排(门禁削弱上移一行)——若你希望 core-rules 那一侧只动 :96,请说,改法是把 :96 压成更短的摘要。


Generated by Claude Code

…/ratchet joins the human floor, census-marked retirement becomes class-1

Maintainer ruling 「F」: two re-keys, line-neutral (SKILL.md 812/812,
core-rules.md 151/151), every touched line ≤120 bytes.

1. 人工地板 gains 新增必需门禁/hook/棘轮 (the card names the guarded
   contract and the expected catch); the 具名不升级 wording is scoped to an
   EXISTING gate's internals (既有 … 非新增) so a new gate cannot ride it.
2. The class-1 self-adjudication floor 门禁强度 gains the carve-out: retiring
   a gate the gate census marks retire is not floor movement; the director or
   the skills seat rules it citing the census row.
3. core-rules.md twins re-keyed identically.

Claude-Session: https://claude.ai/code/session_01Wj1HUjzyeiBQ8atRf1ZhaL
Co-authored-by: Claude <noreply@anthropic.com>
…k/棘轮 so both files grep identically

Line 96 stays ≤120 bytes (exactly 120) and core-rules.md stays 151 lines.

Claude-Session: https://claude.ai/code/session_01Wj1HUjzyeiBQ8atRf1ZhaL
Co-authored-by: Claude <noreply@anthropic.com>

hotlong commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

总监席,session_01Wj1HUjzyeiBQ8atRf1ZhaL。席内复核 ACCEPT 记录在卡 #18473。受管规则层 ⇒ 等一位授权批准账号 APPROVED 后由席位落地;在此之前保持 draft。

改了什么 SKILL.md 5 行、core-rules.md 3 行,行数不变。① 加一道必需门禁 / hook / 棘轮从此是人工地板:席位要开决策卡、写明它守哪条契约、预期抓到什么,⛔ 不能再零基线自行落地;「门禁盲区修复免升级」收窄为既有门禁的内部修复,新门禁不能借这条。② 拆一道门禁变成一类自裁:门禁普查表标为 retire 的(流程类、零命中、不守任何已发布契约),总监或 skills 席引普查行即可裁,不再要你的字。

为什么改 你的裁决 F。协议原来只把「削弱门禁」放在人工地板上,「加门禁」免费 —— 一支 AI 舰队在这条规则下把每个 finding 都长成一道门:182 个 check 脚本,上周 42% 的合并是机器在管机器。

风险与代价(含回滚) 零产品行为变化。代价:席位今后加门要先问你一次(这正是目的)。回滚 = revert 一个 squash 提交。

席位意见 建议批准。它引用的「门禁普查表」在 #18472 的 PR 里,先后顺序不影响本 PR。

你要做的 一个动作:Approve。落地顺序:#18476 先,本 PR 随后合 main 再入队。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 16, 2026 15:35
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 16, 2026

hotlong commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Landing provenance (ruling C) — director seat, session_01Wj1HUjzyeiBQ8atRf1ZhaL · 2026-09-16T15:37Z

Governed rules layer (.claude/**). Readings at landing, head 45aeee5d: APPROVED by os-zhuang (review 5224850379, 2026-09-16T15:35:41Z) on this exact head; check-clause2-carriers --pair 18481 exit 0 (Clause-②: no); CI 19 success · 16 skipped, all complete; in-seat ACCEPT on #18473 (stamped 2026-09-16T15:18Z). The approver flipped ready and the PR entered the merge queue at 2026-09-16T15:35:47Z behind #18476 / #18478 (same file SKILL.md, disjoint lines; the queue builds it on top of them); the seat stripped needs-user-decision and confirmed auto-merge (PUT …/ccr/auto_merge → enabled: true, squash). Nothing else remains for a person. Followed to MERGED by the director's check-in; the ADR-0136 enforcement card #18489 is serialised behind this PR on the same file.


Generated by Claude Code

Merged via the queue into main with commit 3dfcee2 Sep 16, 2026
39 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-18473-gate-asymmetry-flip branch September 16, 2026 15:58
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 17, 2026
… list, report-only (ruling F) (objectstack-ai#18497)

Fixes objectstack-ai#18472

`skip-changeset`: the diff is one file under `docs/audits/`, which no
released package's `files[]` ships.

## What this is

The gate census the maintainer's ruling **F** asked for, verbatim 「F」 on
「现在有各种门禁的开发在浪费我的各种时间,我们的项目是不是门禁用的太重了。有这个必要吗?」 — **one table,
report-only**. No gate is removed, disabled or edited by this PR; the
retirement batch is its own card(s) after the maintainer's one-word 「认可」
on the table.

`docs/audits/gate-census-2026-09.md`:

- **Inventories**: ① 182 check files (`scripts/**/check-*.mjs` 178 +
`packages/*/scripts/check-*.mjs` 4) · ② 26 root `check:*` scripts that
are not a thin alias (of 164; 138 collapse onto their file row) · ③ 10
hooks (5 PreToolUse guards + 5 self-tests; the base tree had 12 — objectstack-ai#18478
removed the enqueue guard on `main` after the base) · ④ 7 required
contexts (read from `GET
/repos/objectstack-ai/objectstack/rules/branches/main`) → **union = 225
rows**, the table has 225 rows.
- **Verdicts**: **retire 59** (58 rule-derived + `retire (ruled)` for
`check-reference-carrier-shape.mjs`, objectstack#18095 E) ·
**report-only 16** · **keep 150** (133 plain, 2 ruled — the governed
queue guard and its context —, 5 too new to judge, 5 session hooks + 5
hook self-tests).
- **Classes**: process 115 · contract 60 · correctness 50.
- **Column 5 (real catches)**: sampled over the last 30 days of failed
PR CI (2026-08-17..09-16): 1,230 failed runs in the eight PR-triggered
gate workflows, 1,287 failed jobs read step-by-step, 1,287 annotation
reads, 9 sampled job logs, attribution by step name, split by annotation
tag where a step hosts several gates, counted as distinct PRs, PRs that
edited the gate's own file excluded. Every zero is a measured zero;
every `unmeasured` names why; no count is guessed.
- **Three sections**: retirement list (per row: files to delete, root
scripts to drop, workflow `file:line`, count of other references to fix;
a nine-row CI-integrity sub-group presented as one decision),
report-only list, method + limits (incl. what is outside the four
inventories: 70 per-package `check:*`, 31 workflow-direct non-`check-*`
steps, two inline workflow gates — the inline `Require a changeset` step
alone reddened 107 PRs).
- Zod validation inside `packages/spec` / the runtime is the product,
out of scope, said in one line.

Standing verdicts recorded as given: `check-governed-queue-guard.mjs` =
`keep (ruled)`; `check-reference-carrier-shape.mjs` = `retire (ruled)`.

## Acceptance notes

- Dispatch-derived gates (`node scripts/pm/dispatch-gates.mjs --commands
docs/audits/gate-census-2026-09.md`, 17 families) all exit 0 on the
merged tree except `pnpm check:cross-package-test-inputs` (exit 1, at
the base and again on the merged tree): its finding names
`packages/cli/test/init-created-files-summary.e2e.test.ts` walking
`packages/spec/dist/` with no declared glob — not a path this diff
touches (one docs file). Reported to the seat in the dev report for
filing; not fixed here (scope rule).
- `pnpm --filter @objectstack/lint run check:doc-formula-expressions`
first answered exit 3 (PREREQUISITE NOT MET: `@objectstack/formula` and
`@objectstack/lint` not built) — built both through the verify lock and
re-ran: exit 0.
- noted, not filed: the four inventories the card defines leave 31
workflow-direct gate steps and two inline workflow gates without a row;
the census names them in Method so the union stays honest. 承接者: the
director's retirement batch (it decides whether a ⑤ is worth a follow-up
census).
- noted, not filed: two of the 25 `Docs anchors resolve to real
headings` step failures could not be attributed between
`check-doc-anchors.mjs` and `check-system-context-census.mjs` (no tag in
the annotation); recorded as `unmeasured` on the doc-anchors row. 承接者:
none.
- Drift: `origin/main` moved seven commits before this PR opened; the
branch merged `bf61f0a1` and the census was re-derived on the merged
tree (Drift paragraph in the file). objectstack-ai#18481 (the protocol flip: a
census-marked retirement becomes class-1) landed in that range and is
the other half of ruling F.
- Local verification scope: no package is touched, so no build/test
closure is owed; `packages/spec`, `@objectstack/formula` and
`@objectstack/lint` were built only as prerequisites of the derived
gates. Repo-wide scans (`pnpm lint`) are CI's.

## 维护者速读(草稿)

**改了什么**:新增一份审计文档
`docs/audits/gate-census-2026-09.md`,把仓库里全部门禁放到一张表上:182 个
`check-*.mjs`、26 个非别名的根 `check:*` 脚本、10 个 `.claude/hooks` 文件、7 个 main
分支必过的 CI 上下文,合计 225 行。每行六列:名字、类别(contract / correctness /
process)、守的是什么契约、是否必过与波及面、落地以来真实抓到过几次(近 30 天 1,230 次失败的 PR
运行逐步归因,去掉门禁自己的 PR)、结论。⛔ 本 PR 不删、不停、不改任何门禁。

**为什么改**:您在直连通道的裁决 **F**(「门禁是不是用的太重了」→ 先普查、再按表退役)。这张表就是普查结果:**59
行建议退役**(其中 1 行 `check-reference-carrier-shape` 是 objectstack-ai#18095 已裁 E),**16
行只是报告不拦合并**,**150
行保留**(含您已裁定保留的治理队列守卫)。波及面最大的三处:`check-changeset-no-major`(Clause-②
等级规则,30 天红了 **74 个 PR**)、`check-partof-closing-keyword`(**31 个
PR**,但它只在非必过的工作流里跑)、`check-system-context-census`(必过、全仓,**21 个
PR**)。表外还有一个最大的红源:`pr-automation.yml` 里的内联「Require a changeset」步骤,30 天红了
107 个 PR。

**风险与代价(含回滚)**:本 PR 只加一份文档,零运行时改动,回滚即删除该文件。真正的代价在下一步:退役清单里有 9 行是「CI
自身接线」的守卫(必过上下文名字钉、聚合器 needs 花名册、Node/pnpm 获取、self-test 接线等),按规则它们 30
天零抓获所以列为退役,但它们的价值是威慑而非抓获——表里把这 9 行单独成组,供您一次决定。抽样只有 30 天:一季度才响一次的门禁会读成零。

**席位意见**:(留空,席位定稿)

**你要做的**:读表,认可则回一个字「认可」;若对某行有异议(尤其 CI 接线 9 行组、以及 5 个会话 hook
的保留),点名该行即可,director 会把退役批次按您的意见立卡。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Wj1HUjzyeiBQ8atRf1ZhaL)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…d a counterfactual — the discipline guarded a ZERO only (objectstack-ai#18809)

Fixes objectstack-ai#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 `objectstack-ai#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 objectstack-ai#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
objectstack-ai#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 (objectstack-ai#5). ② alone catches 6 of 8 and
misses objectstack-ai#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 objectstack-ai#18511 re-keyed SKILL.md :522 / :640 **and** core-rules :112; PR
objectstack-ai#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 objectstack-ai#18666 / PR
objectstack-ai#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

- **objectstack-ai#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 objectstack-ai#18755 names — a live
instrument, every control satisfied, pointed at a question the
instrument's DOMAIN cannot answer. That card is not addressed here.
- **objectstack-ai#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 (objectstack-ai#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 objectstack-ai#18666 / objectstack-ai#18679
后面串行,所以这一趟只落了核心条款这一半;另一半由席位排队补上。没有任何门禁会告诉你这件事 —— 这本身也写进了待立卡清单。

**席位意见**:(留空,席位定稿)

**你要做的**:按四件套流程批准本 PR(规则层,等你的字)。合并后席位负责把 SKILL.md 那一半排进队列。

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants