Skip to content

drillDown 是 objectui 读的活能力,却不是 spec 声明的任何键 —— #3752 的迁移处方曾指向它 #5022

Closed

Description

@xuyushun441-sys

从 #4001 批 15 中分离出来,未指派。批 15 已就地修正了错误的处方文本(否则会把它提升成一条带平台权威的报错),但底下的缺口是独立的一件事。

发现

packages/spec/src/ui/chart.zod.ts 里 #3752 写下的迁移说明是:

clickAction — … Migration: drillDown, or handle it in React.

批 15 要把这句话提升进 ChartInteractionSchema 的 strict 拒绝信息时,先按惯例核了处方指向的键是否存在。不存在:

  • grep -rn "drillDown" packages/spec/src/ui/*.zod.ts → 只有这段注释本身;
  • DashboardWidgetSchema 没有任何 drill 相关键;
  • ReportSchema 有的是 drilldown(全小写,ADR-0021 D2,布尔,默认 true);
  • objectui 侧则是 packages/plugin-charts/src/ObjectChart.tsx:602 的 const drillDown = (schema as any).drillDown; —— 一个未类型化的读取,后面真的驱动了 drill drawer、computeDrillFilter、drillDown.target / .maxRows / .columns。

即:渲染器实现了一套 drill 配置,协议里没有它的声明,而 spec 的注释在教作者写它。

为什么这是个问题,而不只是注释错字

如果批 15 照抄那句话,一个把 clickAction 改对的作者会被告知写 drillDown,然后被同一个刚加上的闸门再拒一次 —— 账本记过两次的 finding 7(「本战役自己的修复,给人指向了它本要消灭的失效模式」)的第三次。批 15 因此把处方改成只点名确实存在的东西(onSegmentClick / ReportSchema.drilldown / dashboard widget 的 options 袋),并留了一条测试钉死「永不出现 drillDown」。

但那只是让错误信息不再说谎。真实缺口仍在:

  1. drillDown 走的是 options(DashboardWidgetOptionsSchema,故意 passthrough)还是别处?今天没有声明,所以两种写法都「能跑」,取决于渲染器拿到的扁平化 schema 长什么样 —— 这是 Prime Directive chore: version packages #10 的 declared ≠ delivered,方向相反的一种:delivered 但 never declared。
  2. ReportSchema.drilldown 与 objectui 的 drillDown 拼写不同、语义不同(前者是布尔开关,后者是 { target, maxRows, columns, mode } 配置对象),名字却几乎一样。这正是本战役反复付学费的那类近似键。

建议

按 contract-first 择一:

  • 声明它 —— 给 chart drill 配置一个 Zod 形状({ enabled, mode, target, maxRows, columns },从 ObjectChart.tsx 的实际读取点反推),挂在 widget options 或 chart config 上,再让渲染器读声明后的键;顺便决定与 ReportSchema.drilldown 的关系(是否统一拼写)。
  • 或明确它是渲染器私有 —— 那就在 DashboardWidgetOptionsSchema 的文档里点名 drillDown 属于「渲染器自有、协议不建模」的那一类(该 schema 已有这段话,只是没列它),并保持 (schema as any) 的读取方式,但不要在 spec 注释里把它当作可授权键推荐。

参考

Activity

  1. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    维护者裁决(2026-08-04):A,且改动不大就整件进 v17(含拼写关系收口,不再拆两步)

    执行定调:

    1. 声明 drill 形状:按 ObjectChart.tsx 实际读取点反推({ enabled?, mode?, target, maxRows?, columns? } 以实测为准),strict(战役标准)。落点与拼写的最小改动路径:直接声明渲染器已在读的 drillDown(objectui 零改动),挂 chart config(可发现性优于 options 袋)。
    2. 拼写关系收口(v17 内完成):ReportSchema.drilldown(布尔开关)与 chart drillDown(配置对象)是两个概念,强行统一命名反而合并语义——默认走双向点名消歧:两个 schema 的 strict 门各带对方拼写的 guidance(在 report 上写 drillDown → 点名「这是 chart 的配置对象,报表开关是 drilldown」,反向同理)。若 dev 实测重命名的爆炸半径确实微小且语义可清晰分离,可提议重命名方案,量大即回落双向消歧,不阻塞。
    3. spec 注释里 ChartConfig 剩余的 declared ≠ delivered 属性:stepSize / clickAction / zoom / description / height(#3729 收尾) #3752 处方的历史错误已由批 15 修正;本单落地后把「渲染器读声明键」的 objectui 跟改立为跨分片 follow-up 单(unassigned)。
    4. changeset:additive 为主(minor);若含重命名则 major。窗口开工先核 .changeset/pre.json。

    needs-user-decision 摘除,target:v18 → target:v17,入队(T2 序,量小顺手)。


    Generated by Claude Code

  2. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    CLAIM

    • 会话:session_01Ehu85kbvMcrNTUJjwxvLJ9(spec 车道 PM,派发给 os-dev 实施)
    • 分支:claude/issue-5022-drilldown-declare
    • 方向:维护者 2026-08-04 已裁「改动不大就进 v17」——走「声明它」臂:从 ObjectChart.tsx 实际读取点反推 Zod 形状,drill 配置成为声明键;与 ReportSchema.drilldown 的近似键关系按实测定,不猜。

    Generated by Claude Code

  3. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    实测结果与裁决前提冲突 —— 回 needs_decision,未写任何 spec 代码

    会话 session_01Ehu85kbvMcrNTUJjwxvLJ9 派发的 os-dev。分支 claude/issue-5022-drilldown-declare 已 push(一个空提交,只承载证据,便于恢复),没有开 PR。

    裁决(2026-08-04)第 1 条的落点理由是:

    直接声明渲染器已在读的 drillDown(objectui 零改动),挂 chart config

    这句话的前提在 chart config 上不成立。逐条量给你看(objectui @91757a7,objectstack @41e605ec70)。


    一、ObjectChart.tsx 的真实读取词汇(反推,不用 issue 里的猜测)

    packages/plugin-charts/src/ObjectChart.tsx:602 起,经 @object-ui/core 的 drill-down.ts:

    键 类型 读取点 语义
    enabled boolean isDrillEnabled 缺省关:无 config = 关;{} = 开;enabled: false = 关
    filter Record computeDrillFilter 支持 ${event.*} 插值;缺省时按 groupBy/xAxisKey 推导
    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'(那是 DrillDownDrawer.tsx:84 的实现,chart 自绘抽屉不走它,'navigate' 静默落回 Sheet)。

    所以 issue 正文猜的 { enabled?, mode, target, maxRows, columns } 里,mode 是别的 widget 的键,漏了 filter / title。

    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 全仓无消费者 —— 已另立 objectui#3354。


    二、载体测量 —— 两个候选都不在 dashboard 元数据面交付

    1. widget.chartConfig.drillDown → 无人读。

    • DashboardRenderer.tsx 全文 0 处 chartConfig;
    • ADR-0021 正路 DatasetWidget.tsx:595-605 只取 showLegend 一个键,注释自陈 "the rest of chartConfig stays unforwarded";
    • 同文件 :632 构造的 chart schema 里没有 drillDown。

    2. widget.options.drillDown → 只在一条 v17 作者走不到的分支上被读。

    • DashboardRenderer.tsx:494 / :570 / :531 确实读它,但全部在 isObjectProvider(widgetData) 的 legacy inline 分支内;
    • 而 DashboardWidgetSchema.dataset 是必填(dashboard.zod.ts:427,无 .optional()),于是 datasetBound 恒真,DashboardRenderer.tsx:691 / :720 恒渲染 DatasetWidget,那份带 drillDown 的 componentSchema 被整个丢弃。
    • ⇒ 对任何 spec 合法的 v17 widget,options.drillDown 永不被读。

    3. ADR-0021 正路的 drill 是派生的、不可配置的。
    DatasetWidget 的 canDrill 由 dimensionFields/drillRanges 决定,openDrill 从 dataset 行构 filter,DrillDownDrawer 只收 { objectName, filter, title, dataSource } —— 上面那 6 个键一个都不认。

    4. 唯一真活的面是 react tier。
    react-blocks.ts:226-247 把 ChartConfigSchema 的键作为扁平 prop 发布给 ObjectChart 块,而 ObjectChart 读的正是扁平 schema.drillDown —— 所以 react 页面上写 drillDown 今天就生效。但它既不在 spec 的 dataProps/interactions 里,也不在 objectui registry 的 inputs 里(ObjectChart.tsx:822-830),SDUI save gate 会报 unknown-prop(warning,不拦)。


    三、我们自己的文档已经写下了相反的结论

    content/docs/ui/dashboards.mdx:273:

    Drill-through is automatic — there is no per-widget drill configuration in the dataset form. … Those knobs live in the renderer; dataset-bound dashboards drill through the semantic layer.

    skills/objectstack-ui/SKILL.md:1484:

    Dataset-bound dashboards use the semantic-layer drill above and ignore the rest of this block.

    也就是说:已发布的文档和 skill 已经采用了本 issue 的第二臂(渲染器私有),只是没写进 spec schema 的 JSDoc。裁决选的是第一臂。两者今天是矛盾的。


    四、需要你裁的两个问题

    Q1 —— 载体(架构分叉,必须先定)

    选项 长期正确性(本项目北极星 / contract-first) 让 AI 写的元数据难写错 代价
    A. 只挂 react 块契约(ChartDrillDownSchema + ObjectChart 的 interactions 覆盖层),dashboard 面不声明,并在 DashboardWidgetSchema 的 strict guidance 里点名「dataset 形态的 drill 是派生的,不可配置」 最诚实:declared = delivered 由实测保证,不给 dashboard 契约添一个无人读的键;与已发布文档一致 强:AI 在 dashboard 上写 drillDown 会被 strict 门响亮拒绝并被告知去处;react 上写则有正式契约 react 契约里的 prop 目前是手写类型串(台账已记这是弱点);dashboard 作者仍然配不了 drill(这就是今天的事实)
    B. 挂 ChartConfigSchema(裁决原文) react tier 活、dashboard 面惰性 —— #5011 给 compareTo 记过的 "same key, two fates"。注意这不是我新造的:chartConfig 的 12 个键里已经有 11 个是这个形状(见 #5175),所以 B 是「加入一个既有的系统性缺陷」,不是「制造一个新的」 弱:AI 在 dashboard widget 上写 chartConfig.drillDown 会 parse 通过、渲染无效果、无任何提示 —— 正是本战役要消灭的静默 小(spec 单文件),但要在 JSDoc 里公开写明「dashboard 面不转发」,等于把一个不交付的键写进公共参考页
    C. 挂 options(或 chart config)并在 objectui DatasetWidget 真正落地 终局最强:一套 drill 词汇在正路上被执行,正是 #5011 收口 compareTo 的做法 最强:declared = enforced 由构造保证 不是小改动,跨仓:DatasetWidget 要实现 enabled/target/columns/maxRows/title/filter,且 dataset 聚合行的 drill 语义与 mode: 'record' 天然不匹配。裁决的前置条件「改动不大」不成立

    我的建议:A。 两个轴都指向它 —— 长期正确性上它是唯一让 declared = delivered 成立的选项(B 明知故犯,C 超出本单预算);防 AI 写错上它最强,因为它把 dashboard 面的错写从「静默无效」变成「strict 门带处方的拒绝」。B 的长期代价要说白:一旦写进公共参考页,下一次 ADR-0049 清扫要把它当死键处理时,它已经有协议权威了,退休成本比现在声明的收益高。

    如果你更看重「dashboard 作者能配 drill」这个能力本身,那答案是 C,并且应该按 #5011 的规格另开一单做跨仓收口,而不是塞进本单。

    Q2 —— 声明的形状(取决于 Q1)

    • chart 诚实子集:{ enabled?, filter?, title?, target?: 'drawer' | 'dialog', columns?, maxRows? } —— 每一个键都有 ObjectChart 的读取点。
    • objectui 共享全集:再加 mode / view / report / sort / target: 'navigate' —— 但 chart 一个都不读,view/sort 更是任何 widget 都不读(objectui#3354)。

    建议:诚实子集,并在 JSDoc 里点明这是 chart 面的 drill,mode/report 是 table/pivot/metric 面的键。代价是 spec 类型不能直接被 objectui 的其余 4 个 widget 复用 —— 但那 4 个 widget 本来就不是 react tier 的发布块,让它们继续用 objectui 自己的 DrillDownConfig 是正确的分层,不是重复定义。


    五、近似键(按要求核过,未发现冲突,可照裁决第 2 条做双向消歧)

    ReportSchema.drilldown(全小写,布尔,默认 true,ADR-0021 D2)与 chart 的 drillDown(驼峰,配置对象)没有任何面重叠:前者在 ReportSchema 上、由 plugin-report 的 DatasetReportRenderer.tsx:51,136 读;后者在 chart/widget 的扁平 SDUI schema 上。两者不共享任何 schema、任何渲染器、任何存储行。所以不需要 STOP,双向 guidance 点名消歧是可做的 —— 等 Q1 定了落点之后一并写。


    六、顺带发现(已按 Prime Directive #10 立单,均未指派)


    Generated by Claude Code

  4. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    维护者裁决(2026-08-04,第二轮,基于上楼实测):Q1 = A,Q2 = 诚实子集

    前一轮裁决(05:45)的「挂 chart config」落点前提被实测推翻,维护者改裁 A:

    1. 落点:只挂 react 块契约 —— ChartDrillDownSchema + ObjectChart 块的 interactions 覆盖层(进 registry inputs,让 SDUI save gate 从 unknown-prop 变成正式契约);dashboard 面不声明,DashboardWidgetSchema 的 strict guidance 点名「dataset 形态的 drill 是派生的、不可配置的」—— 与已发布文档(dashboards.mdx:273 / SKILL.md:1484)一致。
    2. 形状:chart 诚实子集 { enabled?, filter?, title?, target?: 'drawer' | 'dialog', columns?, maxRows? },每键有 ObjectChart 读取点;JSDoc 点明 mode/report 是 table/pivot/metric 面的键,不抄 objectui DrillDownConfig 全集。
    3. 前轮裁决仍有效的部分:与 ReportSchema.drilldown 的双向点名消歧 guidance 照做(在新落点上);additive → minor changeset;target:v17(T2 序)不变。
    4. 顺序:objectui#3354(DrillDownConfig 死键)宜先于或并行于本单落地,避免协议照含死键的接口成形;dashboard 作者可配 drill 若成真需求,按 dashboard widget compareTo:三个声明分支在 ADR-0021 dataset 路径上全部无效(两个静默丢弃,一个抛错) #5011 规格另立跨仓单(C 臂),本单不做。

    执行:恢复原 dev 会话(测量上下文在手)实施,配额重置后进行。


    Generated by Claude Code

  5. yinlianghui commented on Aug 5, 2026

    @yinlianghui
    Contributor

    知会:objectui 侧 DrillDownConfig 已清理,spec 声明请以清理后的形状为准

    objectui#3354 已落地(draft PR:objectstack-ai/objectui#3382),即本单第三轮实测里点名的那两条前置。清理后的形状与本单已声明的 ChartDrillDownSchema 对照:

    键 objectui DrillDownConfig 本单声明的 chart 子集 状态
    view 已删除 未声明 ✅ 一致 —— 全仓零读取点,已确认删除,并加了编译期钉子防它悄悄回来
    sort 已删除 未声明 ✅ 一致 —— 同上
    target: 'navigate' chart 现在真的兑现了 'drawer' | 'dialog' ⚠️ 不一致,见下

    前两条正是本单实测建议的方向,现在事实上成立了:第三轮实测里写的「view/sort 全仓无消费者」已不再需要靠注释提醒后人,类型上就没有这两个键了。

    一处需要 spec 车道回看的地方

    本单第二轮裁决把 target 定为 'drawer' | 'dialog',理由是「每键有 ObjectChart 读取点」—— 一条测量。objectui#3354 的另一半正是把这条测量改掉:ObjectChart 现在兑现 target: 'navigate'(对齐 DrillDownDrawer.navigateOnly,含 host 缺失时回落 drawer)。于是渲染器交付 3 个 target,协议声明 2 个,而 validate-react-page-props 会 parse strict schema 拒掉作者写的 'navigate'。

    objectui 侧刻意没有把 'navigate' 加进 object-chart 的 registry inputs 描述 —— 那等于让面板推荐一个发布闸门随后拒绝的值,正是本单要消灭的失效模式。协议联合先动,面板再跟。

    已按 Prime Directive #10 另立 #5435(未指派)记录这条,不在本单里改。


    Generated by Claude Code

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

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions