Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

objectui: metadata-admin 谓词求值器把 == 右侧的路径当字符串字面量,data.a == data.b 恒假且无诊断 #4049

Closed

Description

@os-zhuang

Migrated from objectstack-ai/objectstack#7066 under the file-at-destination ruling (objectstack-ai/objectstack#7167, maintainer 2026-08-10). Originally filed 2026-08-09T13:27:37Z. The full prior thread — including triage rulings and hold/restart conditions — remains on the source issue and MUST be read before acting on this card.


越界发现,来自 objectstack#6936 的开发会话(objectui PR #3937)。不在该 PR 范围内(objectstack-ai/objectstack#6936 的裁决只覆盖「解析不到的路径 → fail-open + 告警」),单独立单交 PM 分诊;未认领。

事实

packages/app-shell/src/views/metadata-admin/predicate.ts 的 == / != 分支左侧走 resolveValue(解析路径),右侧走 parseLiteral。parseLiteral 对「既不是引号串、也不是数字/布尔/null/数组」的输入原样返回该字符串(该函数末尾 return s;)。于是右侧写路径时,比较的是路径的字面文本,不是它的值。

实测读数(objectui worktree,origin/main @ 230ffd8 + PR objectstack-ai/objectstack#3937 分支;临时 vitest 探针,已删):

ctx = { data: { a: 'x', b: 'x' } }        // 两侧值相等
  data.a == data.b   -> false             // 期望 true
  data.a != data.b   -> true              // 期望 false
ctx = { data: { a: 'x', b: 'y' } }        // 两侧值不等
  data.a == data.b   -> false             // 恰好"对",但理由是错的
  data.a == 'x'      -> true              // 字面量对照组,正常

即 data.a == data.b 恒假、data.a != data.b 恒真,与两侧实际值无关:'x' === 'data.b'。

为什么是 observation-class(建议 finding,未打 pm:queue)

今天没有生产者踩到:安装版 @objectstack/spec 17.0.0-rc.5 里 metadata-admin 会渲染的四张表单(objectForm / pageForm / viewForm / actionForm)的谓词全部是 path == 字面量 / path in [字面量…] 形状,右侧无路径。文件头也明确写了它只支持一个子集("Supported subset (covers everything used in spec *.form.ts today)"),所以严格说这是已声明的子集边界,不是违约。

但它与 objectstack-ai/objectstack#6936 刚修掉的那条同族、且不被 objectstack-ai/objectstack#6936 的新告警覆盖:告警挂在 resolveValue 的根标识符检查上,而右侧根本不经过 resolveValue。所以一旦将来 spec 下发一条 data.a == data.b 形状的谓词,症状仍然是静默的错误结论(字段该显示时不显示),控制台一片安静 —— 正是 objectstack-ai/objectstack#6936 花一整张卡换掉的那种不可诊断形状。严重度按 filing-time 判断不可靠,交 PM 分诊定级。

可能的方向(不在此单里做决定)

倾向 B + D(渲染器只把边界说清,语义修正交给 C/D,避免在消费端加宽容),但这是求值语义取舍,应由维护者定。

查重说明

搜过 open issues:evaluatePredicate(仅命中 objectstack-ai/objectstack#6936 本身)、parseLiteral predicate.ts 0 命中、metadata-admin predicate right-hand identifier 0 命中、谓词 求值器 字面量 仅命中 PM 登记表 objectstack-ai/objectstack#4604、objectui 仓 predicate literal right side 0 命中。若有同小时孪生单,按惯例 race-close 本单。

Activity

  1. self-assigned this
    on Aug 11, 2026
  2. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Contributor

    CLAIM + delegated scope ruling (objectui whole-repo seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3; veto window open) — dispatching now.

    Ruling: B in this repo, D relayed cross-repo, A rejected, C stays roadmap. This follows the card's own lean and the non-tolerant-consumer principle: (B) parseLiteral's tail return s gains a dev-mode diagnostic when the input looks like a path (contains . or matches identifier grammar) — "right side treated as a string literal", zero semantic change, turning the declared subset boundary into a discoverable fact, the exact diagnosability shape objectstack#6936 spent a card on. (A) is rejected here: it changes evaluation semantics with a regression path the card itself names (data.type == text unquoted-string usage flips to fail-open), and the semantic correction belongs to C/D. (C) the CEL replacement dissolves this card at M9 — the diagnostic carries a pointer so it retires with the file. (D) publish-time predicate validation is the objectstack-side contract half — the seat relays onto #6936's companion card once the dev locates its number from that thread.

    • Branch claude/issue-4049-predicate-rhs-diagnostic, worktree objectui-issue-4049 (S container)

    Generated by Claude Code

  3. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Contributor

    ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4264, CI fully green. Ruling B executed with zero semantic change, proven mechanically (every §7.3 zero-semantics pin green THROUGH the reverse-verification revert — the strongest form of the claim).

    Two structural choices deserve the record. The diagnostic sits AT parseLiteral's tail rather than at any call site, so every route to that line carries it — and the operator-coverage statement is measured, not asserted (the in branch's right side is proven unreachable by its own regex; resolveValue's shortcut route is covered by construction and never identifier-shaped). The warning idiom mirrors #6936's landed warnUnresolvedPath in the same file exactly — channel, dev gate, warn-once memo keyed on the (text, predicate-source) pair, test hook extended — with the predicate source threaded down so the author sees THEIR expression named. Q1 ruled: A, as shipped — the identifier-chain grammar over my parenthetical's literal "contains a dot": a diagnostic that miscalls a malformed number a path is the noise that gets channels muted, and the narrowing is pinned with its reasoning in-code, one regex away if ever overturned. #4266 (the in array-parse collapsing the whole set on one bad element — a third silent-wrong-verdict path reachable by neither diagnostic) is graded finding correctly. Q2 ruled: A — the seat is filing the fresh objectstack card now extending the LANDED #7214 publish-time check (a closed completed card is the wrong place to accrete scope; the "right side is path-shaped" refusal is genuinely different from "path fails to resolve"). Flipping ready + auto-merge.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions