Repository navigation
清空剩余 6 条 authorWarn 死键 —— book ×2 / job.id / translation.validationMessages / app.homePageId / app.areas[].order(ADR-0049,v17 限时) #4667
Description
#4488 的活性审计把「声明看起来在做事、实际什么都不做」的键标成 dead + authorWarn。#4509、#4583 之后,账本里还剩 8 条 authorWarn。其中 2 条(app.areas[].visible / requiredPermissions)是 fail-open 授权闸门、需要单独决策,已在 #4651。
本 issue 收剩下的 6 条——它们的共同点是处方已经在账本里写死、没有待决问题,全部走 remove。
⏳ 为什么限时
@objectstack/spec 现在 17.0.0-rc.1,.changeset/pre.json 仍是 mode: pre。破坏性移除现在落地进 17.0.0;changeset pre exit 之后要等 v18。 这 6 条会在整个 17.x 生命周期里继续以「看起来在配置什么」的姿态被作者写下去。
六条 + 各自的路线
⚠ 路线不是统一的,而两条路线的账本纪律是相反的(strict 删除 ⇒ 删账本行,否则报 ORPHAN;retiredKey 墓碑 ⇒ 保留账本行,否则报 UNCLASSIFIED)。逐条确认过:
| # | 键 | 所在 schema | 路线 | 账本行 |
|---|---|---|---|---|
| 1 | book.translations |
BookSchema(strictObject) |
strict 删除 + guidance |
删 |
| 2 | book.groups[].translations |
BookGroupSchema —— plain z.object,没有 .strict() |
retiredKey 墓碑 |
留 |
| 3 | job.id |
JobSchema(strictObject) |
strict 删除 + guidance |
删 |
| 4 | translation.validationMessages |
ObjectTranslationDataSchema(strictObject) |
strict 删除 + guidance |
删 |
| 5 | app.homePageId |
AppSchema(strict) |
与同文件既有退役键一致 → retiredKey |
留 |
| 6 | app.areas[].order |
NavigationAreaSchema(strict + strictUnknownKeyError) |
strict 删除 + guidance |
删 |
第 2 条是这批最容易做错的一处:BookGroupSchema 不是 strict,所以平删会被 zod 静默 strip —— 一个静默 no-op 换成另一个静默 no-op(#2169 「Mark Done 什么也没做」的形状)。必须走 retiredKey。
每条的证据与处方(摘自账本,均 verifiedAt 2026-08-01)
1 + 2 book.translations / groups[].translations —— 没有任何 resolver 读 book 的内嵌翻译图;tree endpoint 和 portal 逐字渲染 label/description,通用 bundle 翻译器只覆盖 view/action/object/app/dashboard/page。陷阱在于邻接:两个文件之外的 doc.translations 在每条读路径上都是活的,所以这个图读起来像同一个特性。
3 job.id —— describe() 写着「省略时默认取 name」,暗示一个不存在的身份覆盖。name 才是 job 在每一处的身份:调度键(app-plugin.ts:833)、sys_job 行键(db-job-adapter 按 name upsert 并自己铸 row id)、JobExecution.jobId 戳。没有任何代码读 id,所以两个只有 id 不同的 job 是同一个 job。
4 translation.validationMessages —— 平台自己的签名按在上面两次:schema 示例给了具体覆盖({"discount_limit": "折扣不能超过40%"}),而 #3778 的 legacy-key 迁移表把退役的 errors: 作者往这儿指。
⚠ 移除时必须改写
translation.zod.ts:291的errorsguidance:
`errors` is the retired object-first dialect — use 'validationMessages' for rule messages
它是一条指向死键的处方。这已经是同类第三次了(#4583 批 A 的READ_ONLY_BELONGS_ON_DATASOURCE、#4509 的mapping.guidance.skipErrors)——退役一个键时,先 grep 有没有别的处方指着它。
5 app.homePageId —— schema 自己的对冲措辞(「if not set, usually defaults to the first navigation item」)描述的就是唯一存在的行为。没有 shell 读它:落地页就是第一个导航项(按 order),根落地走 isDefault 路由(objectui RootLandingRedirect)。⚠ appUnknownKeyError 里有三个 alias 指着它(home / homepage / landingpage,app.zod.ts:905-907),要一并处理。
6 app.areas[].order —— 没有渲染器给 areas 排序(AppSidebar 和 AppSchemaRenderer 都按数组顺序迭代),声明顺序就是显示顺序。对照组是真被排序的 nav 项 order(NavigationRenderer.tsx:1154)——能用的兄弟键正是它读起来像活的原因。⚠ NavigationAreaSchema 的 alias 里有 sort: 'order'。
需要一并做的
- ADR-0087 D2 conversion + D3 链步:
app.homePageId/areas[].order可以扩已有的app-dead-authoring-keys-removed(同 major 同类型,新开会撞 fixture 互斥契约);book / job / translation 各需入册 - 三处指向死键的 alias / guidance 清理(上面标 ⚠ 的)
authorable-surface.json:strict 删除的行消失是闸门 (a) 的绊线,要在同一 PR 里刻意删;retiredKey的则变成… [RETIRED]lint-liveness-properties.test.ts跑在真实 shipped 账本上,book.translations/groups.translations/job.id的正向断言会随账本反转 —— datasource 账本判定的 20 条死键至今无人处置:三个块整块无人读,其中 readOnly 让一个 shipped 示例的「只读副本」可写(ADR-0049 enforce-or-remove) #4583 和 #4488 审计发现的四个"授权门断连":email_template / job / validation 的元数据条目到不了执行点,action 导航项点不动 #4509 都在这里翻过车,预先处理liveness/README.md的 book / job / translation / app 行 + 计数(计数用闸门的--json重算,不要手编)
完成后
authorWarn 只剩 #4651 那两条 —— 而那两条需要的是决策,不是补丁。
Activity
os-zhuang commentedon Aug 2, 2026
🔒 CLAIM
session_01E5CYr5SDwe85gH2Jr5KSguclaude/auth-gate-disconnect-issues-6wz8ewobjectstack-4509开工时机:等 #4664(#4509 三项收尾)出合并队列。它现在
mergeable_state: clean已入队;在它落地之前于同一分支上提交,会把这批工作堆进那个已经绿了、已入队的 PR。落地后从最新 main 重开分支。同期已完成的相邻工作,供交叉参考:#4558(#4509 四条主线)、#4601 + #4629(#4583 datasource 20 键归零)、#4664(本批的前一批)。
进度会随做随更新本条下方。
Generated by Claude Code
docs: correct the hand-written pages that still taught the retired ke…
os-zhuang commentedon Aug 2, 2026
已随 PR #4680 落地并合入
main(17:51Z,走合并队列,21 项检查全绿)。六条键全部退役,两条路线的账本纪律相反,逐条按 issue 里的表执行:retiredKey墓碑 —— 账本行保留book.groups[].translationsapp.homePageIdstrict 删除 +
guidance—— 账本行删除book.translationsjob.idtranslation.validationMessagesapp.areas[].orderBookGroupSchema是 plainz.object、没有.strict(),平删会被 zod 静默 strip —— 这条走墓碑,并加了一条 pin 断言它必须 throw 而不是被 strip。退役的 alias 拼法(
i18n/home/homepage/landingpage/sort)都指向同一处处方,而不是改名到一个同样已经没了的键。#3778 那条把errors作者指进validationMessages的处方已改写为指向object.validations[].message—— 这是同类第三次,退役前先 grep 反向处方这条经验已写进 PR 正文。ADR-0087:三条新 conversion + 扩展
app-dead-authoring-keys-removed下钻areas数组,全部接进 protocol-17 的 D3 链步。活性账本
authorWarn8 → 2,剩下的就是 #4651 的两个 fail-open area 闸门(app.areas[].visible/requiredPermissions)—— 那两条要的是决策,不是补丁。Generated by Claude Code
feat(spec)!: 清空剩余六条 authorWarn 死键 —— book ×2 / job.id / translation.v…
feat(spec)!: 移除 app.areas[] 的两个 fail-open 访问闸门 —— visible / requiredP…
docs(spec): `app.homePageId` 墓碑说清真正的退役理由 —— 「no shell ever read it」是假的 (
docs(ui): apps 页停止教 17.0.0 已退役的 `version` / `mobileNavigation`,两个 os:…
feat(spec): the liveness state table's counts become a generated arti…
fix(spec): correct stale "DocSchema declares no tags" restatements in…
fix(spec): correct stale "DocSchema declares no tags" restatements in…
feat(spec,objectql): give a custom validation rule's message a bundle…
feat(spec): translate dataset labels via translateDataset + a dataset…
docs(spec): book.json's liveness header names the dead row, not a pai…
docs(spec): book.json's liveness header names the dead row, not a pai…
fix(example-todo): re-key the three locale bundles' 19 dotted `messag…
fix(spec): JobSchema's own @example no longer opens with the retired …
fix(spec): the protocol 16 → 17 conversion summaries and three descri…
test(spec): automation test titles state each cited decision in words…
test(spec): the first ui/ file group's test titles state each cited d…
test(spec): the first system/ file group's test titles state each cit…
test(spec): the last system/ file group's test titles state each cite…