Skip to content

docs(governed): the retirement route is retiredKey() on any shape, with the retiredAfter stamp and the step-18 registry shape - #20797

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20465-retirement-kit-routes
Sep 30, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20465-retirement-kit-routes

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #20465
Fixes #20575

Clause-②: no

Family PR, one commit per card. Tier H as a whole (AGENTS.md, docs/adr/**), so it stays a draft for the maintainer's review. Nothing here publishes: skip-changeset.

What changed

#20465, commit 249b7836 (the route, the retiredAfter stamp, and the ADR-0087 window sentence from the spec seat 4 fold 5873572274):

  • .claude/skills/spec-property-retirement/SKILL.md §2 route table. Row 1 is now "any shape, strict or not" → retiredKey() tombstone. On a strict shape a bare deletion is loud, but it reports only an unrecognized key and loses both the prescription and the tsc channel. Row 2 keeps the guidance map only for a spelling the shape never declared (a retired key's old alias, a wrong-layer pointer). Such a spelling has no property for a tombstone to replace. The example is now data/mapping.zod.ts. The same section's ledger table, and two lines in §4, rename "strict 删除" to "无墓碑删键", because the route table no longer names a strict deletion.
  • The kit's retiredFromLoadPath checklist item now names the required retiredAfter: 'x.y.z'. tsc refuses a retirement without it. A new retirement carries the current packages/spec/package.json label, and retired-after.census.test.ts pins each value. The artifact door opens its window per entry from this stamp.
  • AGENTS.md Post-Task Checklist step 3 says the same as the kit, in one rule: retiredKey() whether or not the schema is .strict(), and a *_RETIRED_KEY_GUIDANCE entry only for a spelling the shape never declared. The closing clause now says what actually differs between the routes: a tombstone keeps its liveness-ledger row, and a key deleted without one loses it.
  • docs/adr/0087-metadata-protocol-upgrade-contract.md, in the 2026-09-13 addendum's window bullet: the sentence "floor >= runtime replays nothing" is struck through and amended in the dated style that ADR-0005 and ADR-0053 used on 2026-09-30. The amendment note gives the provenance (the Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 ruling A, landed as e956924e), and the status line gains an Amended entry.

#20575, commit 2e3f8aa0 (the step-18 D3-chain item, worded for the shape on main after #20535 and #20574):

  • For step 18, the conversion goes only into MAJOR_18_CONVERSIONS in conversions/registry.ts. It is inserted at its identifier's sorted position, as the list's header says. CONVERSIONS_BY_MAJOR[18] and the step's conversionIds both derive from that list. The rationale gains one STEP18_RATIONALE fragment at the sorted position of its D3 semantic id. The merge pin that each header names refuses an append at the tail.
  • Earlier steps keep the old wording, and the misspelled-id warning now covers them only. Step 18 cannot hit it, because its ids are derived.

Measured first (premise holds), at 7a09eee1

  • The route. The retired-key.ts header (:22–:28) gives the reason tombstones stay on closed shapes. The acceptsNothing doc in strict-object.ts calls a tombstone "strictly stronger than a guidance entry". The precedents are real: action.aria (dcd3bceaa, a strictObject, whose comment at action.zod.ts:1678 says "retiredKey() rather than a bare deletion although this is a strictObject"), the list view's tabs (6e3e5462c, in the strict ListViewShapeSchema), and the cube members' inner name (analytics.zod.ts:243, :298).
  • Where the guidance map still carries weight. data/mapping.zod.ts lists the retired keys' old alias spellings (query, onError) in its guidance map so that "an author who learned the alias should land on the prescription". Those spellings were never declared keys, so a tombstone has nothing to replace. app.zod.ts (showall, location) has the same shape.
  • retiredAfter. conversions/types.ts types it as REQUIRED on the retired arm of the union. The census test pins an entry that no published tarball carries retired to the package label (17.5.0 on this tree).
  • ADR-0087's sentence is false on main. idsTheFloorPostdates in artifact-forward-conversion.ts replays a retired entry when the floor is at or below its retiredAfter, even when the floor is at or above the runtime label (verdict 'converted-retired-after'). On this tree three entries carry retiredAfter: '17.5.0', which equals the label (time-default-utc-suffix-dropped, cube-refresh-key-removed, connector-triggers-removed). An artifact whose floor is 17.5.0 therefore replays three entries, where the ADR said it replays none. artifact-forward-conversion.test.ts:564 pins the same case one release back.
  • Step 18. step18.conversionIds is CONVERSIONS_BY_MAJOR[18]!.map((c) => c.id), and CONVERSIONS_BY_MAJOR[18] is inApplicationOrder(MAJOR_18_CONVERSIONS). rationale is joinRationale(STEP18_RATIONALE). The pins are packages/spec/scripts/conversions-major18-merge.test.ts and step18-rationale-merge.test.ts. step17 still carries a hand-kept conversionIds list.

For the reviewer: one point where the text departs from the triage wording

The #20465 triage direction says to keep the guidance map "only for the case that needs it: a shape where a tombstone cannot sit, such as a z.preprocess stage ahead of the closed shape (retired-key.ts:177)". The route follows that direction, but the example did not hold up when measured:

  • retired-key.ts:177 describes acceptRetiredDefaultResidue. That preprocess stage strips an emitted default ahead of a closed shape that still declares the key as a retiredKey() tombstone. The tombstone sits there.
  • The list view's tabs tombstone (6e3e5462c) sits in a strict member of ViewMetadataSchema, which is itself a z.preprocess ahead of its union.
  • The guidance map is consulted only on the unrecognized_keys path of a strictObject, so a preprocess stage cannot carry one either.

So both texts name the case that was measured: a spelling the shape never declared. If the maintainer means a different case, the Tier H review is the place to say so, and the kit follows.

Scope notes

  • The claim's surface for the kit named :86–:87, :207 and :215–:220. Consequential edits in the same file: the §2 ledger table row and the ORPHAN bullet (:102, :107), and §4 :242 and :257 (the "strict 删除" term). The file is otherwise unchanged.
  • The new lines were paid for by deleting narrative that is kept elsewhere:
    • the orphan leg's history (the "report aria/performance" story), which packages/spec/scripts/liveness/orphans.mts carries verbatim in its header;
    • the "上一版样例栽在这" sentence in the retiredFromLoadPath item. The rule it taught is the item's own preceding sentence and the conversions/types.ts docblock.

Line budget (node scripts/pm/check-skill-line-ratchet.mjs, exit 0)

file before after
.claude/skills/spec-property-retirement/SKILL.md lines 337 / 337 337 / 337
same file, widest table row 326 / 326 bytes 324 / 326 bytes
AGENTS.md lines 1106 / 1116 1107 / 1116

Every added line of the kit and of AGENTS.md is within 120 bytes, except table rows, which are structurally exempt. After commit 1 the kit read 335 / 337. The ratchet prints an informational hint to lower the row pin to 324; the pin lives in scripts/pm/check-skill-line-ratchet.mjs, which is outside this surface, so it is left for the owning seat.

Gates, at head 2e3f8aa0

These were derived by node scripts/pm/dispatch-gates.mjs --commands from this change set (31 families, the same set the dispatch named). All exited 0. Reconciled with --ran: "31 derived, 31 run, 0 NOT-MEASURED, 0 UNRUN", a derived zero, with every exit code recorded.

  • check-adr-0087-registration (and its self-test), check-adr-links (and self-test; 691 links resolve), check-adr-symbol-anchors (and self-test; 2157 anchors across 140 records resolve), check-ci-filter-parity, check-closing-keyword-parity (and self-test), check-comment-mask-corpus, check-harness-current --self-test.
  • pnpm check: adr-anchors, agent-test-spelling, changeset-gate-self-tests, cross-package-test-inputs, doc-authoring, docs-audit-scope, driver-memory-census, future-spec-major, gitlink-declared, nul-bytes, pm-governed-merges, pm-governed-prose (2 instruction surfaces name all 6 governed surfaces), pm-prior-rulings, pm-skill-id-lint (34 files clean), pm-skill-ratchet, refd-timer-probe, required-contexts, skill-frame-sync, watch-hint-literal.
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions. The first run exited 3 (PREREQUISITE NOT MET: @objectstack/formula and @objectstack/lint were unbuilt in the fresh worktree), which measured nothing. After turbo run build for those two packages under the verify lock, the re-run exited 0.
  • Beyond the derived set: packages/spec/src/shared/retired-key-migrate-sentence.test.ts reads this kit as part of its corpus. 1 file and 14 tests passed.

Acceptance notes

  • Also out of this PR's surface: kit §3's RETIRED_KEYS_BY_MAJOR item still says to add the entry "in packages/spec/src/migrations/registry.ts". Since Split migrations/registry.ts's two append tables into per-entry files (registry half of #6957's ruling) #7297, an entry is one file under packages/spec/src/migrations/entries/retired-keys/ followed by gen:migration-registry, and the region in registry.ts is generated. The kit is echoing the build-schemas.ts gate (b) failure text, which still says to paste the key into registry.ts under the current major. That gate text is the producer, and it is outside this surface. It is reported to the PM in the dev report, not filed.

维护者速读(草稿)

改了什么

  • 退役手册 §2 与 AGENTS.md 第 3 步统一为一条路线:任何 schema(strict 与否)都用 retiredKey() 墓碑。guidance map 只留给 shape 从未声明过的拼写(如退役键的旧别名),那里没有属性可供墓碑替换。
  • 手册的 retiredFromLoadPath 条目补上必填的 retiredAfter 版本戳。
  • 第 18 步 D3 链条目改写为 main 上的排序键形状:conversion 只进 MAJOR_18_CONVERSIONS,rationale 只加一个排序片段。
  • ADR-0087 中「floor ≥ runtime 不重放任何条目」一句已按落地的逐条窗口规则做了日期化修订。

为什么改

  • 两份受管文本与代码头注、本周三次落地的先例相反,每次退役都要在报告和复核里重新争论路线。
  • 第 18 步条目让作者去改一个已经由代码派生、不再手写的列表。
  • ADR 那句话在 main 上已经为假:3 个 retiredAfter 等于当前版本标签的条目,在 floor 等于 runtime 时仍会被重放。

风险与代价(含回滚)

  • 纯文档与 agent 规则,不发布任何包,也没有运行时影响。回滚方式是 revert 本 PR 的两个提交。
  • 一处措辞偏离:分诊方向举的例子是「z.preprocess 阶段」,实测不成立(那里墓碑照样能放)。两份文本改为点名实测成立的情形,详见正文「For the reviewer」一节。

席位意见

你要做的

  • 审阅 AGENTS.md 与 ADR-0087 两处改动(Tier H),同意则给 APPROVED review。
  • 如果希望 strict shape 保留「删键 + guidance map」路线,或坚持使用原示例的措辞,请在评审里写明,手册这一半会随之修改。

Generated by Claude Code

…dAfter stamp; ADR-0087 window per entry

The retirement kit's route table and AGENTS.md Post-Task Checklist step 3
now prescribe a retiredKey() tombstone whether or not the schema is
strict: on a closed shape a bare deletion is loud but reports only an
unrecognized key, losing both the prescription and the tsc channel
(retired-key.ts header). The strictObject guidance map is named only for
a spelling the shape never declared, such as a retired key's old alias,
where a tombstone has no property to replace (data/mapping.zod.ts).

The retiredFromLoadPath checklist item gains the required retiredAfter
stamp: tsc refuses its absence, a new retirement carries the current
packages/spec label, and retired-after.census.test.ts pins each value.

ADR-0087's artifact-window bullet said a floor at or above the runtime
replays nothing; since e956924 the door replays every retired entry
whose retiredAfter the floor does not exceed. Amended in the dated style.

Lines paid by deleting narrative with another home: the orphan-leg
history (orphans.mts header) and the withdrawn-example sentence.

Claude-Session: https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg
Co-authored-by: Claude <noreply@anthropic.com>
…ape on main

For protocol 18 the retirement kit no longer tells an author to append
to MIGRATIONS_BY_MAJOR[18].conversionIds and extend a rationale string.
On main the conversion goes only into MAJOR_18_CONVERSIONS in
conversions/registry.ts, inserted at its identifier's sorted position per
that list's header (CONVERSIONS_BY_MAJOR[18] and the step's conversionIds
both derive from it), and the rationale gains one STEP18_RATIONALE
fragment at its D3 semantic id's sorted position. The merge pins each
header names refuse an append at the tail.

Earlier steps keep the old wording, and the misspelled-id warning is
scoped to them: it cannot happen on step 18, whose ids are derived.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2e3f8aa08b57a0b2e7643e1d5bed66c44b62b89f
Local-runs: none

Reviewed for PR #20797 (family PR: #20465 chain head, #20575 member), two commits on base 7a09eee1. Inputs: both cards' bodies and every comment, the PR body, its file list (3 files, +44/−26), the net diff, the head's check-runs, and the #20390 ruling comment 5865890672. Readings of repository files are at origin/main 8acdae9d unless a head line is named; the three touched paths are byte-identical between the merge base 7a09eee1 and 8acdae9d, so the PR diff is the net diff against main.

① Derived judgments

1. The route — kit §2 row 1 「任何 shape」(strict 与否) and AGENTS.md step 3's first clause: right. packages/spec/src/shared/retired-key.ts:22-28 is the code's own posture: on a closed shape a bare deletion "is no longer silent — but it is still not enough, which is why tombstones stay. An unknown-key rejection reports only that the key is unrecognised; it cannot carry the FROM → TO mapping, the ADR the removal rests on, or the migration command." strict-object.ts:88-93 (acceptsNothing) calls the tombstone "deliberate and strictly stronger than a guidance entry". The precedents the card names are real and all on strictObject shapes: ui/action.zod.ts aria (dcd3bceaa, the in-shape comment reads "retiredKey() rather than a bare deletion although this is a strictObject"), ui/view.zod.ts:2854 tabs inside ListViewShapeSchema (a strictObject opened at :2527; 6e3e5462c), data/analytics.zod.ts:243 and :298 (cube metric and dimension name). The row's "strict 上裸删也响,但只报 "unrecognized key",两通道都丢" compresses the header's posture the way the card body itself does ("cannot carry the prescription or the tsc channel"). Right.

2. Row 2 「从未声明的拼写」→ guidance map, and the departure from the triage's example: right, and the measurement holds. retired-key.ts:177 is the "Mechanism and placement" paragraph of the acceptRetiredDefaultResidue docblock; the same docblock at :164-168 says the closed shape behind that preprocess "still declares the key as a retiredKey tombstone". ViewMetadataSchema (ui/view.zod.ts:6673) is itself a z.preprocess ahead of a union whose strict members carry tombstones (tabs above). And strict-object.ts:183-185: guidance "is consulted for unrecognized_keys", which a preprocess stage never raises, so a preprocess cannot carry a guidance map either. The triage's "such as" example therefore names a place where a tombstone does sit and a guidance map cannot. What remains for the guidance map once row 1 holds is a spelling the shape never declared — and that is the invariant shared/alias-integrity.test.ts already enforces on every strictObject table: an entry must be filed under a key the shape rejects, and an entry for a declared key (a tombstone is declared) is dead code. Exemplars at the code: data/mapping.zod.ts:35-39 (its header: "an author who learned the alias should land on the prescription", listing query, onError and the other alias spellings for that reason) and ui/app.zod.ts:1155-1167 (showall, location, each "which … aliased"). 「错层指针」 is the guidance slot's own second purpose (strict-object.ts:57-58, :152-154: "tombstones for retired keys, wrong-layer pointers"). What the #20465 direction ordered — one route, retiredKey() on a closed shape; the guidance map kept only for the case that needs it; that case named — is what both texts now say; the direction's illustrative example is corrected on measurement, and the departure is declared in the PR body and in both dev reports for the Tier H checkpoint the direction itself named. Right. One wording caveat is carried to ③ (item 9).

3. The term rename 「strict 删除」→「无墓碑删键」 (§2 ledger table :102, ORPHAN bullet :107, §4 :242 and :257): right. packages/spec/scripts/liveness/orphans.mts:11-19 keys the two dispositions on whether the key stays in the walked shape (tombstone → row stays; key leaves → row goes), which is exactly the table's first column; §2 no longer names a route called strict deletion, and the renamed row still covers the legacy delete-plus-map population and row 3's whole-def deletion. Consistent.

4. Every deleted sentence has a home — no lost rule.

  • The orphan-leg history (three lines: the leg "is new", the direction never failed before it, the report aria/performance rows outlived their keys by a release): orphans.mts header lines 5-25 carries it ("the row rots in place"; "outlived their schema keys by a full release … noticed only because a human happened to be reading the file"). The kept line retains the rule (fix the walk, not the row) and points at the header — the AGENTS.md lines 18-19 shape for a gate-enforced rule.
  • "上一版样例栽在这:它教「只有 migrate meta 能应用翻转」,而 boot 时照样应用,该 conversion 已撤": incident narrative about field-required-notnull-explicit. The rule it illustrated is the item's own preceding sentence (a default flip replays only at a seam that knows the input predates it; otherwise excludeConversionIds) and conversions/types.ts:255-259 ("Setting this does NOT confine a rewrite to history … the data-at-rest seams will still apply it"); the incident is recorded in packages/spec/CHANGELOG.md (the f2b5e46 withdrawal entry) and by the registry's tombstone for the withdrawn id. Not lost.
  • AGENTS.md's "or an entry in the relevant UNKNOWN_KEY_GUIDANCE / *_RETIRED_KEY_GUIDANCE map … when the schema is .strict()": the route this stated is the one the card retires; the map mechanism survives in the new clause. The dropped name is the founding spelling on ObjectSchema (build-schemas.ts:1515) — a nit, ③ item 8.
  • AGENTS.md's "the two routes imply opposite liveness-ledger dispositions" → "a tombstone keeps its liveness-ledger row while a key deleted without one loses it": the same rule, restated so it stays true now that route 2 deletes no property. Right.
  • The ADR sentence is struck and kept beside its amendment, not deleted.

5. The retiredAfter stamp (kit :204-206): right. conversions/types.ts:285 types it required on RetiredConversionState (:297-299: a retired entry "must also carry retiredAfter"); :262-269: it is the package's version label at the moment the retirement lands; retired-after.census.test.ts header: "tsc makes the field REQUIRED; this file pins each VALUE", an unpublished entry carries "the package's own version label"; artifact-forward-conversion.ts:33-37: "The window is decided PER ENTRY". The kit states the rule and leaves the census's one tolerance (a label ahead of the census) to the test, which is the authority on detail. Line budget measured on the head: kit 337/337 lines (pin 337), widest table row 324 bytes (pin 326), no added non-table line over 120 bytes; AGENTS.md 1107/1116 (pin 1116), no added line over 120 bytes.

6. The step-18 D3-chain item (kit :213-220): right. conversions/registry.ts:12817 MAJOR_18_CONVERSIONS (module-private, { conversion, order } entries); its header :12787-12816: "SORTED BY THE CONVERSION'S IDENTIFIER, NOT BY APPLICATION ORDER … Insert … where its identifier sorts … never at the end", naming scripts/conversions-major18-merge.test.ts; :12953 18: inApplicationOrder(MAJOR_18_CONVERSIONS). migrations/registry.ts:5978 maps step 18's conversionIds off CONVERSIONS_BY_MAJOR[18] ("Derived, not listed … Add a conversion there, never here"); :5971 rationale: joinRationale(STEP18_RATIONALE); the STEP18_RATIONALE header :5008-5023: "SORTED BY id … id is your retirement's D3 semantic entry id; insert the fragment where that id sorts", naming scripts/step18-rationale-merge.test.ts. Both pins assert sortedness ("the entries are kept SORTED by identifier", "the fragments are kept SORTED by key") and prove that an end-append conflicts, so "尾部追加被两处头注点名的 merge 测试拒收" is right. step17 (:100, hand-kept conversionIds at :1091) still sits under MIGRATIONS_BY_MAJOR (:18620), so 「更早的步」 keeps the old wording and the misspelled-id warning, as the #20575 direction asked; step 18 cannot misspell a derived id. The order value rule is delegated to the two headers by 「照其头注」/「按其头注」. Right.

7. AGENTS.md step 3 against AGENTS.md's own header (lines 11-19): right. The rewritten clause is rule text only: one executable rule per sentence, no incident narrative, no ruling date or quotation, no issue-number citation, no model name; its provenance is the code it names (retired-key.ts, data/mapping.zod.ts) and the kit it points to. AGENTS.md (line 6) outranks the skill, and the two now say one thing.

8. The ADR-0087 amendment against the ruling's words: right.

Check-run state read on 2e3f8aa0 (29 check-runs, 2026-09-30 ~07:08Z): 14 success, 7 skipped, 7 in_progress, 1 failure.

  • failure (1): Check Changeset — the changeset gate enforcing because the PR carries neither a changeset nor the skip-changeset label; see ②.
  • in_progress (7, all started 07:06Z): Lint & Repo Gates (required; the job that runs every check:* gate the diff derives — pm-skill-ratchet, pm-governed-prose, pm-skill-id-lint, adr-anchors, doc-authoring and the rest), Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace (the required TypeScript Type Check family), Test Core (1/6), Test Core (2/6) (required; shards 3/6 to 6/6 are success). These are honest in-progress readings, not passes: "every check green" is not yet true of this head.
  • skipped (7): Build Core, Build Docs, Console Pin Gate, Dogfood Regression Gate (${{ matrix.shard }}/3), Dogfood Verify CLI, Packed-tarball smoke (opt-in), Temporal Conformance (live PG + MySQL) — path-filtered out for a docs-only diff.
  • success (14): Governed Surface Queue Guard, The card this PR closes must claim this branch, Part-of PR must not also close its card, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Check Documentation Links, Check PR Size, Dogfood Regression Gate, Test Core (3/6), (4/6), (5/6), (6/6), Auto Label, filter.

② Semver level

The diff is three files — .claude/skills/spec-property-retirement/SKILL.md, AGENTS.md, docs/adr/0087-metadata-protocol-upgrade-contract.md — none under packages/**; no accept set, export, config field or runtime string moves. Clause-②: no is right (no packages/spec accept-set change). skip-changeset is the right declaration by AGENTS.md step 3's own definition ("a diff that publishes nothing from any released package"), and the PR body declares it. The LABEL is not on the PR at this head (labels read live: documentation, size/s; assignees: none) because the dev's label-write.mjs call was refused by the session's permission classifier (dev deviation 1). That absence is why Check Changeset concluded failure: the workflow enforces when neither the label nor a changeset is present, and this diff rightly adds no changeset. The red is a missing carrier, not a defect in the diff; the diff owes no changeset. The seat applies skip-changeset (the labeled run re-reads the label live) and assigns os-warren per the claim.

③ Boundary flags

Dev report (5906019234 on #20465, 5906020110 on #20575; open_questions: []) — each deviation and out-of-scope finding, answered:

  1. Label and assignee write refused by the classifier — escalated to the seat: apply skip-changeset and assign os-warren on docs(governed): the retirement route is retiredKey() on any shape, with the retiredAfter stamp and the step-18 registry shape #20797. This is the Check Changeset red on this head; nothing in the diff changes.
  2. Departure from the triage's example — answered in ① item 2: the measurement holds at retired-key.ts:164-179 and strict-object.ts:183-185; the new wording says what the direction ordered and names the measured case; it stays flagged for the maintainer's Tier H review, which the direction itself made the checkpoint and which the PR's 「维护者速读」 asks for.
  3. Surface: consequential same-file edits at :102, :107, :242, :257 and the AGENTS.md closing clause — accepted: term renames inside the claimed file, required for the file to agree with itself once §2 no longer names a strict deletion; zero net lines; nothing outside the three claimed paths.
  4. ADR-0087 amended in the dated style with a status-line entry — accepted (① item 8).
  5. Model-free commit trailers — not a deviation from the repo's rule: AGENTS.md lines 452-456 require exactly the pair both commits carry (Claude-Session: and Co-authored-by: Claude).
  6. Cards read through the public-page payload tier — informational; the newest Claim: on each card (5905577500, 5905586182) names this branch.
  7. Out-of-scope finding 1: kit §3's RETIRED_KEYS_BY_MAJOR item still says the entry goes "在 packages/spec/src/migrations/registry.ts" and that the gate prints "该粘贴的那一行" — confirmed at the code: migrations/registry.ts header ("The three tables below are GENERATED (Split migrations/registry.ts's two append tables into per-entry files (registry half of #6957's ruling) #7297) … Add an entry by adding a FILE … never by typing between the markers"), entries live under migrations/entries/retired-keys/ (e.g. 18.data__TenancyConfig__organizationField.ts), and the gate (b) text at build-schemas.ts:1127-1128 still says "copy these lines in" naming registry.ts. Same filing gate as [finding] spec-property-retirement SKILL.md tells a step-18 author to append to conversionIds and extend the rationale string: both change shape when PR #20572 lands #20575 (a governed text a landing made false), same file, loud failure (check:migration-registry). Rightly not fixed here (outside the claimed surface; the kit has zero line headroom). Escalate: the seat files it as the next retirement-kit family card — the kit line for domain:skills, the producer text in build-schemas.ts for domain:spec — a filed card, not a report note (PD chore: version packages #10: a metadata-authoring trap).
  8. Out-of-scope finding 2: kit §2's enum-value paragraph (:141-144) prescribes a hand-rolled enum error map while enumWithRetiredValues() exists with zero call sites — confirmed: retired-key.ts:321 (spec: there is no VALUE-level retirement mechanism — retiredKey() retires a key, and a ruled enum-member retirement has nothing to land on #17109, "the generic helper"), used only by retired-key.test.ts; HookBodyCapability still hand-rolled. Dormant drift, same family; fold into the same follow-up card.

Reviewer's own findings, none a FAIL:

  1. Exemplar caveat (① item 2). MAPPING_RETIRED_KEY_GUIDANCE and CONTEXT_SELECTOR_RETIRED_KEY_GUIDANCE are mixed populations: beside the alias spellings each also carries 17.0.0-window keys that were DECLARED and deleted without a tombstone (extractQuery, errorPolicy, batchSize; includeAll, placement) — the route row 1 now forbids. No pure exemplar exists on main; the row's words confine the route to 「从未声明的拼写」, so the citation is for the mechanism (how an entry reaches strictObject()'s guidance: slot) and mapping.zod.ts's header explains the alias case in its own words. Named for the maintainer's Tier H read; if a purer pointer is wanted, name the header lines rather than the file.
  2. Code comments now behind the governed rule (not in the claimed surface). strict-object.ts:56-58 and :152-154 still list "tombstones for retired keys" as the guidance slot's first purpose, and ai/tool.zod.ts:31-35 describes its map as "the pattern of object.zod.ts's UNKNOWN_KEY_GUIDANCE" — descriptions of the legacy delete-plus-map population. The tables themselves stay (their entries are live guidance for keys those shapes no longer declare, exactly what alias-integrity.test.ts requires); the docblock line is a one-line correction for domain:spec, carried on the same follow-up card.
  3. Naming nit in AGENTS.md. "the shape's *_RETIRED_KEY_GUIDANCE map" does not literally match ObjectSchema's founding UNKNOWN_KEY_GUIDANCE (build-schemas.ts:1515). The mechanism named is right and each shape has one guidance: map, so an author lands on it; no rule is lost. For the maintainer's pass.
  4. Landing. Tier H as a whole (AGENTS.md, docs/adr/**): this record is the at-tier review the dispatch commissioned; the maintainer's APPROVED review remains the landing key. Before anyone reads the head as green: the seven in_progress jobs must conclude success, and Check Changeset goes green only once the label is applied (item 1).

Implemented-by: claude/issue-20465-retirement-kit-routes
Reviewed-by: session_01KTZmMfzVzjNvyaLyQ8mHvg

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)— PR #20797 · 退役路线统一为 retiredKey()(#20465 + #20575)

skills 席 1 · session_01KTZmMfzVzjNvyaLyQ8mHvg · 2026-09-30T07:30Z · 所审 head 2e3f8aa0

改了什么:三个文件(+44 / −26)。

为什么改

  • 两份受管文本与代码头注、本周三次落地的先例相反,每次退役都要在报告和复核里重新争论走哪条路线。
  • 第 18 步条目让作者去改一个已经由代码派生、不再手写的列表。
  • ADR 那句话在 main 上已经不成立:3 个 retiredAfter 等于当前版本标签的条目,在 floor 等于 runtime 时仍会被重放。

风险与代价(含回滚):只改文档和 agent 规则,不动代码,不发布任何包,没有运行时影响。回滚:revert 本 PR 的两个提交。

席位意见:ACCEPT,建议批准。

  • 契约复核 PASS(评论 5906291524),由隔离的达档子代理出具,席位核验后采纳。复核在代码层核实了路线、改名、每句删掉的话的去处、retiredAfter 规则、第 18 步形状,以及 ADR 修订与裁决原话一致。
  • 一处有意偏离:分诊方向举的例子是「z.preprocess 阶段」,实测不成立,因为那里照样能放墓碑,而 guidance map 在那里读不到。两份文本改为点名实测成立的情形。
  • 两处措辞请你过目,都不影响规则:
    • 从未声明拼写的情形,main 上没有纯样板。mapping.zod.ts 被引用是为了说明机制,其表里还混有旧路线删掉的键。
    • AGENTS.md 写的是 *_RETIRED_KEY_GUIDANCE,ObjectSchema 的那张表叫 UNKNOWN_KEY_GUIDANCE。
  • 同族还有四处文本没跟上,已另立 [finding] retirement kit §3 and build-schemas.ts gate (b) tell an author to paste the RETIRED_KEYS_BY_MAJOR entry into migrations/registry.ts, which is generated from entries/ since #7297 #20803,不在本 PR 改。其中两处是手册 §3 和 build-schemas.ts 的报错,都还叫人往自动生成的 registry.ts 里手工粘贴。
  • CI:Lint & Repo Gates 仍在跑,落地前须转绿。唯一的红是 Check Changeset:本 PR 不需要 changeset,但 skip-changeset 标签没挂上,因为开发子代理打标签被它的权限分类器拒了,席位不代做。该检查不是必需项,不挡入队。

你要做的

  • 审阅 AGENTS.md 与 ADR-0087 两处改动。同意就在本 PR 上给 APPROVED,之后由本席位核对 CI 全绿,再翻 ready 入队。
  • 可选:给本 PR 挂上 skip-changeset 标签,让 Check Changeset 转绿。
  • 如果希望 strict shape 保留「删键 + guidance map」路线,或坚持用原来的示例,请在评审里写明,手册这一半会随之修改。

Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 30, 2026 07:32
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 30, 2026
@os-zhuang
os-zhuang removed this pull request from the merge queue due to a manual request Sep 30, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 30, 2026
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 4cc5bcd Sep 30, 2026
48 of 50 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20465-retirement-kit-routes branch September 30, 2026 08:20
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