Skip to content

把 44 个 strictUnknownKeyError 直调点批量迁到 strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593

Description

@os-zhuang

由 #5483 的过渡看守(路线 1)落地后拆出的后续(未指派,记录测量与边界,不认领)。

现状

#5483 按路线 1 落地:strictUnknownKeyError 自己把 { surface, knownKeys, aliases, guidance } 登记进一张内部登记表,packages/spec/src/shared/alias-integrity.test.ts 因此把这 44 个直调点也判了 —— 零调用点改动。

实测规模:44 个源码调用点,运行时 52 张表(ui/app.zod.ts 的导航项工厂一个调用点跑九次,每个 type 变体一张)。

关键在于这批表被判到的强度不均,闸门自己也是这么写的:

判据 strictObject 那 235 张 这 44 个直调点
别名 key 不得是已知键 对 .shape 判 对手抄数组判
别名 target 必须是已知键 对 .shape 判(含墓碑检查) 对手抄数组判(墓碑不可见)
表内 aliasProbe 不得撞车 判 判 —— 完全等价

第三条不依赖 shape,所以路线 1 就把它开满了(首测 52 张表 0 撞车)。前两条继承手抄数组与 shape 之间的漂移 —— 数组说 x 是已知键,而 schema 里早就没有了,闸门会照着数组给出绿灯。那正是 strictObject 存在的理由(见其 docblock:"the array is a second copy of the truth"),也正是路线 1 消不掉的那一半。

该做什么

把 44 个调用点分批迁到 strictObject(options, shape):

  • 手抄的 *_KEYS 数组连同它的漂移探针测试一起删除;
  • 这批表自动进入 shape 判据(含"别名 target 不得是墓碑"这条路线 1 看不见的);
  • alias-integrity.test.ts 里 toBeLessThanOrEqual(44) 的只减不增棘轮随之降到 0;
  • 路线 1 的登记表(packages/spec/src/shared/alias-table-registry.ts)与闸门里"直调点"那一整段随最后一个调用点一起删除。

分布(实测,基线见下):

ui/app.zod.ts               9   data/datasource.zod.ts      6
automation/approval.zod.ts  4   automation/flow.zod.ts      4
security/permission.zod.ts  4   data/driver/*.zod.ts        6
data/hook.zod.ts            2   data/hook-body.zod.ts       2
security/sharing.zod.ts     2   data/driver/sqlite.zod.ts   (含在上面 6 里)
data/object.zod.ts          1   identity/position.zod.ts    1
security/rls.zod.ts         1   ui/action.zod.ts            1
ui/bulk-action.zod.ts       1

迁移时已知的三个坑

  1. data/object.zod.ts 的表是延迟构建的(objectUnknownKeyErrorImpl ??= …),为的是绕开一个 temporal dead zone —— UNKNOWN_KEY_GUIDANCE 和 shape 都声明在它下面。改成 strictObject 需要先解决这个顺序问题,不是纯机械替换。
  2. ui/app.zod.ts 的导航项是一个按变体拼装的工厂,九张表共用一段拼装逻辑,knownKeys 是 [...BASE, ...NAV_VARIANT_KEYS[variant]]。迁移意味着九个变体 schema 各自成为 strictObject,extraKeys 可能用得上。同一族还有 ui/app.zod.ts 导航项:4 条 expanded 别名在另外 8 个变体上把作者指向该变体同样拒绝的键(二次拒绝) #5555 那条待修缺陷,顺序上建议先修 ui/app.zod.ts 导航项:4 条 expanded 别名在另外 8 个变体上把作者指向该变体同样拒绝的键(二次拒绝) #5555 再迁,否则修法要写两遍。
  3. data/hook.zod.ts / data/hook-body.zod.ts 当时在 [spec] 退役 HookContext session.roles —— #4839 双删后零消费方零生产方(ADR-0049) #5050 手里,分批时避开在飞的文件。

基线

分支 claude/issue-5483-strict-unknown-registry,origin/main 5acb93a。测量复现:pnpm --filter @objectstack/spec exec vitest run src/shared/alias-integrity.test.ts。

Activity

  1. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    补:路线 1 的过渡看守已落地为 PR #5609(draft),本 issue 描述的基线以那个 PR 为准。

    实测数字定稿:44 个源码调用点 → 运行时 52 张表,strictObject 那边 235 张。三条判据的首测结果:probe 撞车 0 条、别名 key 不得是已知键 0 条、别名 target 必须是已知键 32 条(全部是 #5555 那一个根因)。

    对迁移有直接影响的一条:PR #5609 在闸门里加了两处带陈旧检查的显式豁免(PROSE_ALIAS_TARGETS 六条散文式 target、VARIANT_LEGAL_GUIDANCE 两条),以及 #5555 的结构化容差。迁移 ui/app.zod.ts 导航项一族时,这三处会同时失去依据而转红 —— 那是预期行为,不是回归:它们的存在前提就是那批表还在直调登记表里。届时应当删除,而不是改写成新形式。


    Generated by Claude Code

  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    发现分诊(#4949 纪律):晋级 pm:queue + pm:blocked(域已在 domain:spec,摘 finding)。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    打包建议(致 spec 座位):本单与 #5583 同为 strictObject 机械迁移族,建议同一接力棒内派发(一名 dev 一次落地、一 PR 多 Fixes 或紧邻两棒),摊薄四步重建 + CI 圈的每单固定成本。背景:维护者 2026-08-06 拍板 spec 车道缩盘方案,见 #5837。


    Generated by Claude Code

  4. hotlong commented on Aug 7, 2026

    @hotlong
    Contributor

    Release-board audit (maintainer-directed re-audit of non-board domain:spec items, 2026-08-07): adding target:v17 — metadata-protocol change, size M, low-risk: mechanical migration of 44 strictUnknownKeyError call sites to strictObject; pure strictness tightening backed by the alias-integrity gate/ratchet; batch with #5583. (pm:blocked on #5483/PR #5609 retained — board membership is independent of dispatchability.) Maintainer directive: protocol changes land in v17 unless large/risky. Triage seat may veto.


    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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions