Skip to content

fix(metadata-protocol): 读路径 _diagnostics 保留 union 分支的真实拒绝理由 (#5598) - #5765

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5598-diagnostics-union-branches
Aug 6, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-5598-diagnostics-union-branches

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5598

问题

computeMetadataDiagnostics(packages/metadata-protocol/src/metadata-diagnostics.ts)把 zod 的 error.issues 直接 .map()_diagnostics 信封条目。zod 会把一个失败 z.union全部分支折叠成一条顶层 issue —— path 为空串、message 是字面量 "Invalid input" —— 而 ViewMetadataSchema 顶层本身就是 union(z.preprocess(stripViewConsoleDecorations, z.union([...]))),所以库里每一个有缺陷的 view 文档读出来都退化成这一条没有字段名的记录。该文件模块头承诺的用途正是让 Studio 渲染 validity badge、内联字段错误和治理看板,而内联字段错误无处可标。

这不只是"少了点信息",而是同一份文档在两条路径上判决不一致:#5364(PR #5596)修好写路径之后,作者保存一个有缺陷的 view 能看到出错的键名;打开同一份已存在库里的文档却仍然只得到一条 Invalid input。这是同一机制的第 5 个消费者(#4971 / #5014 / #5341 / #5364 是前四个)。

改动

metadata-diagnostics.ts 一处 .map() 换成调用同包 #5596 已落地并导出的 zodIssuesToMetadataIssues复用而非再抄一份策略是重点:分支选取口径(丢弃只报根部 KIND 不匹配的分支;报得最少的分支胜出;unrecognized_keys 破平局;并列全出且有上限;嵌套 union 按绝对路径递归)由该函数单点定义,读写两路径按构造一致,不可能各自漂移。

  • ⛔ 未改 protocol.ts —— 该函数已是模块级 export,无需移动或调整导出。
  • stripDiagnostics 一段未动;新增用例专门守住"读两遍不会把信封自己判成非法"。
  • 对消费者是纯增量:union 自己那条记录仍排在 errors[0],后面才跟上解释它的分支条目,读 errors[0] 的既有代码读到的还是同一条。

实测,issue 正文那份 view 输入现在得到:

{ "valid": false, "errors": [
  { "path": "", "message": "Invalid input", "code": "invalid_union" },
  { "path": "", "code": "unrecognized_keys",
    "message": "Unrecognized key(s) on this view container: `type`, `columns`. …
      • `type` belongs to a single VIEW, not to the container. Wrap it: …" }
] }

第二条正是 #4001 那批 strictObject 的策展处方 —— 以前被 .map() 生产出来又丢掉。

测试

新增 packages/metadata-protocol/src/metadata-diagnostics.union-issues.test.ts(8 例):3 例守 union 展开(展开发生、与共享排序逐字节一致、decorateMetadataItem 把它带到 Studio 面前),1 例守 strip 未被破坏,4 例是对照组 —— 没走 union 的普通字段级拒绝、非对象文档、spec 合法文档、未注册类型,行为必须不变。

反向验证(方向事先预判:红):把删掉的 .map() 限肢放回去,3 条 union 用例转红、4 条对照组保持绿 —— 缺陷本身被写成了一个数字:

 × expands the union instead of serving one rootless "Invalid input"
 × serves the same verdict the save path serves — one ranking, not two
 × reaches the Studio-facing surface — `decorateMetadataItem` carries it
AssertionError: expected 1 to be greater than 1
 Test Files  1 failed (1)      Tests  3 failed | 5 passed (8)

恢复修复后:

packages/metadata-protocol  vitest run
 Test Files  46 passed (46)      Tests  427 passed (427)

消费半径清扫(该判决在哪里被读就在哪里查 fixture,不按被改包划界):git grep _diagnostics 覆盖 metadata-protocol / objectql / metadata / rest / service-automation —— 除 objectql 外均只断言 valid 标志或 warning(另一个生产者),不受增量影响;objectql 直接测 computeMetadataDiagnostics,已跑:

packages/objectql  vitest run src/metadata-diagnostics.test.ts src/protocol-meta.test.ts
 Test Files  2 passed (2)      Tests  92 passed (92)

typecheck / 构建 / 门禁:

  • tsc --noEmit -p packages/metadata-protocol/tsconfig.json —— 改动前后输出逐行相同(159 行,全是该包既有的 test-layer DEBT,check:type-check-coverage 已登记),我的两个文件零错误。
  • pnpm --filter @objectstack/metadata-protocol build —— tsup ESM/CJS/DTS 全部 Build success,无循环依赖告警。
  • check:nul-bytes OK(5641 文件)、check:type-check-coverage OK、check:query-options-erasure OK(baseline 对 5e3c83b 核过,无新增文件)、check:slot-lookup OK、check:published-files OK、check:error-code-casing OK;两个文件 eslint --no-inline-config 干净。

需要评审注意的一点

metadata-diagnostics.ts 现在 import { zodIssuesToMetadataIssues } from './protocol.js',而 protocol.ts 本来就 import 了 metadata-diagnostics.js,于是两个模块间形成一个包内循环 import。运行期安全(函数声明提升,zodIssuesToMetadataIssues 只在运行期被调用,两个模块都没有模块级互相调用),tsup 打包与 DTS 均无告警,全量测试绿。但方向上是小叶子模块反向依赖了大模块 —— 这正好给 issue 正文那条后续裁决项(「五处策略是否收敛成一个共享实现」)多加一个论据:真正的落点应该是一个中立模块,而不是继续按份数增长。按派单约束我没有动 protocol.ts,这条留给 PM 裁决。


Generated by Claude Code

computeMetadataDiagnostics 把 zod 的 error.issues 直接 .map() 成 _diagnostics
条目。zod 会把一个失败 z.union 的全部分支折叠成一条顶层 issue —— path 为空、
message 是字面量 "Invalid input" —— 而 ViewMetadataSchema 顶层本身就是 union,
所以库里每一个有缺陷的 view 读出来都退化成这一条没有字段名的记录,模块头承诺的
"内联字段错误"无处可标。

后果是同一份文档在两条路径上判决不一致:#5364(PR #5596)修好写路径之后,保存
一个有缺陷的 view 能看到出错的键名,打开同一份已存库的文档却仍然只有一条
Invalid input。

改法是复用而非再抄一份策略:读路径改调同包 #5596 落地的
zodIssuesToMetadataIssues,分支选取口径由该函数单点定义,读写两路径按构造一致。
展开是纯增量 —— union 自己那条仍在 errors[0]。stripDiagnostics 一段未动。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7WetGmnfoXNn8cLieKKmx
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 6, 2026 4:17am

Request Review

@github-actions github-actions Bot added the size/m label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-protocol.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/metadata-protocol)
  • content/docs/releases/v9.mdx (via @objectstack/metadata-protocol)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants