Repository navigation
Description
发现于 #5114 热修的 dogfood 浏览器端到端复核(真实 /_console bundle,objectui 固定 pin f5bc4c78,Playwright 驱动)。
与 #5114 是同一个「保存筛选条件」动作上的两个独立缺陷,叠在同一份 body 上。#5114 的重开修掉里层那个,修不到这一条。 写下来免得下一个人以为热修合并后控制台就通了 —— 它不通。
实测:真浏览器里点一次「Filter → Add filter」
showcase 起在自有端口,真实登录,/_console/apps/showcase_app/showcase_task,点工具栏 Filter → Add filter:
PUT /api/v1/meta/view/showcase_task.default -> 422
{"error":"[invalid_metadata] view/showcase_task.default failed spec validation: root: Invalid input",
"code":"INVALID_METADATA","issues":[{"path":"","message":"Invalid input","code":"invalid_union"}]}
抓下来的 body 里,filter 是这个:
"filter": {
"id": "root",
"logic": "and",
"conditions": [
{ "id": "712135fb-58c5-4be4-a611-1925181509b0", "field": "title", "operator": "equals", "value": "" }
]
}是 FilterGroup 对象,不是 ViewFilterRule[]。 而 ListViewSchema.filter 声明的是 z.array(ViewFilterRuleSchema)。
两个缺陷是叠着的 —— 把抓下来的真 body 三变体回放
同一份 body,只改 filter,分别打到 修前(origin/main)/ 修后(#5114 热修)两个真实运行的 server:
| 变体 | 修前 | 修后(#5114) |
|---|---|---|
① 原样(FilterGroup 对象) |
422 invalid_union |
422 invalid_union |
② 拆掉分组 → rule[],保留 UI 的 id |
422 | ACCEPTED |
③ 拆掉分组 → rule[],去掉 id |
ACCEPTED | ACCEPTED |
读法:外层类型错(本 issue)挡在前面;把它拆开之后,下一道就是 #5114 那个 id。 #5114 的重开让 ② 从 422 翻成通过 —— 必要,但不充分。外层不修,控制台保存筛选条件仍然 422。
产出端链路(objectui,固定 pin 上逐跳可查)
components/src/custom/filter-builder.tsx——handleChange(newGroup: FilterGroup)→onChange?.(newGroup),交出的是分组对象;addCondition给每行盖id: crypto.randomUUID()(:228)plugin-list/src/ListView.tsx:2155-2161——onChange={(newFilters) => { setCurrentFilters(newFilters); onFilterChange?.(newFilters) }},原样上抛app-shell/src/views/ObjectView.tsx:1592-1595——onFilterChange={(filter) => { persistViewPatch(viewDef.id, viewDef, { filter }); … }}(同源另一处::1337)persistViewPatch(ObjectView.tsx:242)→dataSource.updateViewConfig(objectName, viewId, { ...baseViewDef, ...merged })data-objectstack/src/index.ts:2608→client.meta.saveItem('view', viewId, merged)→PUT /api/v1/meta/view/:name
对照:Studio 侧那条路今天是好的 —— app-shell/src/views/metadata-admin/widgets.tsx:1727-1732 的 handle 在写回前把每行压成 {field, operator, value}(顺带剥了 id),所以走 Studio 视图编辑器配筛选不会 422。缺的是运行时工具栏这条路的同一半转换。
处方倾向(不代劳裁定)
按 Prime Directive #12 契约优先,修产出端,⛔ 不要把 ListViewSchema.filter 放宽成 union([rule[], FilterGroup]):
- A(推荐):运行时工具栏在
persistViewPatch之前把FilterGroup折成ViewFilterRule[]—— 这个转换 Studio 侧已经写好了(widgets.tsx的handle),反方向也有(plugin-view/src/config/view-config-utils.ts的parseSpecFilter/toFilterGroup);缺的就是写方向的这一半。代价小,且把「一个视图的filter在库里只有一种形状」这条守住。 - B:如果分组语义(
logic: 'or'、嵌套组)确实要进协议,那是一次有意的 spec 扩展(带 ADR + 迁移 + 读端全线更新),不是靠消费端容忍。
选 B 之外的任何「放宽 schema」都会让 filter 在库里同时有两种形状,所有读端都得两边都认 —— 正是本仓一直在还的那类债。
logic: 'or' 今天没有任何落盘表示。修 A 之后 and 能正确折平,or 会被静默降级成 and —— 那是「声明 ≠ 生效」的另一面,建议在修 A 时一并判定(要么折平时对 or 显式报错,要么走 B)。
关联
- 控制台保存筛选条件会 422:
ViewFilterRuleSchema拒绝 filter-builder 盖的id,而 wire 成员的.strip()救不到嵌套块 #5114(同一保存动作,里层ViewFilterRule.id;热修已重开该 schema,不覆盖本条)—— PR fix(spec): 重开 ViewFilterRuleSchema —— 控制台盖的 UI 行 id 让保存筛选条件 422(#5114 热修) #5154 ViewItemSchema同时是授权形状和 Studio 往返的 wire 成员 —— 拆成两个 schema 还是保持宽松?(挡住 #4001 批 18 最后 2 站点) #5074(授权/wire 递归拆分)—— 本条不是姿态问题、是类型问题,不在ViewItemSchema同时是授权形状和 Studio 往返的 wire 成员 —— 拆成两个 schema 还是保持宽松?(挡住 #4001 批 18 最后 2 站点) #5074 范围内- union 分支里的 unknown-key 处方永远到不了作者:
zodIssuesToFields只映射顶层 issue,失败的 union 只剩Invalid input#5014(union 报错被压平成Invalid input)—— 本条的 422 正是那个形状:报错里读不出filter两个字,所以它在main上活着没人发现
Activity
xuyushun441-sys commentedon Aug 4, 2026
维护者裁决(2026-08-04):A1
FilterGroup折叠成 spec 声明的ViewFilterRule[];复用 Studio 侧已存在并已测试的同一变换,spec 零改动,存储只保留一种形状。logic: 'or'/ 嵌套分组:响亮拒绝(A1)—— 折叠遇到无法无损表示的形状时给用户可读报错,⛔ 不许静默降级成and。分组 OR 过滤若将来有真实需求,再按 B 臂走 ADR 扩协议,本单不做。id的接纳由 PR fix(spec): 重开 ViewFilterRuleSchema —— 控制台盖的 UI 行 id 让保存筛选条件 422(#5114 热修) #5154(控制台保存筛选条件会 422:ViewFilterRuleSchema拒绝 filter-builder 盖的id,而 wire 成员的.strip()救不到嵌套块 #5114)提供,已在合并队列。CLAIM
session_01Ehu85kbvMcrNTUJjwxvLJ9(spec 车道 PM,派发给 os-dev 实施)claude/issue-5159-filter-foldGenerated by Claude Code
xuyushun441-sys commentedon Aug 4, 2026
重派记录(2026-08-04)
前次派发的 dev 在配额窗口期被维护者手动停止;维护者现指示重派。裁决不变(A1,见上方 2026-08-04 裁决评论),分支沿用
claude/issue-5159-filter-fold;若远端存在截断前的 checkpoint 提交,按「中断运行不留证据」纪律全量重验后再复用。依赖已就位:PR #5154(内层id接纳)已合入 main。session_01Ehu85kbvMcrNTUJjwxvLJ9(spec 车道 PM)Generated by Claude Code
test(app-shell): pin the measured `id` decision behind the filter fold
xuyushun441-sys commentedon Aug 4, 2026
结案(2026-08-04)
由 objectui PR #3360(已合并)按 A1 裁决修复:写方向折叠抽为共享
viewFilterFold.ts(两个调用点走同一函数,顺带删除落后 4 个运算符的手抄映射表);or/嵌套组响亮拒绝(10 语言包);id剥离 —— 本单回放矩阵中变体 ②「rule[] 带 id 被接受」只在 framework main 的服务端成立,objectui 自己钉的 spec(17.0.0-rc.2)会以 unrecognized_keys 拒绝,故剥离是必须而非偏好,该 premise 修正已钉为测试;三变体全部在 payload 边界对真实ListViewSchema闭合。跨仓引用不自动关单,手动关闭。Generated by Claude Code
fix(app-shell): 运行时工具栏保存筛选前把 FilterGroup 折成 spec 的 ViewFilterRule[] (o…