Repository navigation
休眠:invalid_key / invalid_element 把真实 issue 挂在 issue.issues 上,union 家族的三个消费者一个都不下降 #5389
Description
Activity
分诊(cli 车道 PM,
session_016FNvXhtSdnEGEfLEsMmvxh,2026-08-05):持有(留finding)。为什么还留着:休眠形状,正文自证今日不可达(spec 内z.record键 schema 全为z.string()/enum,enum 坏键走unrecognized_keys不产invalid_key)。晋级路径倾向正文方案 (2)——先加「record 键 schema 限 string/enum,放宽前须先做 (1)」的 gate,把不可达变成结构性保证;该 gate 落packages/lint/spec scripts,属 spec-tooling/devx 车道,非本车道。下轮分诊轮复核。
Generated by Claude Code
发现分诊轮:持有。休眠确认成立(spec 内 record 键 schema 全为 string/enum,
invalid_key/invalid_element今天不可产出),重启条件:第一个带约束的 record 键落仓,或有人做 (1) 家族补完 / (2) 键 schema gate —— 届时晋级入队。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
发现分诊轮:持有(维持 08-05 判级)+ 补域
domain:spec(前两轮均未打域标签,本轮补齐)。- 域锚定:两条晋级路的主落点都在 spec —— 路 (1) 的三个消费者中,CLI 侧现已 import spec 的
formatZodIssue,真正要改的是packages/spec/src/shared/error-map.zod.ts一处 +packages/rest/src/rest-server.ts一处;路 (2) 的键 schema gate 虽落packages/lint(devx),但它约束的是packages/spec内的写法。按「主要落地站点」锚定 ⇒domain:spec;若维护者最终选路 (2) 且 gate 独立成单,那一单再单独打domain:devx。 - 为什么还留着(理由未变,本轮未复测):休眠形状 —— spec 内
z.record(...)的键 schema 全是z.string()或 enum,z.string()键永不失败、enum 坏键走顶层unrecognized_keys,故invalid_key/invalid_element今天产不出来,无用户可达面。 - 重启条件(维持):第一个带约束的 record 键落仓(例如
z.record(SnakeCaseIdentifierSchema, X)),或有人做 (1) 家族补完 / (2) 键 schema gate —— 届时晋级入队。 - 查重:同族三条 formatZodError 把 union 分支的拒绝信息压成 "Invalid input" —— #4001 策展的散文在 CLI 路径上到不了作者 #4971 / union 分支里的 unknown-key 处方永远到不了作者:
zodIssuesToFields只映射顶层 issue,失败的 union 只剩Invalid input#5014 /os validate/os build用的是 CLI 自己的 formatZodErrors,它同样把 union 分支的处方裁掉 —— #4971 修的不是这条路径 #5341 均已收官(它们修的是invalid_union的issue.errors),本条是同形状的另外两个 code,入口不同,不重复。
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- 域锚定:两条晋级路的主落点都在 spec —— 路 (1) 的三个消费者中,CLI 侧现已 import spec 的
Findings triage — verdict: HOLD stands (keeps
finding,domain:spec). This round re-measured the dormancy premise rather than restating it, because that premise is the entire basis of the hold and it decays with every spec change.Dormancy re-measured on
origin/main@1363084— still holdsThe claim under test: every
z.record(...)key schema insidepackages/specisz.string()or an enum, so neitherinvalid_keynorinvalid_elementcan be produced today. Enumerated every non-z.record(z.string()occurrence and resolved each key schema to its definition:Site Key schema Resolves to Can emit invalid_key?api/rest-server.zod.ts:241,:397CrudOperationz.enum([...])(:175)No — bad key ⇒ top-level unrecognized_keysdata/driver.zod.ts:181DateGranularityz.enum(['day',…,'year'])(query.zod.ts:122)No — same system/translation.zod.ts:626LocaleSchemaz.string().describe('BCP-47 …')(:12)No — z.string()keys never failsystem/core-services.zod.ts:241CoreServiceNameenum No system/auth-config.zod.ts:189z.string()— No data/object.zod.ts:682(retention.onlyWhen)z.string()— No data/driver/memory.zod.ts:231(initialData, incl. nested)z.string()— No No map/set on the authoring surface either.
invalid_key/invalid_elementremain unproducible ⇒ dormant ⇒ held. The remainingz.record(hits in the grep are prose in docblocks and tests aboutFlowNodeSchema.config(z.record(z.unknown()), deliberately open), not new key schemas.LocaleSchemadeserves the callout because it is the one that looks like it should be constrained — a BCP-47 tag keyingTranslationBundleSchema. It is a barez.string()carrying its constraint only in.describe(). That is exactly the shape that would arm this finding the day someone tightens it, which makes it the most likely trigger of restart condition ① and worth naming here so the next reader does not re-derive the table.Restart conditions (unchanged in substance):
- the first constrained record key lands in spec (
z.record(SnakeCaseIdentifierSchema, X), a tightenedLocaleSchema, or similar) ⇒ the shape is armed on that slot in all three consumers at once, silently ⇒ promote immediately; - someone completes route (1) — descend
issue.issuesalongsideissue.errorsinpackages/spec/src/shared/error-map.zod.tsandpackages/rest/src/rest-server.ts— or adds route (2)'s key-schema gate.
Dedup: the three same-family issues #4971 / #5014 / #5341 are all closed and fixed
invalid_union'sissue.errors; this is the other two codes on the same shape, a different entry, not a duplicate.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- the first constrained record key lands in spec (
os-project-manager commented
on Aug 8, 2026 CollaboratorMore actionsFindings sweep (maintainer-authorized one-off, 2026-08-07 — registered on #6015): promoted to the queue.
invalid_key/invalid_elementare the 4th/5th members of a family already fixed three times (#4971, #5014, #5341) in exactly the three consumers this card names — scoped, precedented, and the DX failure mode (top-level issue with the real error buried) is identical.finding→pm:queue. Triage may split per consumer if preferred.
Generated by Claude Code
- added a commit that references this issue
on Aug 17, 2026 - added a commit that references this issue
on Oct 7, 2026
观察类记录(今天点不着,无用户可见影响)。从 #5341 的正文里抽出来单独存档 —— 那单被 PR 关掉之后,这条注记就埋在一个 closed issue 里了。
事实
invalid_union把分支的真实 issue 挂在issue.errors[]上,这个家族已经修了三次:formatZodError(spec,defineStack抛错走它)packages/spec/src/shared/error-map.zod.tszodIssuesToFields(REST wire)packages/rest/src/rest-server.tsformatZodErrors(CLI 终端)packages/cli/src/utils/format.ts同样的形状还有另外两个 issue code,三个消费者一个都没处理:
invalid_key——z.record(K, V)的键 schema 失败,真实 issue 在issue.issues上(注意是issues,不是 union 的errors);invalid_element—— map / set 的元素 schema 失败,同上。三处消费者读的都只有顶层 issue 与(#5341 之后)
issue.errors,没有任何一处读issue.issues。所以这两种 code 一旦出现,作者/调用方拿到的就是裸的一行,散文全部丢在 payload 里 —— 与 #4971 / #5014 / #5341 是同一个缺陷,只是入口不同。为什么今天点不着
packages/spec里z.record(...)的键 schema 目前全是z.string()或 enum:z.string()键永远不失败;unrecognized_keys(实测过),不产生invalid_key。map/set 在 authoring 面同样没有。所以现在没有任何 authoring 面能产出这两种 code,用户今天遇不到,不构成缺陷。
什么时候会点着
任何人写下第一个带约束的记录键,例如
z.record(SnakeCaseIdentifierSchema, X)—— 从那一刻起,该 slot 上的坏键在终端、wire、defineStack三条路上都会退化成一行没有信息的判决,而三份代码不会有任何一处报警。建议(不急)
两条路,选一条:
issue.issues与issue.errors一并下降(CLI 侧现在是 import spec 的formatZodIssue,所以真正要改的只有 spec 一处 + REST 一处);packages/spec内z.record(...)的键 schema 必须是z.string()或 enum,想放宽时先把 (1) 做掉。(2) 更符合「先把不会写错做成结构性的」这条口径,但 (1) 是真正把家族补完。谁先动谁定。
记录人:#5341 的实现(
domain:cli)。未认领,无pm:queue,交 PM 定级。