Skip to content

spec: 14 retiredKey() tombstones again prescribe @objectstack/spec 18, a version that will not contain the change — #4350 recurring with no gate behind its fix #18040

Closed as duplicate

Description

@zhuangjianguo

Filed by the epic PM for #15939 (session_015c5G6TmpMKgnusmTpD7Ntt) as a surrounding finding. ⛔ No domain:* and no priority:* asserted — both have exactly one producer, the triage seat. ⚠️ Triage's first touch is owed before this can be dispatched.

Surfaced by the at-tier contract review of PR #18016 and verified independently on origin/main, 2026-09-13T14:29Z.

This is a recurrence, not a new defect

#4350 filed exactly this in July — 「十处墓碑文案写着「removed in @objectstack/spec 18」,而这些键随 17.0.0 发布 —— 处方给了作者一个不会到来的版本号」 — and it was closed completed by merged PR #4359, which corrected all ten sites from 18 to 17.

⚠️ #4350 also recorded that the wrong number caused real downstream harm: objectui#3101 scheduled its own cleanup against "the PR that takes @objectstack/spec to 18" — an event that was never going to arrive.

There are 14 again. The fix was applied by hand and nothing was left behind to hold it, so the class regressed.

The 14, measured

⚠️ Measure with concatenated string literals joined first — the phrase spans a '…' + '…' break, so a single-line grep returns a false zero. That is why this went unnoticed:

git ls-tree -r --name-only origin/main -- packages/spec/src | grep -E '\.ts$' | while read f; do
  git show "origin/main:$f" | tr '\n' ' ' | sed "s/' *+ *'//g" | grep -o -E "@objectstack/spec 18"
done | wc -l
file count
packages/spec/src/kernel/plugin-lifecycle-advanced.zod.ts 5
packages/spec/src/system/disaster-recovery.zod.ts 2
packages/spec/src/system/cache.zod.ts 2
packages/spec/src/api/export.zod.ts 2
packages/spec/src/ui/view.zod.ts 1
packages/spec/src/integration/connector.zod.ts 1
packages/spec/src/automation/execution.zod.ts 1
total 14

Lit control on the same corpus and method: 88 files say @objectstack/spec 17; tree-wide the split is 238 tombstones saying 17 against these 14. Dark control 0.

Why 17 is the right number

Established independently by the at-tier review, from primary sources rather than from the majority:

  • retiredKey()'s own docblock says the guidance names "the version that removed it", and its worked example is 17.0.0 — a package version.
  • The package is at 17.4.0 with 37 pending minor changesets and 0 major, so every one of these tombstones first refuses in 17.5.0. A reader who hits one is on 17.x, and "18" names a version that does not exist.
  • PROTOCOL_VERSION is pinned in lockstep with the package major (protocol-version.test.ts), so it cannot be moved to 18 unilaterally either.
  • MigrationStep.major is documented as "the protocol major this step migrates into" — a different axis by design. RETIRED_KEYS_BY_MAJOR[18] being 18 is correct and is not evidence that the prose should say 18. Conflating the two axes is the whole error.

This reproduces #4350's own reasoning, which reached 17 by the same route in July.

Scope note: 5 of the 14 are days old

plugin-lifecycle-advanced.zod.ts's five arrived with PR #17986 (#17780), part of the #15939 epic, merged 2026-09-13. That file is internally mixed — its own HEALTH_CHECK_INTERVAL_RETIRED says 17 — so the round followed the half it happened to read. ⇒ the class is not static; it is actively growing, which is the argument for a gate rather than a second hand-sweep.

For the record: the same defect was caught before landing on the epic's last card (#17785 / PR #18016) and corrected there, so the epic's remaining cards are clean. Only #17986's five got through.

Suggested shape — enforce-or-remove, not a third hand-sweep

  1. Correct the 14. Mechanical: the number, nothing else.
  2. Add the gate fix(spec)!: 退役处方指向 protocol 17 —— 这趟车真正会发的 major (#4350) #4359 did not. A rule that a retiredKey() prescription may not name a package major greater than package.json's current major is checkable from the tree alone and needs no release knowledge. ⚠️ It must join the concatenated literals before matching, or it will read the same false zero this defect has been hiding behind since July.

⇒ Without step 2 this is the third occurrence waiting to happen. Recorded as declared ≠ enforced: the rule is real, was ruled on in #4350, and has nothing holding it.

Dedupe

search_issues over this repo for the tombstone-version shape ⇒ 7 results, 0 open. #4350 is the direct predecessor (closed completed by #4359). #16277, #15437, #15332 are adjacent version-prose defects on other surfaces; #12772, #6914, #7062 are other tombstone-content classes. ⚠️ Declared limit: semantic search, not an exhaustive title enumeration.

Refs: #4350 (predecessor) · PR #4359 (its fix) · objectui#3101 (the downstream harm it recorded) · PR #17986 (source of 5 of the 14) · #15939 (the epic that surfaced the recurrence)

Activity

  1. claude commented on Sep 14, 2026

    @claude
    Contributor

    分诊 / Triage — 关为 #18048 的重复,四条独有证据已带过去

    同一缺陷:packages/spec/src 里 14 处墓碑写 @objectstack/spec 18。本卡 2026-09-13T14:31Z,#18048 15:33Z,相隔约 1 小时。

    #18048 存活,因为它已定级(pm:queue / domain:spec / priority:p3)且载着 ADR-0087 级别裁定的语境(为什么 18 永远不会是载体)。

    ⭐ 但本卡有四条 #18048 没有的,已全部写到那边:①这是复发(#4350 七月修过十处,PR #4359 手改、未留门禁 ⇒ 类回归);②下游实害先例(objectui#3101 把清理排期挂在「把 spec 推到 18 的那个 PR」上,一个永不到来的事件);③测量陷阱(短语跨 '…' + '…' 拼接换行 ⇒ 单行 grep 得假零,这正是它没被发现的原因);④亮控 88 文件写 spec 17、全树 238:14、暗控 0。

    ⇒ 据此已扩大 #18048 的范围:补一道门禁,且门禁必须先 join 行再匹配。⛔ 不补门禁就会有第三次。

    分诊席位 · session_01PAMZt3owWHe7CMyTzrDkwF · R+233 · 经 REST 通道以 claude[bot] 续跑 · 本评论来自分诊座位


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions