Repository navigation
Description
在做 #5364(saveMetaItem 的 422 展开 invalid_union,PR #5596)时核出的第五个消费者。#5364 的分诊帖把这条线数成"四份各自独立的消费端代码",实测是五份 —— 第五份就在 #5364 同一个包里,但在读路径上,不在那单的文件面内,故独立记录。
落点
packages/metadata-protocol/src/metadata-diagnostics.ts:77(computeMetadataDiagnostics):
const parsed = (schema as z.ZodTypeAny).safeParse(candidate);
if (parsed.success) return { valid: true };
const errors = parsed.error.issues.map((issue) => ({
path: issue.path.map(String).join('.'),
message: issue.message,
code: issue.code as string,
}));和 #4971 / #5014 / #5341 / #5364 完全同一个 .map():只映射顶层 issue,issue.errors 里每个分支的真实拒绝理由(含 #4001 那批 strictObject 的策展处方)被丢弃。
为什么它是独立的一条
#5364 修的是 saveMetaItem 的写路径 422。这一条是读路径:computeMetadataDiagnostics → decorateMetadataItem / decorateMetadataItems 给 getMetaItems() / getMetaItem() 服务出去的每个文档挂 _diagnostics 信封,该文件自己的模块头写明用途是"so Studio (and any other consumer) can render validity badges, inline field errors, and governance dashboards"。
也就是说:#5364 修好之后,作者保存一个有缺陷的 view 能看到字段名了;但打开一个库里已经存着的有缺陷的 view,Studio 的内联字段错误仍然是一条没有字段的 Invalid input。两条路径的判决会不一致。
computeMetadataDiagnostics / decorateMetadataItem / decorateMetadataItems 都是 @objectstack/metadata-protocol 的公开导出(src/index.ts:29-32)。
实测(origin/main @ e900015)
用 #5364 那个同样的 view 输入:
computeMetadataDiagnostics('view', {
name: 'task_list', object: 'task', type: 'list', label: 'Tasks',
columns: [{ field: 'title', summary: { type: 'sum', fieldd: 'amount' } }],
})得到:
{
"valid": false,
"errors": [
{ "path": "", "message": "Invalid input", "code": "invalid_union" }
]
}ViewMetadataSchema 顶层本身是 union(view.zod.ts 的 z.preprocess(stripViewConsoleDecorations, z.union([...]))),所以 view 类型的每一个有缺陷的存量文档都退化成这一条。
这一条的修法应当是最便宜的一次
PR #5596 已经在同一个包、同一个文件的隔壁落了 zodIssuesToMetadataIssues(issues),信封形态就是 { path, message, code } 且 code 透传 zod 原码 —— 与 MetadataValidationResult.errors[] 的形态完全一致。所以这一条大概率是:导出/引入该函数,把上面那个 .map() 换掉,加读路径的回归测试。computeMetadataDiagnostics 先做了 _diagnostics 的 strip(stripDiagnostics),那一段不要动。
判决必须与另外四处一致(丢弃只报根部 KIND 不匹配的分支;报得最少的分支胜出;unrecognized_keys 破平局;并列全出且有上限;嵌套 union 按绝对路径递归),否则同一个错误在保存时和打开时给出两套说法。
同族现状
| # | 消费者 | 文件 | 状态 |
|---|---|---|---|
| 1 | formatZodError |
packages/spec/src/shared/error-map.zod.ts |
✅ #4971(PR #5342) |
| 2 | zodIssuesToFields |
packages/rest/src/rest-server.ts |
✅ #5014(PR #5362) |
| 3 | formatZodErrors |
packages/cli/src/utils/format.ts |
⏳ #5341 待派 |
| 4 | saveMetaItem 的 422 |
packages/metadata-protocol/src/protocol.ts |
⏳ #5364(PR #5596 待评审) |
| 5 | computeMetadataDiagnostics |
packages/metadata-protocol/src/metadata-diagnostics.ts |
⏳ 本单 |
值得记一笔的是,这五个里有四个都是在修上一个的过程中发现的 —— 这类"同一机制多份拷贝"的缺陷靠一次静态扫描数不全。第 5 份修完后,或许值得单独裁决要不要把这套策略收敛成一个共享实现(spec 目前只导出字符串渲染器,三个结构化消费者各抄一份),而不是继续按份数增长。
Activity
os-zhuang commentedon Aug 5, 2026
分诊:入队
pm:queue+domain:engine-core+pm:blocked。packages/metadata-protocol/src/metadata-diagnostics.ts:77的computeMetadataDiagnostics(metadata* 家族)→domain:engine-core。path:"" message:"Invalid input",Studio 无字段可高亮 #5364(修法复用其 PR fix(metadata-protocol): 元数据保存的 422 保留 union 分支处方,Studio 重新拿得到字段名 (#5364) #5596 落的zodIssuesToMetadataIssues,该 PR 尚在评审;先落它,读写两路径才有共同实现与一致判决口径可引。元数据保存的 422 也丢掉 union 分支处方:一个 view 保存失败只回一条path:"" message:"Invalid input",Studio 无字段可高亮 #5364 关闭后解锁回队)。zodIssuesToFields只映射顶层 issue,失败的 union 只剩Invalid input#5014 已修,os validate/os build用的是 CLI 自己的 formatZodErrors,它同样把 union 分支的处方裁掉 —— #4971 修的不是这条路径 #5341 待派(CLI,不同文件),元数据保存的 422 也丢掉 union 分支处方:一个 view 保存失败只回一条path:"" message:"Invalid input",Studio 无字段可高亮 #5364 在飞(写路径),本单是读路径第 5 消费者,无重复。正文提出的「五处策略收敛为共享实现」属后续裁决项,不并入本单。metadata-diagnostics.ts自 fix(spec): OpenAPIcomponents.schemas不再是空的 ——lazySchemaProxy 撞上typeof === 'object',并补上产物自洽门禁 #5459 后无变更,前提成立。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
解锁 + 认领:PM 循环第 3 轮(engine-core 车道)。Blocker #5364 已随 PR #5596 合并关闭,按 unlock sweep 摘
pm:blocked回队并认领。会话:
session_01V7WetGmnfoXNn8cLieKKmx分支:
claude/issue-5598-diagnostics-union-branchesWorktree:
objectstack-issue-5598域:
domain:engine-core文件面:
packages/metadata-protocol/src/metadata-diagnostics.ts、metadata-protocol 测试、.changeset/*.md。⛔ 不碰protocol.ts(#5736 在队列)、不碰packages/spec。修法照 issue:复用同包 #5596 落的zodIssuesToMetadataIssues,判决与另四处一致;stripDiagnostics一段不动。「五处收敛共享实现」的后续裁决项不并入本单。Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
PM 评审(会话 session_01V7WetGmnfoXNn8cLieKKmx):PR #5765 ACCEPT,待 CI 绿后放行。
已读全量 diff。要点:
.map()换成同包 fix(metadata-protocol): 元数据保存的 422 保留 union 分支处方,Studio 重新拿得到字段名 (#5364) #5596 落地的zodIssuesToMetadataIssues,分支选取口径单点定义,读写两路径按构造一致——正是派单要求的形状。⛔ protocol.ts 未动(函数本就模块级导出),stripDiagnostics未动且有「读两遍稳定」用例守着。errors[0],既有消费者读到的还是同一条;4 例对照组(普通字段拒绝/非对象/合法文档/未注册类型)钉住非 union 路径逐字节不变。protocol.save-union-issues.test.ts),本文件只钉「读路径接上了同一份判决」——两份测试不会漂成两份口径。裁决 dev 提请注意的循环 import:本 PR 原样接受。
metadata-diagnostics.ts → protocol.ts的包内循环运行期安全(函数声明提升、无模块级互调)、tsup/DTS 无告警;当下抽中立模块必须动 protocol.ts,会与在飞的 #5264 冲突。此观察作为新论据并入本单正文的后续裁决项(五处策略是否收敛为一个共享实现):若收敛,落点应是中立叶子模块(如metadata-issues.ts),由 protocol.ts 与 metadata-diagnostics.ts 同向依赖,而不是按份数继续增长。该项仍留 triage/维护者裁决,不阻塞本 PR。CI 绿后我会转 ready 并挂 auto-merge。
Generated by Claude Code
fix(metadata-protocol): 读路径 _diagnostics 保留 union 分支的真实拒绝理由 (objectst…