Skip to content

feat(lint): react 页发布闸门改为 PARSE ChartAggregateSchema,不再手写重推它的规则 (#5020) - #5608

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5020-chart-aggregate-gate
Aug 5, 2026
Merged

feat(lint): react 页发布闸门改为 PARSE ChartAggregateSchema,不再手写重推它的规则 (#5020)#5608
os-zhuang merged 1 commit into
mainfrom
claude/issue-5020-chart-aggregate-gate

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

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 手推 refinement ✅ 仍在,且 :288 的 ChartDrillDownSchema.safeParse 先例仍在
两个 schema 均 STRIP 姿态 ✅ 实测:safeParse({function:'count',groupBy:'status',groupby:'status'}) success 且丢 groupby;嵌套 dateGranularty
zod 4 union 分支消息塌缩 ✅ 实测:groupBy 的失败以一条 invalid_union、message 为字面量 Invalid input 到达,具名消息只在 issue.errors
渲染器兑现 groupBy 缺席 ✅ objectui ObjectChart.tsx:640:schema.aggregate?.groupBy || schema.xAxisKey;spec 自己的 chartAggregateCategoryKey TSDoc 也写明「未分组聚合返回单行、没有分类列」
groupBy 被声明为必填 ✅ schema 与 react-blocks.ts:245 的已发布类型串都必填 —— 三方不一致确实存在,这正是 R2 降 warning 的依据
仓内 authored aggregate 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:

aggregate.groupBy: Invalid input (received {"dateGranularity":"day"}) — no accepted form matched:
  (1) Invalid input: expected string, received object
  (2) field — Invalid input: expected string, received undefined

这条解包在 #5583 之后更要紧:strict 分支的 unrecognized_keys 会以完全相同的方式塌缩,具名 surface + 改名建议要靠它到达作者。

另外补了一件 zod 不做的事:回显作者写下的值Invalid option: expected one of … 不带 received 值,而被删掉的手写检查是带的;现在按 issue path 从输入里取回(通用逻辑,不在消费端复述任何契约知识)。

R2 —— 严格度分级

授权内容 之前 之后
function 静默通过 error aggregate.function: Invalid option: expected one of …(nothing is set there)
function 不在枚举内 error(本地词表) error(枚举自带词表)+ (received "median")
field 非字符串 静默(或被误报成缺 field) error aggregate.field: Invalid input: expected string, received number
sum/avg/min/max 缺 field error(手写措辞) error,schema refinement 原话
aggregate 不是对象 静默 return error aggregate must be a configuration object, not string.
groupBy 存在但类型错 静默通过 error(解包 union)
groupBy 缺席 静默通过 warning,不阻断

groupBy 缺席是唯一降级项:它是唯一有活渲染路径兑现的形状,按 #5068 条款「渲染器兑现的键先声明后设闸」不设 error。finding 自带解释并点名 #5583(产品问题「无分组单值图是否受支持」在那边裁)。分级是按 issue 分的,不是按整个 aggregate:{ function: 'median' } 同时产出一条 error(function)+ 一条 warning(groupBy)。

⚠️ 本 PR 没有关闭什么(过渡期缺口已钉住)

两个 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 翻

  1. packages/spec/src/ui/chart.zod.ts —— 仅注释散文(文件头 + schema 旁那段大块注释),schema 本体零改动;批 15 那句「DO NOT convert before that is decided」标注为已满足并指向 spec: ChartAggregateSchemaChartGroupBySchema object 分支转 strictObject —— #5020 parse 接通后的收紧半边(#4001 后续批) #5583,而不是被推翻。
  2. packages/spec/src/ui/chart.test.ts —— 仅 pin 注释改指 spec: ChartAggregateSchemaChartGroupBySchema object 分支转 strictObject —— #5020 parse 接通后的收紧半边(#4001 后续批) #5583;两条「still STRIPS — deliberate」断言本体不动,姿态未变必须保持绿(75 tests 全绿),常驻 BFS 断言不动。
  3. docs/audits/2026-07-unknown-key-strictness-ledger.md —— :636 triage 行与 :849 strip 行 no gateauthorable,并在汇总段落把 no gate 桶里的 chart 一对划掉。⚠️ strip 行的 Class 单元格是机读的,只能放一个 verdict:~~no gate~~ **authorable** 会被 check:strictness-ledger 判为「not a verdict」(我先踩了一次),所以历史写在证据列。.counts.md 按 os-regen 规程用 gen:strictness-ledger 整体重生成,未手改数字:no gate 31 → 29、authorable 11 → 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.ts 135 passed(新增约 49 条,含 37 行表驱动词表逐绿:5 个函数 × 裸串 arm + 结构 arm + 5 个 granularity,外加 alias 与 count 带 field)
  • pnpm --filter @objectstack/lint typecheck —— 绿(过程中吃了一记 TS7006:Array.isArray() 会把 ReadonlyArray 窄化成 any[],已改成可选属性判空,注释里记下了)
  • pnpm --filter @objectstack/specsrc/ui/chart.test.ts —— 75 passed
  • pnpm --filter @objectstack/spec check:generated —— 10/10 绿(含 check:strictness-ledgercheck:docscheck:authorable-surface)
  • node scripts/check-nul-bytes.mjs 绿;另对改动文件跑了越过闸门盲区的自查 grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]',干净
  • eslint 干净
  • 反向验证(方向先判后跑):预判「把手写限枝放回去 → 新 pin 全红」。实跑 11 条红,与预判一致 —— 10 条新断言 + 1 条既有断言(flags a non-count function with nothing to aggregate,因为它现在断言 schema 原话,属预期的 FROM/TO 后果)。两条 STRIP 容忍断言的容忍那半在新旧代码下都绿(两版都不查未知键),这一点如实说明,不假装是本 PR 的成果;判别性由上面那半提供。

给 review 的一条注意

在飞的旧 changeset .changeset/objectchart-aggregate-result-columns.mdreact-chart-aggregate-invalid 描述成「(error)」,而本 PR 之后它多了一个 warning 分支。没有去改别人那份 pending changeset(冲突磁铁、且不在本单范围);本 PR 自己的 changeset 已把分级写清楚。


Generated by Claude Code

…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
@vercel

vercel Bot commented Aug 5, 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 5, 2026 8:22pm

Request Review

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/lint, packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/lint, @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 20:24

Copy link
Copy Markdown
Contributor Author

CI 归因(一次性):ESLint job 红 = main 自带的 check:engine-double-contract 失败(#5604,#5584 引入,签名逐字一致:action-execution-calldata-not-found.test.ts:69,102),非本 PR;该 job 不在必需检查内,不阻塞合并。#5604 修复落地后红自然消失。本 PR 自身门禁全绿(lint 1335 tests / spec 75 / check:generated 10/10)。


Generated by Claude Code

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 protocol:ui size/l tests tooling

Projects

None yet

2 participants