feat(lint): page-component field bindings + non-dashboard chart bindings (#3583) - #3684
Merged
Merged
Conversation
…ngs (#3583) Phase 2 of the #3583 assessment: the two remaining reference classes the HotCRM audit found, both wired into validate/lint/compile together. validate-page-field-bindings — `PageComponent.properties` is an untyped bag, so a highlights strip, KPI card, or details section can name a field the bound object lacks and the component silently skips it. Object binding follows dataSource.object -> properties.object -> page.object, so a multi-object page is judged per element instead of against one page-wide guess; `record:related_list` resolves columns/sort/filter against the RELATED object and its add-picker against the picker's own. Advisory, matching FORM_FIELD_UNKNOWN. The descriptor table is hand-written on purpose: a Zod schema cannot say which `z.string()` prop is a field name, and `PageComponent.type` accepts arbitrary strings, so unknown types are skipped silently. It also covers shapes the props schemas do not describe but real pages author anyway (record:details `sections[].fields` and `hideFields`, the picker's `labelField`) — linting the schema shape alone would find nothing on the actual corpus. validate-chart-bindings — extends ADR-0021 axis checking past dashboards to report charts (report.chart + blocks[].chart), list-view charts (three container paths), and dataset-bound page chart components. An axis naming a raw field instead of a declared measure is an error (post-cutover rows are keyed by measure NAME, so the series returns empty); a declared but unselected measure is a warning. Reports needed their own handling: ReportChartSchema narrows xAxis/yAxis to bare STRINGS, which the dashboard rule's Array.isArray guard skips silently. The react <ObjectChart> block is object-bound rather than dataset-bound and nothing in the repo defines what its aggregate names the result column, so it is left out rather than guessed at (ADR-0078: verify, then enforce). Fixes a defect in the rule shipped by #3657: its page walk read a top-level `page.components` array that PageSchema does not have — components live under regions[].components[] and slots, and sub-trees nest inside the untyped properties bag (children, items[].children, body, footer), not a `children` key on the component (PageComponentSchema is strict). The quick-actions check was visiting nothing on a parsed stack, and its tests encoded the invented shape so they passed. Traversal is now one shared, tested module; on the showcase app it reaches 194 components where the old shape found 46. Source-authored pages (html/react/jsx) are skipped — their regions hold a derived cache the source wins over. Verified zero false positives on app-crm, app-showcase and app-todo with the fuller traversal, after confirming the walk actually visits their 194 components, 1 report chart and 2 list charts (a silently-empty walk would pass vacuously). Suites: lint 435, cli 637 passing; eslint, nul-bytes and api-surface clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GBks5G7AkwrvgF2kL5rfkT
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 19 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 27, 2026 14:41
This was referenced Jul 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#3583 方案的 Phase 2:剩下的两类引用(页面字段绑定、仪表盘之外的图表轴),同 PR 接入
validate/lint/compile。先说一个必须修的缺陷
上个 PR(#3657)里的 quick-actions 检查走的是
page.components—— PageSchema 根本没有这个键。组件实际挂在regions[].components[]和slots下,子树嵌在无类型的properties包里(children/items[].children/body/footer),而不是组件上的children(PageComponentSchema是.strict(),不允许该键)。也就是说那段检查在解析后的 stack 上一个组件都没访问到,而它的测试恰好也是照着这个臆想的形状写的,所以全绿。现在遍历抽成一个共享且有测试的模块;在 showcase 上它访问到 194 个组件,旧形状只有 46 个。
validate-page-field-bindingsPageComponent.properties是无类型包,所以 highlights 条、KPI 卡、details 分区都能写一个对象上不存在的字段,组件静默跳过、页面少渲染一项。dataSource.object→properties.object→page.object,多对象页面逐元素判定,而不是拿一个页面级猜测硬套。record:related_list的columns/sort/filter解析到关联对象(objectName),add-picker 解析到 picker 自己的对象。FORM_FIELD_UNKNOWN一致 —— 页面消费方是降级而非崩溃。描述表是手写的,这是刻意的:Zod schema 说不出哪个
z.string()是字段名(statusField和agentId一样都是字符串),而且PageComponent.type接受任意字符串,所以未知类型一律静默跳过。表里还覆盖了 props schema 没描述、但真实页面确实这么写的形状(record:details的sections[].fields和hideFields、picker 的labelField)—— 只按 schema 形状写规则,在真实语料上会一条都查不出来。validate-chart-bindings把 ADR-0021 的轴校验从仪表盘扩到:报表图表(
report.chart+blocks[].chart)、列表视图图表(三条容器路径)、以及数据集绑定的页面图表组件。values选中的 measure → warning(查询根本不返回它)。ReportChartSchema把xAxis/yAxis收窄成裸字符串,仪表盘规则的Array.isArray(yAxis)守卫会静默跳过它 —— 直接把老规则指向报表会一条都查不出来。故意不做:react
<ObjectChart>是对象绑定而非数据集绑定,而仓库里没有任何地方定义它的 aggregate 把结果列叫什么,所以「轴该解析成什么」没有可辩护的答案。猜一个就是在制造误报。留作后续(ADR-0078:先验证再强制)。验证
关联:#3583 · #3640(方案)· #3657(Phase 1)· ADR-0021 / ADR-0072 / ADR-0078
Generated by Claude Code