Uh oh!
There was an error while loading. Please reload this page.
fix(app-shell): metadata-admin 谓词解析不到的路径 fail-open + dev 告警 (objectstack#6936) - #3937
Merged
Merged
Conversation
…, loudly (objectstack#6936) `predicate.ts` promised fail-open in its header and delivered it only for thrown errors. A path whose ROOT identifier is not a name in the evaluation scope resolved to `undefined`, and every comparison then judged it false — `['text'].includes(undefined)`, `undefined === 'text'` — so a predicate referencing a name that is not there HID the field, silently. Fail-closed, the opposite of the documented promise. Per the maintainer ruling on objectstack#6936 (option C): an unresolvable root identifier now makes the predicate evaluate `true`, and dev mode warns once per (path, predicate) pair naming both, mirroring `warnOnUnknownActionKeys`. The boundary is deliberate and pinned: an absent draft VALUE (`data.type` on a draft with no `type`) still resolves and still compares false — only an unresolvable root fails open. The signal is a thrown internal error rather than a sentinel so fail-open is a property of the whole predicate: a sentinel threaded through `!` would negate back to false, i.e. fail-closed by another route. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
commented
Aug 9, 2026
CollaboratorAuthor
✅ 验收通过(objectui 分片 PM,session 实物核验:head 验收要点:
范围外 finding objectstack#7066( Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 9, 2026 13:29
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixesobjectstack-ai/objectstack#6936
执行维护者裁决 C 案(objectstack#6936 评论 5230638168):metadata-admin 谓词求值器对「解析不到的路径」由静默判假改为 fail-open 判真 + dev 期告警。
问题
packages/app-shell/src/views/metadata-admin/predicate.ts的文件头一直承诺 fail-open(“on any parse error → returns true: better to show a field than to silently hide it”),但只有抛错走到那个 catch。标识符解析不到时走的是另一条路:resolveValue逐段下钻取不到就返回undefined,随后所有比较一律判假 ——['text','number'].includes(undefined)、undefined === 'text'—— 于是引用了不存在名字的谓词把字段静默隐藏。实现是 fail-CLOSED,与自述方向相反,且控制台一片安静。踩法是版本错配窗口(objectui#3923 已浏览器实测):
@objectstack/spec17.0.0-rc.5 及更早把objectForm的子字段谓词写成裸拼法(type in ['text',…],共 16 条),而本引擎在{ data: draftRow }作用域下求值。前端带着 objectstack#6331 的读取器修复、后端仍是旧版时,裸type解析为空,Studio 对象字段列表里 16 个类型相关子字段(Min / Max / Precision / Scale / Max Length / Min Length / reference / deleteBehavior / expression / returnType / autonumberFormat / language …)对所有行类型同时消失,用户看到的症状是「配置项没了」,与权限问题、类型不支持无法区分。改动
@object-ui/core的warnOnUnknownActionKeys。memo 只按 path 记的话,只会报出 16 条里的第一条、对其余十五条保持沉默。语义边界(本 PR 的承重部分)
「解析不到」= 路径的根标识符不是本作用域声明的名字(
type、record.status、page.selectedId,而作用域里唯一的名字是data)。它不等于「值算出来是 undefined」:data.type == 'text'在还没填type的草稿上 —— 根data解析成功,草稿只是那个键上没有值。这是合法的 undefined 值,照旧判假、字段照旧隐藏、不告警。草稿允许是空的;把 fail-open 扩大到「任何缺失值」会让一行新建字段一次点亮全部类型相关子字段(本 PR 在渲染层钉了这一条)。data.tpye这种深一段的拼写错误,在没有 schema 的情况下与「草稿没填」不可区分,而本求值器手上没有 schema。抓它属于生产端发布期校验谓词路径引用(伴生卡,已另立),不是渲染器可以猜的事(Commandment #0.1)。失败信号用抛出内部错误而不是 sentinel 值,理由是 fail-open 是整条谓词的属性、不是失败那个子表达式的属性。sentinel 需要人工穿过
!、&&、||、in、==逐个传递,第一个漏掉的运算符就会把结论反过来 ——!unresolvedPath会把内层解析成「真」再取反回假,即换条路又回到 fail-CLOSED(这一条已单独钉住)。抛出则把所有失败都路由到本来就存在的那一个 fail-open 出口。不变且已钉:原有 parse-error fail-open(仍静默 —— 路径解析成功、读取时炸了,是另一个事实);CEL 顺序的短路吸收(
false && 不可解析为假、true || 不可解析为真,都不告警,因为那一半根本没被求值);CEL 风格的宽松 nullish 相等(空草稿上data.type == null仍为真)。测试
新增 63 个断言,分两个文件:
predicate.test.ts(求值器层):①解析不到 → 真 + 告警内容含路径与谓词原文(含!取反、错误作用域根、继承名constructor、warn-once、同路径不同谓词各报一次);②解析到且真的不等 → 假、静默;③解析到但值为 undefined/null 的合法比较行为逐一钉住(含嵌套data.config.kind、== null宽松相等);④原 parse-error fail-open 回归钉(getter 抛错 → 真且不借用新告警);⑤短路吸收;⑥直接从安装版 spec 读objectForm的谓词做版本错配重现(16 条全裸拼法),外加一张字面量表把 16 条固化下来 —— spec 升到含 objectstack#6254 的版本后裸拼子集会合法地变空,所以「谓词总数大于 0」这条守卫无条件断言,避免整块变成对空集合的绿。SchemaForm.unresolvedPredicate.test.tsx(渲染层,issue 里 B1/B2 两向读数的复现):B2 裸拼法下子字段重新出现且控制台有话说;B1data.拼法仍按行类型区分;未填类型的行仍保持子字段隐藏。命令与结果:
反向验证(方向先判后跑,预判为常规 Red):把
resolveValue里新增的抛出那一段去掉后,63 条里 31 条转红(fail-open 组 9 条、垃圾表达式 1 条、可达的不可解析一半 1 条、bundled-spec 裸谓词 2 条、16 条字面量表、B2 渲染 2 条),典型失败是AssertionError: expected false to be true;其余 32 条保持绿,正好是边界组(解析到判假、合法 undefined、parse-error 回归、短路吸收、B1 渲染)—— 这组保持绿正是它们钉的是改动前就有的取值语义、而不是新分支的证明。另外 objectstack#6331 留下的既有断言「both keys absent → shown」(data.type == 'list'对{}判假隐藏)在本改动下保持绿,如果边界放宽到「任何缺失值」它就会红。changeset:
@object-ui/app-shellpatch。⛔ 未折叠伴生卡(发布期校验谓词路径引用,producer 侧),⛔ 未碰 objectstack#5149 的运行时求值器文件,⛔ 未改 objectstack#6331 落地的
readVisibility读取面。Generated by Claude Code