Skip to content

ChartAggregateSchema 有活的承载键却没有任何 parse:react 页发布闸门手写重推了它的规则,未知键从不检查(#4001 批 15 的 no gate 首例) #5020

Description

@xuyushun441-sys

从 #4001 批 15 的门测量中分离出来,未指派。批 15 已按测量把这两个站点留开并三处留痕,本 issue 是它指向的那件事。

测量结果(2026-08-03/04,均带对照)

packages/spec/src/ui/chart.zod.ts 的 7 个 object 站点里,5 个从 dashboard / report 两个 metadata-type root 可达(DashboardWidget.chartConfig、ReportChartSchema),已在批 15 收紧。剩下 2 个 —— ChartAggregateSchema 和 ChartGroupBySchema 的 object 分支 —— 情况不同,而且不是「死槽位」:

测量 结果
承载键 ✅ 活的。aggregate 是 react 层 < ObjectChart objectName aggregate={…} > 的真实可授权 prop(ADR-0081),发布在生成的 react-blocks 契约里,objectui 的 ObjectChart 读 schema.aggregate 去跑查询
从 24 个 metadata-type root + ObjectStackSchema 做 BFS ❌ UNREACHABLE(同一次运行里,上述 5 个是 root-graph,批 13 的 no-door 形状是 UNREACHABLE —— 双向对照都成立)
三个仓库里的 .parse() / .safeParse() ❌ 除本文件单测外没有

问题

唯一实际审查作者所写 aggregate 的东西是 react 页发布 lint(packages/lint/src/validate-react-page-props.ts),它手写重推了这个 schema 的规则:CHART_FUNCTIONS 复制了函数词表,fn !== 'count' && !field 复制了那条 refinement,结果列命名另算一份。它从不检查未知键。

react-blocks.ts 里这个 prop 是一条手写的类型字符串;旁边的 Zod schema 不是契约的生成来源。

所以今天:

< ObjectChart objectName="invoice" aggregate={{ function: 'sum', field: 'total', groupby: 'status' }} / >
//                                                                              ^^^^^^^ 静默丢弃

groupby(而非 groupBy)被剥掉 → 聚合退化成无分组、单行返回 → 图表画出坐标轴和一个点,build/validate 全绿。dateGranularty 同理:日期不分桶,趋势线变成一根。这正是 #4001 的原始失效模式,只是发生在 react 层。

为什么批 15 没有顺手收紧

.strict() 是 parse 的属性,这里没有 parse。收紧只会花掉一次 v17 破坏性变更让文件看起来完成,并留下账本自己警告过的东西 —— 「一个被精确校验的死槽位是更有说服力的谎言」(#4583)。而且这里比批 13 的情形更需要小心:这套词汇是活的,所以正确动作不是 ADR-0049 退役,而是把闸门接上。

批 15 因此在账本的分类规则表里加了第四类 no gate,与批 13 的 no door 并列 —— 两者都「没有 parse」,但处方相反:

承载键 parse 正确的下一步
no door(批 13) 无 无 ADR-0049 enforce-or-remove
no gate(批 15) 活的 无 接上 parse;退役会打断一个能用的功能

建议(contract-first)

让 validate-react-page-props.ts 用 ChartAggregateSchema.safeParse() 判定 aggregate,而不是重推它的规则:

  • 未知键立刻有了闸门,且错误信息可以走本战役的 strictObject 路子(点名 + 改名建议);
  • CHART_FUNCTIONS、count/field refinement 两份重复实现收敛成一份 —— 目前它们已经能各自漂移;
  • 之后这两个站点就是普通的 authorable,收紧是一行改动。

需要先确认的一点:react 页 prop 是静态字面量抽取的结果,NOT_STATIC 的值不能判。所以是「能静态判定时才 parse」,与现有 if (aggregate === undefined || aggregate === NOT_STATIC) return; 的短路保持一致。

参考

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions