越界发现,来自 objectui#3151 的实现(渲染器侧那一半已修)。未认领,仅记录。
背景
GlobalFilterSchema.defaultValue 是 string | number | boolean,对 type: 'date' | 'dateRange' 的 filter 而言,作者能写的字符串实际上只有两类合法值:
- 一个已知的日期区间预设名(
last_7_days、this_month、…); - 一个 ISO 日期(
2026-01-15),或一个日期宏 token({today}、{7_days_ago})。
第三类(既不是预设名也不是日期,通常就是拼错:last_7_dayz)在 spec 层是完全合法的 —— schema 只知道它是 string。
现在发生什么
- objectstack#4475:预设名那一半在 objectui#3150 修好之前会降级成等值比较,KPI 全为 0(
200 OK,零行,没有任何信号)。 - objectui#3151:拼错的值会降级成一个永不命中的等值比较,同样是
200 OK + 0。已修:buildFilterCondition 现在跳过该 filter 并 console.warn 点名 filter 与该值。
为什么还值得单独立一条
objectui#3151 的修复是运行时的,只能给出一条 console.warn:
- 只有打开 devtools 的人看得见 —— dashboard 照常渲染,只是不再过滤;
- CI / publish 完全看不见:一份 AI 生成的 dashboard 元数据带着拼错的
defaultValue 可以一路发布上线,没有任何门拦它; - 它是放宽方向(跳过 = 不过滤)。这是三选一里对作者最友好的失败模式(数字会明显变化而不是变成 0),但它不是拒绝。
按 Prime Directive #12(contract-first:在作者/发布时大声拒绝,而不是在消费端容忍),这类「只有两种合法拼法」的值应当在 spec 层就被钉死,而不是靠每个消费者各自宽容。这也正是 AI 写元数据最容易藏错的地方 —— 声明即强制,才不会让一个拼写错误在 13 个 widget 上安静地复制 13 遍。
需要定夺的选项(维护者决定,不要直接实现)
- A. 在
GlobalFilterSchema 上做条件校验:当 type 是 date / dateRange 时,defaultValue 必须匹配 预设名枚举 | ISO 日期 | 日期宏 token(superRefine)。
- 长期正确性:最强 —— 声明即强制,发布/解析时硬失败,错误在作者手上而不是在用户的 0 上。
- 代价:预设名清单目前住在 objectui 的
PRESET_RANGES,spec 要成为这份词汇表的唯一来源(日期宏 token 已经有 DATE_MACRO_TOKENS / DATE_MACRO_PARAM_RE 在 spec 里,可以复用);会拒绝掉存量里已有的拼错元数据(这正是意图,但要走 ADR-0087 的转换/迁移评估)。
- B. 只做 publish-gate lint,schema 保持宽松。
- 代价:schema 与 lint 两处知识,容易漂移;
z.infer 出来的类型仍然说「任意 string 都行」。
- C. 什么都不做,只留 objectui 的运行时
console.warn。
- 代价:门槛完全落在「有人正好打开了 devtools」,AI 生成的元数据依旧可以带着拼写错误发布。
倾向 A:两条判据同向 —— 长期架构上它把 filter 值的词汇表收进 spec 这一个契约里(消费端不必再各自宽容),AI 写元数据这条上它在作者时就硬拒绝而不是在渲染时静默降级。但它要动 spec 的公开契约(词汇表归属 + 存量元数据的处置),所以先立此条请维护者定夺,不在 objectui#3151 的 PR 里顺手做。
关联
越界发现,来自 objectui#3151 的实现(渲染器侧那一半已修)。未认领,仅记录。
背景
GlobalFilterSchema.defaultValue是string | number | boolean,对type: 'date' | 'dateRange'的 filter 而言,作者能写的字符串实际上只有两类合法值:last_7_days、this_month、…);2026-01-15),或一个日期宏 token({today}、{7_days_ago})。第三类(既不是预设名也不是日期,通常就是拼错:
last_7_dayz)在 spec 层是完全合法的 —— schema 只知道它是 string。现在发生什么
200 OK,零行,没有任何信号)。200 OK+ 0。已修:buildFilterCondition现在跳过该 filter 并console.warn点名 filter 与该值。为什么还值得单独立一条
objectui#3151 的修复是运行时的,只能给出一条
console.warn:defaultValue可以一路发布上线,没有任何门拦它;按 Prime Directive #12(contract-first:在作者/发布时大声拒绝,而不是在消费端容忍),这类「只有两种合法拼法」的值应当在 spec 层就被钉死,而不是靠每个消费者各自宽容。这也正是 AI 写元数据最容易藏错的地方 —— 声明即强制,才不会让一个拼写错误在 13 个 widget 上安静地复制 13 遍。
需要定夺的选项(维护者决定,不要直接实现)
GlobalFilterSchema上做条件校验:当type是date/dateRange时,defaultValue必须匹配预设名枚举 | ISO 日期 | 日期宏 token(superRefine)。PRESET_RANGES,spec 要成为这份词汇表的唯一来源(日期宏 token 已经有DATE_MACRO_TOKENS/DATE_MACRO_PARAM_RE在 spec 里,可以复用);会拒绝掉存量里已有的拼错元数据(这正是意图,但要走 ADR-0087 的转换/迁移评估)。z.infer出来的类型仍然说「任意 string 都行」。console.warn。倾向 A:两条判据同向 —— 长期架构上它把 filter 值的词汇表收进 spec 这一个契约里(消费端不必再各自宽容),AI 写元数据这条上它在作者时就硬拒绝而不是在渲染时静默降级。但它要动 spec 的公开契约(词汇表归属 + 存量元数据的处置),所以先立此条请维护者定夺,不在 objectui#3151 的 PR 里顺手做。
关联