Skip to content

Commit 745ac30

Browse files
docs(skills): retirement kit routes a RETIRED_KEYS_BY_MAJOR entry through entries/retired-keys/ and gen:migration-registry, and an enum-value narrowing through enumWithRetiredValues() (#20828)
Fixes #20803 Clause-②: no Dispatched by the `domain:skills` seat 1 (seat post #7623) under the PM claim 5907960440, session `session_01KTZmMfzVzjNvyaLyQ8mHvg`, branch `claude/issue-20803-retirement-kit-registry-entries`. This is a draft. The file is on a governed surface (`.claude/**`, Tier S in the `GOVERNED_SURFACES` register), so the seat lands it after an at-tier contract review. ## 维护者速读(草稿) - **改了什么**:退役技能包 `.claude/skills/spec-property-retirement/SKILL.md` 的两段文字。§3 把「退役键登记」改成在 `entries/retired-keys/` 加一个文件、再跑 `gen:migration-registry`;§2 的枚举值退役改指现成的 `enumWithRetiredValues()`,不再教手写 `error` map。文件行数不变(337/337)。 - **为什么改**:登记表 `registry.ts` 早已改为由 `entries/` 逐文件生成,旧文字却让照做的 agent 往生成区手打,`check:migration-registry` 必红、白费一轮;枚举值退役已有通用 helper,旧文字仍在教旧的手写形状。 - **风险与代价(含回滚)**:纯文字改动,零运行时影响,不发布任何包。回滚即 revert 本 PR。它与 #20809(门 (b) 打印的补救文案)须说同一条路线:本 PR 先落,#20809 引用本文。 - **席位意见**: - **你要做的**:无。本 PR 属 Tier S,由席位达档复核 PASS 后入队落地。 ## What changed One file, `.claude/skills/spec-property-retirement/SKILL.md`, two texts, net 0 lines (337 before and after; ceiling 337). The landing site is the one the claim named. The measurement found no other producer of the wrong text inside `.claude/**`. **Text 1: §3, the `RETIRED_KEYS_BY_MAJOR` checklist item.** It sent the entry to be typed into `packages/spec/src/migrations/registry.ts`, and it said the gate's failure output prints "the line to paste". That table is now generated: it is concatenated from one file per entry, and the generator reverts a hand edit between the markers, which `check:migration-registry` reports first. The item now says what an author actually does: - add one file under `packages/spec/src/migrations/entries/retired-keys/`, named as `entries/README.md` says. The kit does not restate the naming convention. - run `pnpm --filter @objectstack/spec gen:migration-registry`, which generates the entry into `registry.ts`. Never type the entry there. Kept unchanged: - the literal `'DEFKEY:NAME'` value, spelled as `authorable-surface/CATEGORY.json` spells it (minus `[RETIRED]`), filed under the current major; - gate (b) reads it by exact set membership, with no inference; - the (b2) warning: do not add the entry before the tombstone lands; - the "why a second table" note and the aging-clock note. Two lines of the old item are byte-identical in the new one: the `authorable-surface/…` line and the (b2) tail line. Deleted: "the gate's failure output prints the line to paste". That sentence named the paste route this card retires, and what gate (b) prints is #20809's to change. The new text is true both before and after #20809 lands, because it names the file route and never quotes the gate's current wording as the thing to follow. **Text 3: §2, the enum-value paragraph.** It prescribed a hand-rolled enum `error` map dispatched on `issue.input`, citing `HookBodyCapability` (`data/hook-body.zod.ts`) and the `object.managedBy: 'system'` precedent. It now points at `enumWithRetiredValues()` in `shared/retired-key.ts`: declare the retired members and their prescriptions there, with no hand-written `error` map. Kept: an enum-value narrowing is invisible to the four ratchets, and none of the three key routes applies to it. Deleted: the two hand-rolled citations. Offered as the shape to copy, they now point at the wrong route. They also have another home: the helper's own docblock (`retired-key.ts`, "The gap this closes") names both as the hand-rolled precursors of the helper. ## Measured before writing (`origin/main` at `79114850`) - **The registry is generated.** `packages/spec/src/migrations/registry.ts:19-26` says the three tables are GENERATED from `./entries/`, one file per entry, "never by typing between the markers. See `./entries/README.md`." There are 237 files under `entries/retired-keys/`. - **The README owns the route.** `entries/README.md` "Adding an entry" gives the file-name rule and the `gen:migration-registry` command. Its "Four rules" say that the next `gen:` run reverts a hand edit inside the markers, and that `check:migration-registry` (`tsx scripts/build-migration-registry.ts --self-test --check`) reports it before that. - **The helper has no production caller.** `HookBodyCapability` (`data/hook-body.zod.ts:48-60`) and `object.managedBy` (`data/object.zod.ts:1705-1710`) are still hand-rolled `z.enum(values, { error })` maps. `enumWithRetiredValues()` has 0 non-test call sites. `git grep` outside `*.test.ts` finds only its definition and docblock in `retired-key.ts`, and one test file uses it (`shared/retired-key.test.ts`). - **The helper covers every retirement the hand-rolled map covers.** Both hand-rolled sites answer one exact retired string with a prescription and leave every other value to zod, which is exactly the helper's `retired` map. The helper's docblock names one case it does not cover: a retired member that WAS the default. A hand-rolled map cannot cover that case either, because the default must be a live member. The docblock routes that case to `acceptRetiredDefaultResidue`. So the kit says "don't hand-roll" with no exception, and stays true. - **No other sentence sends an entry into `registry.ts`.** The census covered `.claude/**`, `AGENTS.md` and `CLAUDE.md`, grepping for `migrations/registry`, `registry.ts`, `RETIRED_KEYS_BY_MAJOR`, `RETIRED_DEFS_BY_MAJOR`, `copy these lines` and `粘贴`, plus paste/append near registry. It found two `registry.ts` hits, both in the kit: - the text-1 line, fixed here; - the step-18 D3-chain item. That item is true and stays untouched: a `MIGRATIONS_BY_MAJOR` step's `rationale` and `conversionIds` are hand-written outside the generated markers (`registry.ts:37-42`; `STEP18_RATIONALE` at `:5024`). The kit's D3 `semantic` item already routes through `entries/`, and nothing outside the kit matched. ## One route, said once in each place (with #20809) At this head, #20809 has no branch or PR, so this lands first. Under the triage split the second to land quotes the first, so gate (b)'s printed remedy should name the same directory and command: - directory: `packages/spec/src/migrations/entries/retired-keys/`, with the file name per `entries/README.md`; - command: `pnpm --filter @objectstack/spec gen:migration-registry`. #20809 remains open. This PR touches nothing under `packages/spec/**`. ## Line budget - **Lines:** 337 / 337 (ceiling 337, headroom 0). Text 1 went from 6 lines to 7, and text 3 from 4 lines to 3. - **Line width:** the widest new line is 116 bytes, and every new non-table line is 120 bytes or less. - **Table rows:** none touched. The widest is 324 bytes against the 326 pin. - **No re-wrap of untouched lines.** Every deleted clause is false or has another home (see above). ## Changeset None: the diff publishes nothing. `.claude/**` ships in no package's `files[]`: - 69 non-private `package.json` manifests, 0 of whose `files[]` names `.claude`; - the root package is `private`; - no `.claude/` directory sits inside any package directory (`git ls-files` shows 50 tracked files under the root `.claude/` only); - positive control: `@objectstack/spec`'s `files[]` reads `dist`, `json-schema`, `liveness`, `prompts`, `llms.txt`, `README.md`, `src/**/*.zod.ts`, `CHANGELOG.md`, `api-surface`, `spec-changes.json`. The seat applies `skip-changeset`; this PR writes no label. ## Gates (at head `21db5689`) `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths) derived 18 commands, the same set as the dispatch. I ran all 18 plus three extras, `check:pm-settings-deny-roster`, `check:pm-skill-id-lint` and `check:pm-governed-prose`. Every one exited 0. `dispatch-gates --ran` with the recorded exit codes reported "18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero)". - `check:pm-skill-ratchet` exit 0: "spec-property-retirement/SKILL.md is 337 lines (ceiling 337; headroom 0)"; "widest table row is 324 bytes (pin 326; headroom 2)". - `check:skill-frame-sync` exit 0; `check:doc-authoring` exit 0; `check:nul-bytes` exit 0 ("no raw ASCII control bytes"); `check:pm-skill-id-lint` exit 0 ("34 file(s) clean"). - `check:pm-governed-merges` exit 0 · `check:pm-governed-prose` exit 0 · `check:pm-settings-deny-roster` exit 0. - `pnpm --filter @objectstack/lint run check:doc-formula-expressions` exit 0, after a `turbo run build --filter='@objectstack/lint...'` (4 tasks, exit 0) under `os-verify-lock.sh`. - `check-ci-filter-parity` · `check-closing-keyword-parity` (+ `--self-test`) · `check-comment-mask-corpus` · `check-harness-current --self-test` · `check:agent-test-spelling` · `check:cross-package-test-inputs` · `check:driver-memory-census` · `check:gitlink-declared` · `check:refd-timer-probe` · `check:required-contexts` · `check:watch-hint-literal`: each exit 0. ## Acceptance notes - **A sibling of gate (b), same family as #20809.** `packages/spec/scripts/build-schemas.ts:2636-2640` is the def-removal gate. It prints the same "copy these lines in … under `18: [ … ]`" remedy for `RETIRED_DEFS_BY_MAJOR`, whose table is generated from `entries/retired-defs/`. #20809 lists only gate (b) (`:1127-1128`), so its author may want to take this sibling in the same edit. Not filed separately. - **Zero production callers for the helper.** `enumWithRetiredValues()` still has no production call site, and its docblock `@example` shows `HookBodyCapability` built with it, which `main` does not do. Both hand-rolled precursors work as they are; this is an observation, not a defect. Carrier: none. - **Table-row pin headroom.** The kit's table-row pin has headroom 2 (widest 324 against pin 326), and the ratchet prints a hint to lower it. That headroom predates this PR, and this PR touches no table row, so the pin is left as it is. Carrier: none. --- _Generated by [Claude Code](https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent addbbf0 commit 745ac30

1 file changed

Lines changed: 8 additions & 8 deletions

File tree

  • .claude/skills/spec-property-retirement

‎.claude/skills/spec-property-retirement/SKILL.md‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,9 @@ ratchet(#2978)会先开火,要求你**有意删除**对应的 manifest key;删
138138
为 #4650
139139
路径 3(`def no longer emitted by this build`)。这串输出本身就是路线的证据,留在 PR 里。
140140

141-
枚举值收窄既然对四张 ratchet 不可见,它的处方就只能挂在**枚举自己的 `error` map**
142-
上、按 `issue.input` 分派(`packages/spec/src/data/hook-body.zod.ts` 的
143-
`HookBodyCapability`,沿用 `object.managedBy: 'system'` 的先例)—— 三条路线里没有一条
144-
适用于「def 存活、只少一个值」。
141+
枚举值收窄既然对四张 ratchet 不可见,它的处方就只能挂在**枚举自己**上:用
142+
`enumWithRetiredValues()`(`shared/retired-key.ts`)声明退役值及其处方,⛔ 不再手写 `error` map ——
143+
三条路线里没有一条适用于「def 存活、只少一个值」。
145144

146145
### guidance 字符串怎么写
147146

@@ -180,11 +179,12 @@ conversion 是消费者跟的;D3 条目是升级方的 agent 读的。三个都
180179
个 `fixture`,其 `expectedNotices` 等于**键**数,不是条目数。walker
181180
(`mapCollection`、`mapFlowNodes`、`renameKey`)住在 `conversions/walk.ts`,
182181
copy-on-write —— 没命中就原样返回输入引用。
183-
- [ ] **一条 `RETIRED_KEYS_BY_MAJOR` 条目**,在
184-
`packages/spec/src/migrations/registry.ts` —— 字面 `'<defKey>:<name>'`,拼法照
182+
- [ ] **一条 `RETIRED_KEYS_BY_MAJOR` 条目**,在 `packages/spec/src/migrations/entries/retired-keys/`
183+
加一个文件(命名见 `entries/README.md`),再跑 `pnpm --filter @objectstack/spec gen:migration-registry`
184+
把它生成进 `registry.ts`,⛔ 永不在那里手打。条目值是字面 `'<defKey>:<name>'`,拼法照
185185
`authorable-surface/<category>.json`(去掉 `[RETIRED]` 标),挂在本 major 下。
186-
这就是门 (b) 按精确集合成员读的字符串;不做推断,也不从邻键辐射。门禁的失败输
187-
出会打印该粘贴的那一行。⚠ **不要**在墓碑落地前先加条目:点名一个还活着的键的
186+
这就是门 (b) 按精确集合成员读的字符串;不做推断,也不从邻键辐射。
187+
⚠ **不要**在墓碑落地前先加条目:点名一个还活着的键的
188188
条目,会以「无人消费的注册」打红门 (b2)。
189189
*为什么要第二张表:* #4659 之前门 (b) 拿键的**叶名**去比对每条已注册的
190190
`surface`(`endsWith('.' + name)`,全 major,def 忽略),于是 `dashboard.aria`

0 commit comments

Comments
 (0)