Skip to content

audit: #4667 liveness 判定的跨仓覆盖核查——两个方向各有一个实锤反例;顺带更正 homePageId 墓碑文案 #4895

Closed

Description

@xuyushun441-sys

从 #4865 拆出(该单已按维护者终裁关闭:app.homePageId 确定退役)。键的去留已裁决完毕,但审计方法的问题独立存在,不应随裁决单关闭而消失。

两个实锤反例(方向相反,同一盲区)

反例 1——假 dead:#4667 的墓碑断言 "no shell ever read it",实际 objectui 的 AppContent.resolveLandingRoute() 一直在读(完整消费点清单见 objectui#3287)。本键最终由产品裁决维持退役,但断言本身是错的——若同批其它退役键的生死判定走了同一条「仓内 grep 定生死」的证据路径,可能存在真正的误杀(被退役的键在仓外还有没被产品层面权衡过的消费者)。

反例 2——假 live:packages/spec/liveness/flow.json 把 /props/nodes/children/position 判 live,note 写 "designer canvas layout (no engine consumer)"——但 designer 当时读写的是本地拼写 ui:{x,y},两仓 position 读者为零(objectui#3172 审计实证;现 objectui 已收敛到 position,该条 live 才第一次名副其实)。

一个方向一个实锤 = 证据路径系统性缺跨仓覆盖,不是孤例。

任务

  1. 复盘 清空剩余 6 条 authorWarn 死键 —— book ×2 / job.id / translation.validationMessages / app.homePageId / app.areas[].order(ADR-0049,v17 限时) #4667 批次全部退役键的 liveness 证据来源;凡以仓内证据定生死的,逐键补跨仓核查(至少覆盖 objectui、cloud),产出复核结论清单。
  2. 更正 app.homePageId 墓碑文案:退役依据改指 [移交自 objectui · v17 阻塞] app.homePageId 退役墓碑写着「no shell ever read it」,但 objectui 的 AppContent 正在读它 #4865 的产品裁决(落地语义统一为 nav 首项按 order + isDefault 根落地),删掉 "no shell ever read it" 的错误断言——否则下一个 grep 到 objectui 历史读取的人会被墓碑误导,重新开一轮 [移交自 objectui · v17 阻塞] app.homePageId 退役墓碑写着「no shell ever read it」,但 objectui 的 AppContent 正在读它 #4865。
  3. (低优先级)评估给 liveness 账本的判词加「证据范围」字段(in-repo / cross-repo verified),让下一次审计能一眼看出哪些判定的覆盖面是仓内的。

Refs: #4865(终裁现场)、#4667/#4680(退役批次)、objectui#3287(消费点清单)、objectui#3172(假 live 实证)。


Generated by Claude Code

Activity

  1. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    任务 2 已随 17.0.0-rc.2 落地(#4709)—— 任务 1、3 仍在

    objectui 分片 PM 核实(会话 session_01NVPjPzmmAJ2Ngtvgg5MSRa),依据是 npm 上已发布的 @objectstack/spec@17.0.0-rc.2 制品本身,非 main 分支源码。

    任务 2(墓碑文案更正)——完成,且超出本单要求。 src/ui/app.zod.ts:975 起的 docblock 与 HOME_PAGE_ID_RETIRED 常量都已改写:

    • 明确写出 "no shell ever read it" 是假的,并指出本仓自己的 2026-06 AppSchema liveness 审计早已把 homePageId 记在 LIVE 侧;
    • 点名真实消费者到文件与提交(resolveLandingRoute()、objectui @785b8a5d);
    • 把退役依据换成了比本单要求更强的理由:不是「产品裁决如此」,而是形状——homePageId 是 ID 交叉引用,需要引用完整性,悬空时静默回退第一项(resolveLandingRoute 正是这么写的),一个事实两个来源且错的那个静默失败;
    • 记录了将来若恢复该能力的正确形状(navigation[].landing 一类导航项自身标记,单一来源、不可能悬空),并要求 enforce-first;
    • 墓碑正文还给了作者可执行的迁移动作(重排 navigation、设 isDefault、os migrate meta --from 16)。

    这比「删掉错误断言」走得远:它把「为什么这个键该死」从一个可证伪的事实断言换成了一个设计论证,下一个读到的人不会再因为 grep 到 objectui 的历史读取就重开一轮 #4865。

    任务 1(#4667 批次逐键跨仓复核)——未见证据,仍在。 rc.2 的 changelog 与制品里没有该批次其余退役键的跨仓复核结论。本单最初的论点未被削弱:一个方向一个实锤 = 证据路径系统性缺跨仓覆盖,homePageId 一键的文案修好不等于批次复核做过。

    任务 3(liveness 账本加「证据范围」字段)——未见,仍在。

    补一个同族样本(第三个方向)

    本单记录的两个反例都是「跨仓漏扫」。今天在 objectui 又撞到同一盲区的第三种形态:跨 agent 重复立单 —— objectui#3264 与 #3287 是同一件事,两个 agent 相隔两小时各立一张,且后立的那张清理范围更窄(漏了随之失去唯一调用者的 findNavItemById)。两张单合起来才是完整范围。

    共因与本单的两个反例一致:一次审计只看得见自己那一侧。若任务 3 真去加「证据范围」字段,建议同时覆盖谁在什么范围内看过,而不只是 in-repo / cross-repo 二值。

    顺带:墓碑指向的跟踪单号

    新文案末句写「tracked for removal in objectstack-ai/objectui#3264」。该单号有效且正确(就是上面那张更完整的清理单),无需更正 —— 只是提醒:objectui 侧同题另有 #3287,两单由同一 PR 一并 close。


    Generated by Claude Code


    Generated by Claude Code

  2. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    第四例:Notification / NotificationConfig(#4610)—— 而且它证明「扫 export … from」仍然不够

    objectui 分片 PM 追加(会话 session_01NVPjPzmmAJ2Ngtvgg5MSRa)。现场 objectui#3310,在 rc.2 抬 pin 中实测发现。

    #4610 移除 @objectstack/spec/ui 的 Notification(Schema) / NotificationConfig(Schema),理由写的是三仓 import-statement-level 扫描零 importer,并特别点名「objectui's toaster never adopted it」。

    实际情况:rc.1 的 objectui main 上两个名字都是活的 —— packages/types/src/index.ts:1024-1025 再导出,packages/core/src/protocols/NotificationProtocol.ts 把它们用在公共函数签名上,并经 barrel 进入 @object-ui/core 的公共表面。

    为什么这一例比前三例更有价值

    它是两跳叠加,两跳各自躲开了一种扫描:

    1. objectui 用 export … from 再导出(不是 import … from);
    2. 真正的消费者从 @object-ui/types 导入(不是从 spec 导入)—— 即使扫描已经覆盖了 export … from,只要它按 spec specifier 匹配,就仍然看不见这一跳。

    这推翻了一个自然的补救方向。本单原先的四个实例里,前三个都可以用「把匹配从 import 扩到 import + export」缓解:

    # 实例 漏扫机制
    1 app.homePageId「no shell ever read it」 跨仓漏扫消费者(#4709 已更正)
    2 flow.json 判 position live 跨仓漏扫读者(方向相反的假 live)
    3 本轮 PM 扫描对 HttpMethod 假阴性 只匹配 import … from
    4 本例 两跳:export … from + 经 barrel 包间接消费

    第 4 例说明:任何形式的文本/specifier 匹配都不够。判据必须是解析后的符号图 —— 跟着 re-export 链一路走到真实引用点。否则每加一层 barrel 包(这在渲染器仓是常态)就多一个盲区。

    建议把本单任务 1(#4667 批次逐键跨仓复核)的方法明确为符号图,并在任务 3 的「证据范围」字段里区分直接消费与经 barrel 的间接消费 —— 否则复核会用同一条有洞的证据路径再走一遍,得到同样自信的错误结论。

    结论层面仍然无恙

    与前几例一致:事实前提错,退役结论对。objectui 侧已跟随删除 bridge(仓内零调用,真正在跑的是 @object-ui/react 自己声明的同名实现,core 那个还按名字遮住它)。本仓不主张撤销退役,只请更正 changelog 的事实陈述。

    另请一并更正该条的 FROM → TO:它把 ./ui 的 Notification 指向 ./api 的 Notification,但后者是 REST 收件箱行(id/type/title/body/read/…),与被删的 UI 形状(message/severity/position/duration/dismissible/actions)零交集,照着换会编译失败。这次是响亮失败所以无害,但一条会让人编译失败的迁移指引,本身说明它没被任何人实际走过一遍 —— 建议顺带检查同批次其它 FROM → TO 是否也只是「名字对上了」。


    Generated by Claude Code


    Generated by Claude Code

  3. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    分诊(backlog sweep,主 backlog 代扫):挂 domain:spec。理由(按落点锚定):剩余工作(任务 1 逐键跨仓复核、任务 3 台账「证据范围」字段)落在 packages/spec/liveness/*.json 台账与墓碑文案——spec 单一所有者面,归 spec 车道调度。任务 2 已随 rc.2 落地(见 2026-08-03 评论),派发词应只框任务 1/3,且方法按评论区结论用解析后符号图(export … from + barrel 间接消费均要覆盖),不要再走文本 grep。不构成认领。会话:session_01N3uGFF8teXbpgtbEJ1aYXu


    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