fix(app-shell): 检查器读写 { dialect, source } 表达式信封 (#3218) - #3228
Conversation
) `HookSchema.condition` is `ExpressionInputSchema`, so a persisted hook carries `{ dialect, source }` — the Hook inspector's guard box read a bare string only and rendered empty, and `ConditionBuilder.emit` compiles only the rows on screen, so the author's next edit replaced a guard they were never shown. Read side converges on `conditionText` (the one reader #3216 settled on) via a shared `expressionSource` / `writeExpressionSource` pair — no fourth `typeof c === 'string'`. Write side follows the #3218 ruling: preserve `dialect` and `meta`, replace `source`, DISCARD `ast` (derived from the old source; `objectstack compile` refills it). With no prior envelope the commit stays the bare-string shorthand, which the spec's pipe normalizes to `{ dialect: 'cel', source }`. Swept the rest of the family: action `visible` / `disabled`, the generic SchemaForm condition widget (which put a literal `[object Object]` in the editor), the object-validations rule `condition` plus its type-switch carry (a third narrow read that dropped the guard), and the flow-edge inspector's write, whose read #3216 had already fixed. Fixtures are authored input fed through `HookSchema.parse`, never hand-written envelopes, so they cannot drift from the spec. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PRJtkgUAaVG11FsJQbvZWA
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
PM 验收:ACCEPT,放行合并队列CI 15 项 13 绿 2 skipped、零红,已独立复核。 裁决(选项 B + 普查是本 PR 最大的价值,远超原单派发时那句"
外加 也就是说,我在裁决里判断"A 会静默改写 dialect"的那个口子,在仓里有五个入口。只修报告的那一处,等于修好一扇门、留着四扇。 边界守得对没有顺手改 一处设计选择我确认一下,认为正确没有原信封可保留时仍提交裸字符串简写(而不是强行构造 Generated by Claude Code |
Fixes #3218
缺陷
HookSchema.condition用的是ExpressionInputSchema(和FlowEdgeSchema.condition同一个 pipe),裸字符串在 parse 时被规范化成{ dialect: 'cel', source }—— 信封才是平台自己产出的形状。Hook 检查器只认裸字符串,于是「Run only when (optional CEL)」渲染为空。空框不是外观问题:
ConditionBuilder的emit只编译当前 UI 里的行,所以作者在这个看起来空的构建器里下一次编辑,提交的是替换掉那条他从没看见过的守卫(清空则提交condition: undefined)。仅仅打开面板是安全的(onCommit只在真实编辑时触发),但诱导那次编辑的正是这个空框。读侧:走既有的那一个读法
全部经
conditionText(#3216 沉淀下来的唯一读法),包在共享的expressionSource/writeExpressionSource里。没有写出第四份typeof c === 'string'——previews/flow-canvas-layout.ts未被改动,只被读取。写侧:按本单裁决(选项 B),含
ast必须丢弃那一条原先提交路径唯一保住的键就是
source;其余全丢,因为它发的是裸字符串,而 spec 的 pipe 把dialect硬编码成cel。也就是说,改一个dialect: 'cron'/dialect: 'template'守卫的一个字符,就把它换到了另一个求值引擎上,并顺手丢掉ast与 ADR-0089 的meta(rationale/generatedBy—— AI 作者最常填、也最没人会手工补回来的那份)。现在编辑
source时:dialectmetasourceastobjectstack compile会重新填,且source在,ExpressionSchema的source || astrefine 仍满足。没有原信封可保留时(值缺失、裸字符串、或
action.disabled上的布尔),提交仍是裸字符串简写 —— spec 的 pipe 把它规范化成的正是{ dialect: 'cel', source },一分不少。这条同时让同一个 helper 对普通string谓词字段也安全:给什么形状,还什么形状。这一点是有意为之,因为通用 SchemaForm 条件控件同时服务这两类字段。普查结果:同族有四处,不止一处
inspectors/HookDefaultInspector.tsxcondition—— 本单报告的缺陷inspectors/ActionDefaultInspector.tsxvisible/disabled(spec 是boolean | ExpressionInput),同样的空框读法widgets.tsx的ConditionWidgetSchemaForm把每一个谓词名字段(visible/hidden/disabled/condition/predicate/*When)都路由到这里,而它做的是String(value)—— 信封进到编辑器里是字面量[object Object],比空框更糟studio-design/ObjectValidationsPanel.tsxcondition;另外类型切换器里还有第三份窄读法(typeof cur.condition === 'string'),信封守卫会被直接丢掉、留下骨架里那条永不命中的'false'。ValidationRuleDraft.condition的本地类型由string改为ExpressionInput外加
inspectors/FlowEdgeInspector.tsx:#3216 只收敛了它的读,写仍是裸字符串,是上面那条 dialect 改写的同一个口子,一并按同一规则修掉。未改动
inspectors/PageBlockInspector.tsx的hidden。它不是本族:PageComponentSchema是.strict()且根本没有hidden这个键(canonical 是visibleWhen)。那是另一个真缺陷,已按 Prime Directive #10 单独立单:#3229。测试(先写,改前失败)
新增两个文件,固件一律不手写信封 —— 按 #3216 的做法把作者输入喂给
HookSchema.parse再断言,固件因此不可能与 spec 漂移。inspectors/HookDefaultInspector.condition.test.tsx—— 真渲染检查器:① 信封条件显示出source;② 编辑后dialect/meta原样保留;③ 原来带ast的,编辑后ast没了(并再跑一次HookSchema.parse证明结果仍然合法);④dialect: 'template'的条件编辑后仍是 template(A/B 分界线);外加清空仍提交undefined。inspectors/expression-envelope.test.ts—— 共享读写对的单元测试(其余调用点靠这一份契约覆盖),含cron、AST-only 信封、布尔disabled、不可变性。改前:
5 failed | 1 passed,失败原因正是守卫渲染为空(找不到条件行、找不到删除按钮、raw 编辑器值为空串)。改后15 passed;packages/app-shell全量257 files / 2203 tests通过,type-check与lint(0 errors)通过。