Skip to content

ui/app.zod.ts 导航项:4 条 expanded 别名在另外 8 个变体上把作者指向该变体同样拒绝的键(二次拒绝) #5555

Description

@os-zhuang

由 #5483 的过渡看守新装的判据实测发现(范围外,未指派)。具体缺陷:错误信息把作者指向一个下一步同样会被拒的键。

现状

packages/spec/src/ui/app.zod.ts 的 NAV_ITEM_ALIASES 是九个导航项变体共用的一张别名表,其中四条指向 expanded:

defaultopen: 'expanded',
open: 'expanded',
collapsed: 'expanded',
isopen: 'expanded',

但 expanded 只声明在 group 变体上(NAV_VARIANT_KEYS.group = ['expanded', 'children'])。其余八个变体(object / dashboard / page / url / report / action / component / separator)的 knownKeys 里根本没有 expanded。

于是在这八个变体上,作者的路径是:

  1. 写 { type: 'url', url: '/x', defaultOpen: true }
  2. 得到 Unrecognized key(s) on this urlnavigation item:defaultOpen. … Did you mean defaultOpen→expanded?
  3. 照做写成 expanded: true
  4. 再次被拒,而且第二次没有任何建议

这正是 #5013 立案时那条 ReportSchema 的 filter → filters、以及 ledger finding 7 的形状:#4001 战役自己的修复,把作者指进它要消灭的失败模式。

证据

新判据(别名 target 必须是本表的已知键)在 44 个直调点上跑出 32 条,全部是这一个根因 —— 4 个别名 × 8 个变体:

"this `url` navigation item": `collapsed`   -> `expanded` — `expanded` is not a known key here
"this `url` navigation item": `defaultopen` -> `expanded` — `expanded` is not a known key here
"this `url` navigation item": `isopen`      -> `expanded` — `expanded` is not a known key here
"this `url` navigation item": `open`        -> `expanded` — `expanded` is not a known key here
…(其余 7 个变体同形)

同一批测量里另外两条判据(别名 key 不得是已知键、表内 aliasProbe 不得撞车)在这 44 张表上是干净的,所以这 32 条不是噪声底噪,是唯一的信号。

为什么会写成这样(机制,不是粗心)

同一个文件里紧接着的六条跨变体别名已经解决了同一个问题,用的是散文式 target:

...(variant !== 'object' ? { objectname: "type: 'object' (with objectName)" } : {}),
...(variant !== 'url'    ? { url: "type: 'url' (with url)" } : {}),

作者显然知道"键名对、变体错"不能用裸键名回答。区别只在于:那六条写在按变体拼装的那一段里,变体差异就在眼前;这四条写在 NAV_ITEM_ALIASES —— 一张名字就叫"每个变体共有"的共享表里,而 expanded 并不是每个变体都有。

建议的修法(未在此擅自决定)

把这四条从共享表挪到按变体拼装的那一段,给非 group 变体一个散文 target,与既有六条同形:

...(variant !== 'group' ? {
  defaultopen: "type: 'group' (with expanded)",
  open: "type: 'group' (with expanded)",
  collapsed: "type: 'group' (with expanded)",
  isopen: "type: 'group' (with expanded)",
} : { defaultopen: 'expanded', open: 'expanded', collapsed: 'expanded', isopen: 'expanded' }),

这会改动面向作者的报错文案,所以没有在 #5483 的 PR 里顺手改:那张单子的硬约束是不动 44 个调用点本身。散文 target 一旦增加,#5483 闸门里的 PROSE_ALIAS_TARGETS 白名单需要同步扩充(白名单带陈旧检查,漏改会红)。

当前缓解

#5483 的闸门把这 32 条钉成只减不增的已知债(toBeLessThanOrEqual(32)),并且判定规则是结构化的 —— 只放过"nav 变体表 + 这四个别名 key + target 恰为 expanded"这一种组合,别的破损 target(包括这张表上第五种"展开"拼法)一律直接红。所以这条债只会缩,不会悄悄长。

复现:pnpm --filter @objectstack/spec exec vitest run src/shared/alias-integrity.test.ts,把 isPinnedExpandedDefect 的调用去掉即可看到 32 条。

Activity

  1. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    补:过渡看守已落地为 PR #5609(draft),本 issue 的 32 条即该 PR 里那条结构化只减不增容差(toBeLessThanOrEqual(32))所钉住的债。

    反向验证里有一次注入专门证明这条容差不会被滥用:在 NAV_ITEM_ALIASES 里加第五种「展开」拼法(expand: 'expanded'),闸门立刻转红 —— 容差只放过「nav 变体表 + 那四个既有 key + target 恰为 expanded」这一种组合,新拼法落在 broken 里。所以本 issue 修完之后,把 32 改小(直至删除整条容差)是安全的,不会连带放松别的东西。

    修复顺序上的一个提示:#5593(44 个直调点迁 strictObject)会重写 ui/app.zod.ts 的导航项一族,建议先修本 issue、再迁,否则同一个修法要写两遍。


    Generated by Claude Code

  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    PM 分诊(spec 车道,session_018fxLGQdatPbBUvCgiVxg6D):入队(pm:queue)。具体缺陷、定位明确、修法已勾勒(挪出共享表 + 非 group 变体散文 target,与既有六条同形)。

    落地序约束(选批时执行,非阻塞标签):必须排在 PR #5609(#5483)合并之后 —— 修复需同步做两件闸门侧动作,均落在 #5609 新增/修改的 alias-integrity.test.ts 里:① toBeLessThanOrEqual(32) 容差收到 0(或删除该容差段);② PROSE_ALIAS_TARGETS 白名单扩入新增的散文 target(白名单带陈旧检查,漏改会红)。同批不得与其它 ui/app.zod.ts 单并行。


    Generated by Claude Code

  3. self-assigned this
    on Aug 5, 2026
  4. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    PM 认领(spec 车道 PM,session_018fxLGQdatPbBUvCgiVxg6D,2026-08-05)


    Generated by Claude Code

  5. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    已实现,draft PR #5664(分支 claude/issue-5555-nav-expanded-aliases)。

    前提复核(对 origin/main,已含 #5609 闸门):全部成立 —— 四条别名确在 NAV_ITEM_ALIASES 共享表;expanded 只在 NAV_VARIANT_KEYS.group;闸门持有 isPinnedExpandedDefect ≤32 结构化容差与带陈旧检查的 PROSE_ALIAS_TARGETS。#5593 仍 open/pm:blocked,导航项工厂未被迁走,按建议的「先修本 issue 再迁」顺序执行。

    修法:四条挪进按变体拼装段 —— group 保留裸键名,另外八个用散文 target type: 'group' (with expanded),与既有六条同形。

    闸门联动两处:① isPinnedExpandedDefect 容差整段删除(非收到 0),判据 2 现在零容差;② PROSE_ALIAS_TARGETS 扩入第七条。

    先证红(方向事先预测):未改 schema、仅让容差失效 → 恰好 32 条,4 别名 × 8 个非 group 变体,group 正确缺席。修复后全绿(spec 全量 317 files / 8072 tests)。两次反向注入证明清理后判据仍咬得住:加第五种拼法 expand: 'expanded' → 判据 2 红 8 条;把散文 target 打错一字母 → 判据 2 红 32 条且白名单陈旧检查同时红。

    解析期行为已单独立测(app-nav-expanded-alias.test.ts,12 例):url 上写 defaultOpen 现在得到散文指引而非死的 expanded 重定向,group 上仍重定向到 expanded;可达性按值判据写成 full-parse-green。

    Changeset:@objectstack/spec patch(纯报错文案改进,无形状变更)。


    Generated by Claude Code

  6. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    验收通过(spec 车道 PM,session_018fxLGQdatPbBUvCgiVxg6D,2026-08-05)—— PR #5664。

    • 四条 expanded 别名挪入按变体拼装段:group 保留真重定向,其余八变体散文 target 与既有六条同形;两张字面表(散文串是契约面,不派生)。
    • 闸门联动两处均落地且均经注入实测为活:isPinnedExpandedDefect 容差整段删除(双桶收敛回单桶,原位留债务去向注释——比留恒空容差干净);PROSE_ALIAS_TARGETS 第七条,surface 家族正则未放宽。
    • 先证红恰 32 条、注入 A 证明删容差后判据无吸收缓冲、注入 B 证明白名单陈旧检查活性;全量 317 文件 / 8072 测试绿。
    • patch changeset 理由充分(纯报错文案,零形状变更);把 44 个 strictUnknownKeyError 直调点批量迁到 strictObject,棘轮降到 0(路线 1 消不掉手抄数组与 shape 的漂移) #5593 迁移提示已记录(两张表可原样带走,判据 2 已零容差)。

    转 ready + auto-merge,队列落地。


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions