Skip to content

合法但惰性的元数据:建 ADR-0078 的完整性闸门(共享谓词 + 全授权面执行) #4544

Description

@os-zhuang

洞在哪

一个元数据实例可以:Zod 校验通过(闸门 1 绿)、用到的每个属性都是 live 的(闸门 2 绿)、而且一个正确写法的同类实例被证明能跑(闸门 3 绿)——然后它依然是死的,因为它漏了消费者需要的兄弟配置,而消费者静默 no-op

奠基案例(cloud#687):一个 AI 写出 { type: 'summary' },没有 summaryOperationsengine.ts 一句 if (!d.summaryOperations) continue 把它跳过。 字段到处读出 0,依赖它的「入住率」公式永远是 0,而 agent 报告「上座率做好了」。

为什么这比 #4001 刚关掉的洞更糟

未知键静默剥离(#4001,已关) 功能性不完整(本 issue)
作者写了什么 一个我们不认识的键 全是我们认识的键
schema 判定 拒绝(现在) 合法
谁会说话 解析拒绝,带处方 没有人
作者看到 一条错误 成功

AI 拿到 success 信封然后报告 done——而「起疑」正是 AI 授权移除掉的那一步。

决策(ADR-0078)

一个「省略后会让实例静默失效」的配置,必须恰好处于三态之一:(1) 被完整性 lint 抓住 · (2) 标记 [EXPERIMENTAL — not enforced] · (3) 真正可选且优雅降级。第四态——「解析通过、无标记、静默惰性」——被禁止。


✅ 全部阶段落地(两仓库)

阶段 内容 状态
1 共享谓词 + validate-functional-completeness + 注册(28 → 29 条) #4547
2 blueprint expression 槽位 + parity pin(framework)· objectBody 透传 + rowColor 修复(cloud) #4577 · ✅ cloud#999
3 Tier-B 逐条验证,落地 webhook/without-triggers #4565
nav 非对象目标(归 ADR-0072,16 → 17 条) #4574
4 注册时诊断建成;生成式遍历否决 #4599

终态闭环:author-time error ↔ runtime warn,一个共享谓词裁决两端。 dogfood boot 兼任权威扫描:所有出货 stack(平台对象、showcase、CRM、Todo)注册时 0 条 functionally-incomplete 警告——零误报,也没有藏在 lint 看不见的门后的惰性字段。

Phase 4 的裁决(直接决策,不再 evidence-gated)

生成式规则遍历:否决,不是推迟。 生成器能枚举候选,不能验证运行时跳过点;无跳过点引用的规则=假处方(本战役 4 条全靠验证遍拦下,自动生成在结构上绕开验证遍)。否决理由已写进 changeset,防止未来以「数据够了」重提。

注册时诊断:#4599 SchemaRegistry.registerObject 是所有元数据门的汇合点(含绕过 Zod 和 lint 的门——#3896rowColor.mapping 证明它们真实存在)。同一谓词、同一 rule id;warn 不 throw(error 的语义是「实例死了」不是「系统该死」)。webhook enqueuer 的静默 return null 同步补 warn,「manual-only」注释改真。

Phase 1 规则(每条附运行时静默跳过证据行)

field/summary-without-operations · field/formula-without-expression · field/relationship-without-reference · field/choice-without-options(select/radio error,checkboxes warning)· view/layout-without-binding(warning)· webhook/without-triggers(Phase 3)

首跑真实 app 即抓到 showcase_field_zoo.f_summary(裸 roll-up,已移除)。

Phase 2 —— A1 的真实形状

summaryOperations 半边早已修好(cloud#970);formula 被落下,且是死胡同而非静默剥离:模型能命名 formula 类型,graph-lint 能报 formula_without_expression,但 blueprint 里没有键可写处方。真正的修复是 parity pin:strict mirror(模型能发出的)与 lenient schema(下游读取的)现在被测试强制 key 一致。

顺带发现与修复


剩余工作(不阻塞,各有归属)

  1. cloud embedder binding 既有失败 —— ai-service.test.ts > embedder binding。非本战役引入(stash 验证:干净基线上文件根本加载不了),属 cloud settings/plugin 接线,留给该模块负责人。
  2. dataset 零 measures / 非法 cron —— 仍缺各自的验证遍(运行时静默跳过行未找到/未追踪)。找到才写,找不到不写。
  3. cloud graph-lint re-home —— 把 cloud 的重复规则迁到共享谓词上,消除漂移面(低急迫:现在两边行为一致)。

纪律(终版)

  1. 每条规则先指名运行时静默跳过的那一行;刻意不设的规则,把豁免证据一起钉进测试。
  2. 运行时注释的「祝福」需要第二个来源证明被祝福的模式仍可达。
  3. 写规则前先查它是否已存在 —— 本战役靠这条避开三个近似重复的 lint;没执行它的一次(cloud#1002,与 [Proposal] Create plugin-setup to own and finalize the platform Setup App #997 重复)当天就付了学费。
  4. 生成式产规则被永久否决 —— 它在结构上绕开第 1 条。

相关

ADR-0078 · ADR-0049 · ADR-0054 · ADR-0019 · ADR-0072 · docs/audits/2026-06-metadata-functional-completeness.md · cloud#687(奠基)· cloud#970(先例)· #4001(相邻洞,已关)
PR:#4547 · #4565 · #4574 · #4577 · #4599 · cloud#999 · (队列修复:cloud#997)

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