Uh oh!
There was an error while loading. Please reload this page.
feat(lint): react 页发布闸门改为 PARSE ChartAggregateSchema,不再手写重推它的规则 (#5020) - #5608
Conversation
…5020) `<ObjectChart aggregate={{…}}>` was judged by a gate that RE-DERIVED its schema: a local `CHART_FUNCTIONS` copy of the function vocabulary and a hand-written twin of the count/field refinement. Two implementations of one contract, free to drift — and since unknown-key handling is a property of a PARSE rather than of a list of `if`s, a gate with no unknown-key check at all. `validate-react-page-props` now calls `ChartAggregateSchema.safeParse()` on a statically resolvable literal, exactly as #5022 did for `ChartDrillDownSchema` one prop over, and both hand-derived copies are deleted. Newly reported, all previously silent: a missing `function`, a non-string `field`, a present-but- wrong `groupBy`, a non-object `aggregate`. Two messages are re-worded because they now come from the schema (FROM/TO in the changeset). zod 4 collapses a union's arm failures into one `invalid_union` whose message is the bare "Invalid input", so the arms are unpacked into the finding — otherwise `groupBy` rejections would say nothing an author can act on, and the same collapse will hide #5583's strict `unrecognized_keys`. An absent `groupBy` reports at `warning`, alone among the graded violations: the schema and the published react-blocks type declare it required while objectui's `ObjectChart` honours its absence (`|| schema.xAxisKey`) and `chartAggregateCategoryKey` documents the ungrouped single-row result. Gating it would enforce a declaration the platform does not itself keep; #5583 decides which side moves. The unknown-key hole is NOT closed by this commit and is pinned open on purpose: both schemas are still STRIP, so the parse drops `groupby` / `dateGranularty` instead of reporting them. Wiring the parse is the precondition, not the closing (#5583). Records updated in the three places the strictness ledger requires: `chart.zod.ts` header prose (schema body untouched), `chart.test.ts` pin comments (assertions unchanged — the posture did not move), and the ledger's two `chart.zod.ts` rows `no gate` → `authorable` with the counts artifact regenerated (`no gate` 31 → 29, `authorable` 11 → 13). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 2 package(s): 109 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
commented
Aug 5, 2026
CI 归因(一次性):ESLint job 红 = main 自带的 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#5020
按 issue 上 2026-08-05 的 PM 裁定(R1/R2/R3)执行的两步走第一步:只做
packages/lint侧接 parse,spec schema 本体零改动(收紧归 #5583)。前提复核(对 origin/main,开工前)
validate-react-page-props.ts:301手写CHART_FUNCTIONS、:356/:363 手推 refinementChartDrillDownSchema.safeParse先例仍在safeParse({function:'count',groupBy:'status',groupby:'status'})success 且丢groupby;嵌套dateGranularty同groupBy的失败以一条invalid_union、message 为字面量Invalid input到达,具名消息只在issue.errors里groupBy缺席ObjectChart.tsx:640:schema.aggregate?.groupBy || schema.xAxisKey;spec 自己的chartAggregateCategoryKeyTSDoc 也写明「未分组聚合返回单行、没有分类列」groupBy被声明为必填react-blocks.ts:245的已发布类型串都必填 —— 三方不一致确实存在,这正是 R2 降 warning 的依据examples/app-showcase/src/ui/pages/renewals-pipeline.page.ts:138完全合法;技能语料 / 文档片段里的 aggregate 也都带合法groupBy,无一处被新闸门打断裁定与实码无冲突,范围未收缩。
R1 —— 接 parse,词表与 refinement 收敛回 schema
checkChartAggregate()照checkChartDrillDown的形状调ChartAggregateSchema.safeParse();CHART_FUNCTIONS与手推的 count/field refinement 删除。NOT_STATIC短路语义不变(ADR-0072 D1)。解包
invalid_union:zod 4 把 union 的分支失败塌成一条顶层 issue、message 只剩Invalid input,直接透传等于没说话。现在把各 arm 的具名消息拼进同一条 finding:这条解包在 #5583 之后更要紧:strict 分支的
unrecognized_keys会以完全相同的方式塌缩,具名 surface + 改名建议要靠它到达作者。另外补了一件 zod 不做的事:回显作者写下的值。
Invalid option: expected one of …不带 received 值,而被删掉的手写检查是带的;现在按 issue path 从输入里取回(通用逻辑,不在消费端复述任何契约知识)。R2 —— 严格度分级
functionaggregate.function: Invalid option: expected one of …(nothing is set there)function不在枚举内(received "median")field非字符串aggregate.field: Invalid input: expected string, received numberfieldaggregate不是对象aggregate must be a configuration object, not string.groupBy存在但类型错groupBy缺席groupBy缺席是唯一降级项:它是唯一有活渲染路径兑现的形状,按 #5068 条款「渲染器兑现的键先声明后设闸」不设 error。finding 自带解释并点名 #5583(产品问题「无分组单值图是否受支持」在那边裁)。分级是按 issue 分的,不是按整个 aggregate:{ function: 'median' }同时产出一条 error(function)+ 一条 warning(groupBy)。两个 schema 仍是 STRIP,所以这条 parse 依旧把未知键静默丢掉 ——
groupby/dateGranularty今天照旧让图退化成单点而 build 全绿。接 parse 是收紧的前置条件,不是收紧本身(.strict()是 parse 的属性,在此之前根本没有 parse 可收紧)。validate-react-page-props.test.ts因此显式钉住今日容忍度并点名 #5583。两条 pin 都做了判别性加固,不靠「什么都没产出」蒙绿(#5046 的坑):同一个 fixture 里在未知键旁边种一个只有 parse 才报得出的违规,断言「恰好 1 条 finding、来自 schema 的措辞、且不含Unrecognized key」。反向验证证明这半边是判别性的 —— 把旧规则放回去,pin 立刻红,报的正是手写措辞aggregate.function "kount" is not an aggregation this chart can run。R3 —— 三处记录随本 PR 翻
packages/spec/src/ui/chart.zod.ts—— 仅注释散文(文件头 + schema 旁那段大块注释),schema 本体零改动;批 15 那句「DO NOT convert before that is decided」标注为已满足并指向 spec:ChartAggregateSchema与ChartGroupBySchemaobject 分支转 strictObject —— #5020 parse 接通后的收紧半边(#4001 后续批) #5583,而不是被推翻。packages/spec/src/ui/chart.test.ts—— 仅 pin 注释改指 spec:ChartAggregateSchema与ChartGroupBySchemaobject 分支转 strictObject —— #5020 parse 接通后的收紧半边(#4001 后续批) #5583;两条「still STRIPS — deliberate」断言本体不动,姿态未变必须保持绿(75 tests 全绿),常驻 BFS 断言不动。docs/audits/2026-07-unknown-key-strictness-ledger.md—— :636 triage 行与 :849 strip 行no gate→authorable,并在汇总段落把no gate桶里的 chart 一对划掉。Class单元格是机读的,只能放一个 verdict:~~no gate~~ **authorable**会被check:strictness-ledger判为「not a verdict」(我先踩了一次),所以历史写在证据列。.counts.md按 os-regen 规程用gen:strictness-ledger整体重生成,未手改数字:no gate31 → 29、authorable11 → 13,恰好这 2 个站点。触碰面声明(spec 车道地带)
chart.zod.ts/chart.test.ts/ 台账三个文件均属 spec 车道散文面。push 前git log --oneline origin/main -- 这些文件复核:最近一次改动是 e900015(#5435 的 drillDown target,已在我的 base 里),当日无其它人在改;git ls-remote --heads origin也无 chart/ledger/5583 相关在飞分支。验证
pnpm --filter @objectstack/lint test—— 59 files / 1335 passed | 4 skipped;单文件validate-react-page-props.test.ts135 passed(新增约 49 条,含 37 行表驱动词表逐绿:5 个函数 × 裸串 arm + 结构 arm + 5 个 granularity,外加 alias 与 count 带 field)pnpm --filter @objectstack/lint typecheck—— 绿(过程中吃了一记 TS7006:Array.isArray()会把ReadonlyArray窄化成any[],已改成可选属性判空,注释里记下了)pnpm --filter @objectstack/spec的src/ui/chart.test.ts—— 75 passedpnpm --filter @objectstack/spec check:generated—— 10/10 绿(含check:strictness-ledger、check:docs、check:authorable-surface)node scripts/check-nul-bytes.mjs绿;另对改动文件跑了越过闸门盲区的自查grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]',干净flags a non-count function with nothing to aggregate,因为它现在断言 schema 原话,属预期的 FROM/TO 后果)。两条 STRIP 容忍断言的容忍那半在新旧代码下都绿(两版都不查未知键),这一点如实说明,不假装是本 PR 的成果;判别性由上面那半提供。给 review 的一条注意
在飞的旧 changeset
.changeset/objectchart-aggregate-result-columns.md把react-chart-aggregate-invalid描述成「(error)」,而本 PR 之后它多了一个 warning 分支。没有去改别人那份 pending changeset(冲突磁铁、且不在本单范围);本 PR 自己的 changeset 已把分级写清楚。Generated by Claude Code