feat(spec,lint): 声明 chart segment drill —— ChartDrillDownSchema,挂在真正读它的那一层 (#5022) - #5221
Merged
Merged
Conversation
…(no code yet)
断点位置:测量完成,**尚未写任何 spec 代码**。测量结果与裁决前提冲突,
倾向 needs_decision(见下)。分支上目前是空提交,仅承载证据。
## 已完成的测量(objectui @91757a7 + objectstack @41e605ec70)
### A. ObjectChart 实际读取词汇(反推自 packages/plugin-charts/src/ObjectChart.tsx)
读到的键(:602 起,经 @object-ui/core drill-down.ts):
enabled? boolean isDrillEnabled: 无 config=关;{}=开;enabled:false=关
filter? Record<string,unknown> computeDrillFilter,支持 ${event.*} 插值
title? string resolveDrillTitle,支持 ${event.*} 插值
target? 'drawer'|'dialog' :755 — **只分支 'dialog',其余一律 Sheet**
columns? string[] :762 → accessorKey/header
maxRows? number :761 → pageSize
ObjectChart **不读**:mode / view / report / sort,且 **不实现 target:'navigate'**
(navigate 只在 DrillDownDrawer.tsx:84 实现,ObjectChart 自绘 Sheet/Dialog,不走它)。
objectui 侧已有 TS 接口 DrillDownConfig(packages/types/src/data-display.ts:897),
是 5 个 widget 共享的更宽形状:{enabled,mode,target(含 navigate),filter,title,
view,report,columns,sort,maxRows}。其中 view/sort 无任何渲染器读取。
### B. 载体测量 —— 与裁决前提("渲染器已在读的 drillDown,objectui 零改动,挂 chart config")冲突
1. widget.chartConfig.drillDown → **无人读**。
DashboardRenderer.tsx 全文 0 处 chartConfig;DatasetWidget.tsx:595-605 只取
showLegend 一个键,注释自陈 "the rest of chartConfig stays unforwarded";
:632 构造的 chart schema 里没有 drillDown。
2. widget.options.drillDown → DashboardRenderer.tsx:494/:570/:531 确实读,但**全部在
isObjectProvider(widgetData) 的 legacy inline 分支内**;而 DashboardWidgetSchema
要求 dataset(dashboard.zod.ts:427,非 optional),datasetBound 恒真,
DashboardRenderer.tsx:691/:720 恒走 <DatasetWidget>,componentSchema 被丢弃。
⇒ 对 v17 合法 widget,options.drillDown 永不被读。
3. ADR-0021 正路 DatasetWidget 的 drill 是**从 dataset 结果派生、不可配置**的:
canDrill 由 dimensionFields/drillRanges 决定,DrillDownDrawer 只收
{objectName, filter, title, dataSource} —— 上述词汇一个都不认。
4. 唯一真活的面是 **react tier**:ChartConfigSchema 的键在 react 块上是**扁平 prop**
(react-blocks.ts:226-247 dataProps),所以 <ObjectChart drillDown={…}> 确实生效;
但 drillDown 既不在 spec dataProps/interactions,也不在 objectui registry inputs
(ObjectChart.tsx:822-830),SDUI save gate 会报 unknown-prop(warning,不拦)。
⇒ 结论:挂 chart config 在 **react tier 交付、dashboard 元数据面不交付**;
挂 options 在两面都不交付。任一选择都会造出 #5011 "same key, two fates" 的形状。
## 剩余步骤(若维护者裁定后继续)
1. [待裁决] 定载体:(A) 只挂 react 块契约 /(B) 挂 ChartConfigSchema 并明写
dashboard 面不转发 /(C) 挂 options 并在 objectui DatasetWidget 落地实现(非小改动)。
2. [待裁决] 定形状:chart 诚实子集 {enabled,filter,title,target:'drawer'|'dialog',
columns,maxRows} vs objectui 共享 DrillDownConfig 全集。
3. 写 ChartDrillDownSchema(strictObject,surface/history/guidance),JSDoc 与
ReportSchema.drilldown 双向点名消歧。
4. 逐键验收测试 + 调和 chart.test.ts:464 的批 15 钉子。
5. liveness/dashboard.json 分类 + check:strictness-ledger 台账行。
6. changeset(minor,v17 pre 模式);objectui 侧把 (schema as any).drillDown 换成声明类型。
7. 合 origin/main(#5154/#5162 已入队)后重跑 check:strictness-ledger + check:generated。
## 顺带发现(超范围,待立 issue)
- widget.chartConfig 在 dashboard 面只有 showLegend 落地,其余 11 个已声明键全惰性;
liveness/dashboard.json 的 chartConfig 行写 "chart-config bag forwarded",高估。
- objectui DrillDownConfig.view / .sort 无渲染器读取;ObjectChart 忽略 target:'navigate'。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
…ema, on the tier that reads it (#5022) `drillDown` drove a real capability with no protocol declaration behind it: objectui's ObjectChart read `(schema as any).drillDown` and really did open a filtered record drawer from it, while `grep drillDown packages/spec` found only a comment — the one #3752 wrote prescribing the key as a migration target. That inverted Prime Directive #10: delivered, never declared. Declared now, additively, at the one surface measured to read it. ## The shape is the six keys ObjectChart reads `{ enabled?, filter?, title?, target?: 'drawer' | 'dialog', columns?, maxRows? }`, each reverse-engineered from a read point: `isDrillEnabled` (absent = off, `{}` = on), `computeDrillFilter`, `resolveDrillTitle`, the `'dialog'` branch, the column map, the page size. objectui's own `DrillDownConfig` is wider because it is shared with the table / pivot / metric widgets, and copying that union would have promoted four keys a chart ignores — two of which NO widget reads (objectui#3354) — into protocol-declared capabilities. Each absent key is a `guidance` entry naming where it does apply, not a rename. ## The carrier is the react block, NOT chart config or the widget options bag Both candidates in the issue measured dead on the dashboard metadata path: `DashboardRenderer` never reads `chartConfig`, and `DatasetWidget` forwards exactly one key out of it (`showLegend`); `options.drillDown` is read only in `DashboardRenderer`'s legacy `isObjectProvider` branch, which a spec-legal v17 widget cannot reach, because `dataset` is required and `datasetBound` therefore discards that component schema unrendered. An ADR-0021 dataset-bound widget drills through the semantic layer and honours no drill config at all — which `content/docs/ui/dashboards.mdx` already said. So the declaration rides `react-blocks.ts`'s interaction overlay, deliberately not `ChartConfigSchema`, whose members a dashboard widget's `chartConfig` also parses. A member there would have been authorable, parse-clean and unread. The three places an author reaches for it now answer instead of shrugging: `chartConfig.drillDown` → the react prop; `widget.drillDown`/`widget.drilldown` → dashboard drill is AUTOMATIC, plus both configurable drills named; `report.drillDown` → back to the chart prop. ## Two-way disambiguation with ReportSchema.drilldown Same word, one letter apart, two capabilities. Edit distance actively gets this wrong — the spellings are distance 1, so a bare "did you mean" sends an author to the boolean slot where their config object fails a second time. Both gates name the TYPE difference, not just the spelling. ## Declared = enforced, in the same commit `.strict()` is a property of a parse, and nothing on the react surface called one. `validate-react-page-props.ts` now PARSES the schema against a static `drillDown={{…}}` literal (`react-chart-drilldown-invalid`) instead of re-deriving the rules the way `CHART_FUNCTIONS` does for `aggregate` beside it — the fix the strictness ledger's `chart.zod.ts` row has been waiting on, shown on one key. A value from React state is skipped: unresolvable is not wrong (ADR-0072 D1). ## Instruments - 批 15's pin (`chart.test.ts`) is RECONCILED, not relaxed: the ChartInteraction prescription still must not say a bare `drillDown`, now because that schema is reached from both tiers and cannot tell which, so the advice would be a dead end for half its readers. - 20 new assertions: per-key acceptance, `{}` enables, absent stays optional, unknown key rejected with the surface named, each non-chart key rejected with its reason, `target:'navigate'` prescribed (and a plain wrong value NOT given that text), the carrier verdict pinned in both directions, and the published react type string derived-checked against the schema shape. - 10 lint-gate tests; ledger row and site counts updated (`ui/` 198 → 199). Filed, not fixed here: #5175 (`chartConfig` delivers 1 of 12 keys on the dashboard path; the liveness row's evidence overstates it) and objectui#3354 (`DrillDownConfig.view`/`.sort` read by nothing; `navigate` silently degrades). Fixes #5022 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…ger surgery) #5220 (#5107) split the strictness ledger into numbers and prose: every count moved to the generated `…ledger.counts.md` (`gen:strictness-ledger`, os-regen managed), and the hand-written file lost its numeric columns and header figures. This branch had hand-edited exactly those numbers, so both conflicts were mine. Resolution, per the rule the split exists to enforce — never hand-carry a count: * Both conflict hunks taken WHOLE from origin/main (the de-numbered `ui/` triage table header and the `#### ui/ — remaining strip sites` table). * Every number I had touched is DISCARDED and recomputed by the generator: `ui/` 198 → 199, strict 117 → 118, total 482 → 483, `chart.zod.ts` 7 → 8, and the strip-of table's `2 | 8`. All of that is now generator output in counts.md; my diff against main under `docs/audits/` contains no hand-written digit. * The two PROSE edits are re-applied onto main's rows, in the new format: the triage `Class` cell reads `**mixed — 6 authorable, 2 no gate**`, and the remaining-strip row keeps main's `**no gate**` verdict (no split declaration needed — that Class is not `mixed`/`split`), extended with the note that the eighth site arrived with its publish gate already wired. `strictness-ledger.test.ts` merged cleanly: main only APPENDED the #5072 chain- posture cases, and my `chart.zod.ts` 7 → 8 assertion is a unit test of the AST counter, not of the ledger document, so #5107 does not subsume it and it stays where it is. Nothing to re-hang. Verified after the merge: spec 306 files / 7856 tests, lint 57 / 1190, both typecheck clean, `check:generated` 9/9 (including the new counts gate), and `check:strictness-ledger` green on all three of its sections. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
xuyushun441-sys
marked this pull request as ready for review
August 4, 2026 11:22
xuyushun441-sys
enabled auto-merge
August 4, 2026 11:22
This was referenced Aug 4, 2026
xuyushun441-sys
pushed a commit
that referenced
this pull request
Aug 4, 2026
#5221) The merge with origin/main brought in #5221 (`chart.zod.ts` gains `ChartDrillDownSchema`). Git text-merged the GENERATED counts file cleanly and WRONGLY — the ledger's own documented failure mode: each side's row deltas do not overlap, so the subtotals merge clean while naming a tree neither branch ever had (483/199, missing #5221's site). Regenerated via `gen:strictness-ledger` from the merged tree, which is the only state in which the numbers are right: 484 sites / ui 200, strict 244 / ui 120. Both hand-written prose rows survive untouched — `view.zod.ts` (this PR) and `chart.zod.ts` (#5221) are different rows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
This was referenced Aug 4, 2026
Merged
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.
Fixes #5022
背景:delivered but never declared
drillDown一直在驱动一个真实能力 —— 点<ObjectChart>的柱子/扇区,objectui 打开按点击类目过滤的记录抽屉。可是协议里从来没有声明过它:objectui 读的是(schema as any).drillDown,grep drillDown packages/spec只能搜到一条注释 —— 正是 #3752 写下的、把这个键当作迁移目标推荐给作者的那句话。批 15 已经就地修正了那句处方,底下的缺口留到这单。这是 Prime Directive #10 反过来的形态:不是「声明了没交付」,而是「交付了从没声明」。
本 PR 声明它,additive,原先能 parse 的一律照旧。
形状:ObjectChart 实测读的六个键
{ enabled?, filter?, title?, target?: 'drawer' | 'dialog', columns?, maxRows? }每一个都反推自一个读取点:
isDrillEnabled(缺省关,{}开)、computeDrillFilter、resolveDrillTitle、'dialog'分支、列映射、页大小。objectui 自己的
DrillDownConfig更宽,因为它是 table / pivot / metric 几个 widget 共用的。没有照抄那个并集:chart 一个额外键都不读,其中view/sort更是任何 widget 都不读(objectui#3354)。照抄会把四个死键提升成带协议权威的能力。每个缺席的键都有一条guidance说明它属于哪儿,而不是给一个 rename 建议。落点:react 块契约,不是 chart config、也不是 widget 的 options 袋
issue 里提的两个候选,实测在 dashboard 元数据面都是死的:
widget.chartConfig.drillDown——DashboardRenderer全文不读chartConfig;DatasetWidget只从里面转发一个键(showLegend),注释自陈 "the rest ofchartConfigstays unforwarded"。widget.options.drillDown——DashboardRenderer确实读,但只在 legacy 的isObjectProvider分支里;而DashboardWidgetSchema.dataset是必填,datasetBound恒真,那份 component schema 恒被丢弃、从不渲染。ADR-0021 dataset-bound widget 的 drill 是从语义层派生的,不认任何 drill 配置 —— 这一点
content/docs/ui/dashboards.mdx早就写着(there is no per-widget drill configuration in the dataset form)。所以声明挂在
react-blocks.ts的 interaction 覆盖层,刻意不进ChartConfigSchema:后者的成员同时是 dashboard widgetchartConfig解析的东西,放进去就会造出一个「可写、parse 通过、没人读」的键。三个作者可能写错的地方,现在都会回答而不是耸肩:
widget.chartConfig.drillDownwidget.drillDown/widget.drilldownreport.drillDown与
ReportSchema.drilldown的双向消歧同一个词,差一个字母,两种能力:
drillDowndrilldown<ObjectChart>propReportSchema键(ADR-0021 D2,默认开)编辑距离在这儿是帮倒忙的:两个拼写距离为 1,裸的 "did you mean" 会把在 report 上写
drillDown的作者送去drilldown,然后他的配置对象在布尔槽位上被第二次拒绝。两边的 guidance 都点名类型差异,不只是拼写。declared = enforced,同一个 PR 里
.strict()是 parse 的属性,而 react 面上此前没有任何东西调用过 parse。packages/lint/src/validate-react-page-props.ts现在直接 PARSE 这个 schema(react-chart-drilldown-invalid),而不是像旁边aggregate的CHART_FUNCTIONS那样把规则再抄一遍 —— 这正是 strictness 台账chart.zod.ts那一行一直在等的修法,先在一个键上做出来。来自 React state、静态不可解的值照旧跳过:unresolvable ≠ wrong(ADR-0072 D1)。
仪器
ChartInteractionSchema的处方仍然不许出现裸的drillDown—— 理由换了:这个 schema 从两个 tier 都能到达且分辨不出自己在哪个上,裸处方对一半读者是死路。断言不动,justification 换了,并写进注释。{}即开、缺席保持 optional、未知键带 surface 名被拒、每个非 chart 键带理由被拒(不是 rename)、target:'navigate'有专属处方(而一个普通的错值不会被喷这段文字)、载体判定双向钉死、发布出去的 react 类型串由 schema shape 反查校验。ui/198 → 199)。顺带发现(已立单,未在此修)
DashboardWidget.chartConfig在 dashboard 元数据面只有showLegend落地 —— 12 个已声明键里 11 个惰性,liveness 台账那一行高估了证据 #5175 ——widget.chartConfig在 dashboard 面只有showLegend落地,12 个已声明键里 11 个惰性;liveness/dashboard.json那一行的证据措辞("chart-config bag forwarded")高估了。DrillDownConfig.view/.sort无任何渲染器读取;target: 'navigate'在 ObjectChart 上静默降级。配套 PR
objectui 半边:objectstack-ai/objectui#3358 —— registry
inputs加上drillDown(让 SDUI save gate 从unknown-prop变成正式契约),并把(schema as any)换成类型化读取。验证
已在
git merge origin/main(未 rebase)之后重跑上述全部。🤖 Generated with Claude Code
https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9