Skip to content

dashboard widget compareTo:三个声明分支在 ADR-0021 dataset 路径上全部无效(两个静默丢弃,一个抛错) #5011

Closed

Description

@xuyushun441-sys

在 #4956 给 dashboard.widgets 做逐键分类时实测发现,与该单裁决无关,独立存在。

声明

DashboardWidgetSchema.compareTo(packages/spec/src/ui/dashboard.zod.ts)声明三个分支,TSDoc 写得很具体:

  • 'previousPeriod' —— 从 widget 的 filter 日期宏自动推导对比窗口
  • 'previousYear' —— 整体回移一个日历年
  • { offset: '7d' | '1M' | '1y' } —— 按显式时长回移

实测

内联 object-provider 图表路径(遗留形态)—— 正常。 DashboardRenderer 把 widget.compareTo 传进 object-chart schema,objectui packages/core/src/utils/compare-to.ts 的 CompareToConfig 恰好就是这三个分支,shiftFilterByCompareTo 全部实现。

ADR-0021 dataset 绑定路径(spec 自称「唯一面向作者的分析形态」)—— 三个分支全废:

  1. 两个字符串分支被显式丢弃。DatasetWidget.tsx:163-168 只在 typeof widget.compareTo === 'object' 时才转发,注释写明理由是「forwarding it makes the executor throw "compareTo requires a timeDimension"」。作者写 compareTo: 'previousPeriod',基础数值照常渲染,对比列静默消失。
  2. { offset } 分支被转发进 DatasetSelection.compareTo —— 而那个契约(packages/spec/src/contracts/analytics-service.ts:104-109)要求的是 { kind, dimension }。packages/services/service-analytics/src/dataset-executor.ts:870-876 拿 cmp.dimension(此时是 undefined)去找 timeDimension,找不到就抛:
[dataset-executor] compareTo requires a timeDimension "undefined" with a dateRange.

执行器里根本没有 offset 这个概念 —— 它只认 kind: 'previousPeriod' | 'previousYear' 加一个具名时间维度。也就是说 widget 侧的词表和 dataset 侧的词表从来没有对齐过,只是没人被要求去看。

为什么值得单开一单

这不是「declared but unread」,而是更靠后的一档:同一个键在两条路径上有两种命运,而且失败的那条恰恰是 spec 自己推荐的规范路径。一个照着 TSDoc 写 { offset: '7d' } 的作者(或 AI)在 dataset 看板上拿到的是整块 widget 报错,在遗留内联图表上却工作正常。

可选方向(需要维护者裁决,别猜)

  1. 在 widget 侧收敛词表:把 compareTo 改成 dataset 侧真正支持的 { kind, dimension },offset 走 ADR-0087 conversion 退休 —— 契约优先,但会动 authorable 面。
  2. 在 dataset 执行器里实现 offset + 自动推导维度,让三个分支都真的可用 —— 保住已声明的能力,成本在分析服务侧。
  3. objectui 侧翻译:DatasetWidget 把 widget 形态映射成 { kind, dimension } —— 最快,但这是消费者侧的宽容映射,Prime Directive Add comprehensive test suite for Zod schema validation #12 明确反对。

台账现状:packages/spec/liveness/dashboard.json → props.widgets.children.compareTo 记为 live(遗留路径确有消费者),并在 note 里完整记录了这条路径分裂 —— 照 action.disabled 的先例,「一个 surface 成立就记 live」必须把边界写清楚,否则就是下一次误读的种子。


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:specDomain lane — seat card indexed by label:pm:seat
    on Aug 3, 2026
  2. added
    pm:queueReady for the PM dispatch loop
    target:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA
    on Aug 4, 2026
  3. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    维护者裁决(2026-08-04):方案 1 + 执行器决议规则(T1,v17 窗口)

    • compareTo 收敛为 { kind: 'previousPeriod' | 'previousYear', dimension? } —— 收敛到已实现的那份契约(DatasetSelection.compareTo),widget 声明变为执行器真实能力的薄投影,declared = enforced 由构造保证。
    • dimension 可省略(从方案 2 借的唯一部分,且是执行器决议规则而非消费端宽容):dataset 唯一时间维度自动采用;歧义时响亮报错并列出候选。消除「强迫作者点名显然维度」的新摩擦。
    • offset 走 ADR-0087:'1y' → kind: 'previousYear' 确定性 D2 改写;'7d'/'1M' 无法忠实改写 → 墓碑带处方(改写为 kind + 调整 filter 窗口);先实测存量——canonical 路径用过 offset 的早已崩,真实存量预期只在遗留路径且极少。
    • objectui 遗留内联路径跟改消费新形状(跨仓 follow-up,spec: DashboardWidgetSchema.responsive 按断点建模 —— objectui#3173 维护者已裁决;目标 17.0.0 正式版,带回退条款 #4876 式知会 objectui 分片)。
    • TSDoc 与参考页同 PR 修正(现文档在自信地教一个会崩的形状);major changeset。
    • ⛔ 方案 3(消费端翻译)否决——PD#12 反对的宽容映射,把词表分裂藏起来;方案 2 的 offset 实现不做——业务增量小(7d 窗口下 previousPeriod ≈ offset 7d;'1y' ≡ previousYear),日历语义边角(跨月长/闰年)是静默错窗口类 bug 温床。

    执行硬约束:串行在 PR #5042(批 14)落地之后 —— 两者动同一文件同一键(dashboard.zod.ts 的 compareTo,批 14 刚收紧其臂集),顺序错了就是在未落地的收紧上改形状。

    needs-user-decision 摘除已完成,入队 T1 序(批 18 之后、批 20 之前)。


    Generated by Claude Code

  4. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    🔒 认领:PM 循环派发(限额重置后恢复,T1 序——#5042 已落地,串行约束已除)
    会话:session_01Ehu85kbvMcrNTUJjwxvLJ9
    分支:claude/issue-5011-compareto-converge
    Worktree:objectstack-issue-5011
    域:domain:spec × domain:services(跨域单认领,文件面全量声明)
    文件面:packages/spec/src/ui/dashboard.zod.ts(仅 compareTo)、packages/services/service-analytics/src/dataset-executor.ts(dimension 决议规则)、conversions/migrations 注册表、台账 ui 段(dashboard 行)、spec 生成物、objectui 只读

    按 2026-08-04 裁决(方案 1 + 决议规则)执行,⛔ 不重议。与在飞批 17(component)、批 18(view)源文件不相交;台账 ui 段多方竞争照惯例。⚠️ 本文件今日已两改(#4995 墓碑、#5042 收紧),开工以 origin/main 现状为准。


    Generated by Claude Code

  5. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    ✅ 验收通过 —— PR #5147(即刻挂轨)

    复核实况(22 文件;⛔ releases/ 零触碰;kind 词表落地实证):裁决四要件全部到位——

    1. 薄投影:compareTo → strictObject({ kind, dimension? }),与执行器契约同源,无第二词表可漂移。
    2. 决议规则在生产者侧:resolveCompareDimension 按执行器自己的判据(带 dateRange 的 timeDimension),唯一→采用、零/多→响亮点名候选——不是消费端宽容。
    3. ADR-0087 按语义分流是本单亮点:string 双形 + {offset:'1y'} 确定性 D2;其余时长刻意不机械改写(previousPeriod 按解析窗口自身长度平移,机械改写会静默改变对比行集)→ D3 semantic。存量实测 4 例(全 string、全在 app-crm)、零 offset——裁决的「先测存量」兑现。
    4. 处方从真实失败读回(带 os migrate meta --from 16);双侧 sabotage(13/17 + 8/9,留绿者逐一说明);兄弟条目在 spec-changes delta 中逐项存活。

    跨分片:objectui#3337 已立(unassigned),把非纯改型的关键一步点透——DatasetWidget 须把 widget 解析出的日期窗口降入 selection.timeDimensions[].dateRange,否则 dataset 路径仍无窗可移。#5014 仅对本槽位溶解(不再有 union),台账行如实记载而非撤回发现。


    Generated by Claude Code

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

Metadata

Metadata

Labels

bugSomething isn't workingdomain:specDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loopprotocol:breakingtarget:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions