fix(pm): drop the re-seat prescription — check-harness-current and SKILL.md:92 become a seat-post reading - #18476
Conversation
…a seat-post reading A stale harness path on the shared checkout is noted on the seat post and picked up at the seat's next natural shift boundary; it no longer closes the shift or interrupts a batch. Line-neutral (812/812), 119 bytes, :91 unchanged. No twin in references/core-rules.md (grep for check-harness-current, 收班、换新会话, 不推进共享检出: zero hits there). Claude-Session: https://claude.ai/code/session_01Wj1HUjzyeiBQ8atRf1ZhaL Co-authored-by: Claude <noreply@anthropic.com>
…f prescribing a re-seat The STALE verdict now says what SKILL.md:92 says: note it on the seat post and pick it up at the seat's next natural shift boundary; never interrupt a batch for it. Exit codes 0/1/2 and the line-terminal `-- STALE` / `-- UNDECIDED` markers are unchanged; the CURRENT and UNDECIDED strings are unchanged. The header's load-moment claim is narrowed to what was measured (`.claude/settings.json`); the load moment of `.claude/hooks/*` and `.claude/agents/*.md` is stated as unmeasured, and the sentence naming re-seating as the remedy is gone. The self-test's STALE expectation is re-keyed to the new wording byte for byte, one case is added pinning that the summary names neither a re-seat nor advancing the shared checkout, and the case floor rises 14 -> 15 to cover it. Claude-Session: https://claude.ai/code/session_01Wj1HUjzyeiBQ8atRf1ZhaL Co-authored-by: Claude <noreply@anthropic.com>
维护者速读(终稿)总监席, 改了什么 两个文件。 为什么改 你今天的裁决 D:一个过时会话的最坏后果是一次可撤销的 PR 状态改动,不值七个席位一天重开三轮;重开还会让在飞的 dev 子代理全部变孤儿。今天的三波重开都来自同一个钩子被返工三遍。 风险与代价(含回滚) 风险:容器早于 deny 表变更的会话,直到自然交班前手里仍有被 deny 的 MCP 写工具 —— 协议照旧禁止调用、ACCEPT 照旧拒收点名写工具的报告、受管面仍由服务端队列守卫 + 你的批准拦着。代价:零。回滚 = revert 一个 squash 提交。 席位意见 建议批准。diff 与裁决逐字对应,没有多改一行;自测能失败(dev 做了消融:把旧判词注回去,恰好那两条 pin 变红)。 你要做的 一个动作:在本 PR 上 Approve。落地后 #18473(门禁增删不对称翻转)接在它后面合 main。 Generated by Claude Code |
|
Landing provenance (ruling C) — director seat, Governed rules layer ( Generated by Claude Code |
…/ratchet joins the human floor; census-marked retirement becomes class-1 (ruling F) (objectstack-ai#18481) Fixes objectstack-ai#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: objectstack-ai#18468's PR objectstack-ai#18476 (`SKILL.md:92`) is still an open draft; objectstack-ai#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 objectstack-ai#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 - noted, not filed: the card's acceptance grep (`新增` + `人工地板` ≥ 1) cannot distinguish before from after on this base; the PR carries discriminating greps instead. 承接者: PM (this report). - noted, not filed: core-rules.md carries no digest of the class-1 self-adjudication list, so item 2 has a single copy by construction — an observation about the digest's coverage, not a defect. 承接者: skills seat (reads this PR). - noted, not filed: the rule's 门禁普查 reference points at `docs/audits/gate-census-*.md`, which lands with objectstack-ai#18472; until then the carve-out has no row to cite. 承接者: objectstack-ai#18472. ## 维护者速读(草稿) **改了什么。** 两处规则各改一行组,行数不变(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` 由 objectstack-ai#18472 落地,落地前例外无行可引,但规则文本本身不依赖该文件存在。 **席位意见。** (留空,席位定稿成评论。) **你要做的。** 这是受管规则层(`.claude/**`):PR 保持 draft;总监席 ACCEPT 后做四件套,合并由你点击。看一眼上面「every line, before / after」八行是否合你的意;尤其 core-rules.md `:95` 为付账被重排(门禁削弱上移一行)——若你希望 core-rules 那一侧只动 `:96`,请说,改法是把 `:96` 压成更短的摘要。 --- _Generated by [Claude Code](https://claude.ai/code/session_01Wj1HUjzyeiBQ8atRf1ZhaL)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #18468
Maintainer ruling D on the card, verbatim: 「D:整个去掉「重开」处方」
What changed
.claude/skills/pm-dispatch/SKILL.md:92re-keyed in place: 812/812 lines, the edited line is 119 bytes,:91is byte-identical to base. The prescription 「否 ⇒ 收班、换新会话再派,⛔ 不推进共享检出」 is gone; the line now reads 「否 ⇒ 记座位贴,自然收班时换会话,⛔ 不中断批次;读数走scripts/pm/check-harness-current.mjs。」 — the reading stays a reading and the pointer stays.references/core-rules.md.grep -n -e check-harness-current -e 收班 -e 换新会话 -e 不推进共享检出 -e harness .claude/skills/pm-dispatch/references/core-rules.mdhits only :19, :20 and :76 — the shift-handover readings, none of them a twin of :92. Repo-wide, the prescription strings lived only in SKILL.md:92 and this script (its header, verdict and self-test).scripts/pm/check-harness-current.mjs:…: note it on the seat post and pick it up at the seat's next natural shift boundary; ⛔ never interrupt a batch for it. Exit codes 0/1/2 unchanged; the per-path-- STALE/-- UNDECIDEDline-terminal markers and their positions unchanged (the file's own provenance contract); the CURRENT and UNDECIDED strings byte-identical..claude/settings.json(the deny-roster measurement); the load moment of.claude/hooks/*and.claude/agents/*.mdis stated as UNMEASURED and the tool asserts neither; the sentence naming re-seating as the remedy is gone.--self-test: the byte-for-byte STALE summary expectation is re-keyed to the new wording; one case is added pinning that the summary names the seat post and the shift boundary and names neither a re-seat nor advancing the shared checkout;SELF_TEST_CASE_FLOORrises 14 → 15 so the added case cannot silently stop running. The ✗-line regex is unchanged because the ✗ line is unchanged. No assertion deleted or weakened.settings.jsondeny roster,.claude/hooks/*,.claude/agents/*.md, every other SKILL.md line.Readings
Self-test, three exit codes (the PM's mechanism hypothesis, confirmed):
8fe5cb8e(origin/main at branch time): exit 0, 14 casesand its summary line is the STALE one, byte for byteAblation, one-shot from the committed state (taken at
c67c0df1; the two files are byte-identical at the rebased head6f008921): the verdict line was mutated back to the old prescription (on-disk grep: injected clause 1 hit; the new wording left in the self-test expectation only, 1 hit) →--self-testexit 1 with exactly the two STALE pins failing (byte for byteanda report, not a prescription); restore viagit checkout HEAD -- PATH→git hash-objectequals the HEAD blob616f53f6…,git diff HEADempty,git status --porcelainempty; control run on the restored tree exit 0, 15/15. Trap-guarded, absolute path.Gates:
node scripts/pm/dispatch-gates.mjs --commandswith no paths (changeset from merge-base) derives 40 families; all 40 were run with each exit code redirected to a file before any pipe, and--ranwith per-command exit codes reconciles40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN(a DERIVED zero). One first-run reading was not a measurement:pnpm --filter @objectstack/lint run check:doc-formula-expressionsexited 3PREREQUISITE NOT MET(unbuilt@objectstack/formulaand@objectstack/lint); those two were built underscripts/pm/os-verify-lock.sh(VERDICT command-exit 0, held 194s) and the gate rerun: exit 0.pnpm check:skill-frame-freshnesswas also run, exit 0 (not derived; the PM asked for it).The 40-run was taken at
c67c0df1;origin/mainthen moved by one comment-only commit (0fe38a92,scripts/check-bash32-floor.mjs, a derived family), so the branch was rebased onto it (no PR existed; the branch was unshared), the derivation re-run at6f008921(identical 40 families, no stale-tree warning) and these families rerun at6f008921, all exit 0:check:bash32-floor,check:pm-skill-ratchet(SKILL.md 812 lines, ceiling 812, headroom 0),check:pm-skill-id-lint,check:skill-frame-sync,check:nul-bytes, and the self-test.Changeset:
skip-changeset—.claude/**andscripts/pm/**publish nothing; no released package'sfiles[]content moves.Writes: REST only —
git push×2 (initial, then the lease-pinned rebase push),POST /pulls×1,POST /issues/N/labels×1 (skip-changeset),POST /issues/18468/comments×1 (the os-dev-report). MCP GitHub write tools: 0.Acceptance notes
SELF_TEST_CASE_FLOOR) rather than the per-battery roster AGENTS.md's self-test section prescribes; the floor was raised to cover the added case, and the roster shape is not this card's surface. 承接者:无。needs:contract-reviewis the seat's to hang or strip; this PR neither hangs nor strips it.维护者速读(草稿)
改了什么:把「共享检出上的 harness 文件落后于 origin/main」这条读数,从「必须收班、换新会话再派」的处方,改成「记在座位贴、到自然收班时再换会话、不打断当前批次」的报告。落在三处:SKILL.md 第 92 行原地改(812 行不变),
check-harness-current脚本的判决文案与头部说明,以及它的自测期望。退出码不变,调用它的席位无感。为什么改:裁决 D,原文「D:整个去掉「重开」处方」。今天同一个钩子返工三次,每次都让在岗的七个 PM 会话同时重开;而重开所防的缺口(旧 deny 清单少三个 MCP 写工具)整班一次都没被踩到,并且 ACCEPT 核验仍旧拒收任何点名 MCP 写工具的报告。
风险与代价(含回滚):接受的残余是维护者原话:容器早于 deny 清单变更的席位,到下次自然收班前仍持有那几个被拒的工具;若席位违规,可用绑定账号改一次 PR 状态——可回退,受管 PR 仍由队列规则集与你的点击把守。行数代价为零(812/812)。回滚 = revert 这两个 commit,脚本自测随之回到旧文案,无数据迁移、无配置变更。
席位意见:
你要做的:本 PR 触受管面(
.claude/**),保持 draft,等你一句批准后由归属席位落地;随后 PR #18447 按裁决 D 在同一批准窗口跟进(先规则、后钩子),一次收班顶两次。Generated by Claude Code