Skip to content

validateExpression 把方言处方 trailer(「predicates are bare CEL…」)逐字节复用到越界类错误上 —— 教作者改方言,改了必然回退(全部 ~10 个表达式槽位) #7073

Closed

Description

@os-project-manager

出自 #6833 的测量派发(会话 session_01AZgRyPVwi1jLb1mNNuUQ9o,座位贴 #6018);该单前提被证伪关单,本卡是其测量中幸存的真缺陷,由座位按 dev 的 needs_decision 建议转立。未认领、未定级(定级归分诊)。

缺陷(实测,origin/main 2c7e62d5f)

packages/formula/src/validate.ts 的 validateExpression,celEngine.compile 失败分支:

errors.push({
  source,
  message:
    `invalid CEL ${role}: ${compiled.error.message}` +
    (hint ? ` — ${hint}` : ` — ${role}s are bare CEL (e.g. \`record.rating >= 4\`).`),
});

trailer 不区分失败类别。对一条语法完美、可下推、只是超出平台预算的 CEL(如 80 项合取,Exceeded maxAstNodes (256)),作者收到的完整消息是:

invalid CEL predicate: Exceeded maxAstNodes (256) — predicates are bare CEL (e.g. record.rating >= 4).

前半句(来自 parseCelToAstWithReason 的 reason-carrying 消息)是对的;后半句教作者去检查方言——他改方言不可能修好,只会回退。对 AI 作者尤其危险:模型会服从最后一句处方。

影响面

validateExpression 是 ADR-0032 的共享验证器,覆盖全部 ~10 个表达式槽位(build / 元数据注册 / validate_expression 工具),不只 sharingRules[].condition。一处修好,全槽位受益。

与既有工作的关系

顺带折入:cel-pushdown-limits.ts docblock 的完备性缺口(建议只修文字)

其 docblock 称「Nothing else needs to move at GA. In particular @objectstack/lint's two enforceability gates need no edit … cannot drift from it」。对它点名的两个门成立,但不完备:第三个门 validateStackExpressions 覆盖同一 sharingRules[].condition 字段且 mode-agnostic(validateExpression 从不读 GA 开关)——宽限窗口内 lint 严于运行时。#6833 的测量判定该分歧良性且 GA 时自愈(收紧方向,安全侧),放松 lint 去追运行时反而会制造 #6833 担心的那个缺陷。故建议:行为不动,只补 docblock 那句「nothing else」的完备性。

查重

validateExpression bounds message / expression-invalid over-budget / CEL bounds trailer 标题扫描:开放单零命中(#6833 的 dev 与座位各查一遍)。#6778/#6831 已关/已合并,是先例不是重复。

出处

#6833 线程载有完整测量表(两个 GA 开关位置 × 五类输入的「谁报什么」对照)。

Activity

  1. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    Contributor

    Triage: pm:queue + domain:engine-core.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 10, 2026
  3. self-assigned this
    on Aug 10, 2026
  4. os-help commented on Aug 10, 2026

    @os-help
    Collaborator

    Claim: PM loop round 2 (spec-surface seat, cross-lane under maintainer directive)
    Session: session_016R9de1FqP7NvwKvqXi92Gh
    Branch: claude/issue-7073-bounds-trailer
    Worktree: objectstack-issue-7073
    Domain: domain:engine-core
    File surface: packages/formula/src/validate.ts (+ its tests), packages/formula/src/cel-pushdown-limits.ts (docblock sentence only, per the card's folded-in item), .changeset/ (stop on breach; explain in the report)
    Container & model: M card, mode:subagent, model: opus
    Serial constraints cleared: no in-flight claim on packages/formula; the consumer-side precedent #6778/PR #6831 is merged, not in flight. Disjoint from my in-flight set (#7129/#7130 objectui, #7145 service-storage, #7150 metadata-fs). ⚠️ An active domain:services seat (session session_015fkdTyGmMD5s8ZtEifvuGy) is working #7135 in plugin-approvals/plugin-reports — no file overlap with formula.

    Instruction provenance (cross-lane dispatch): maintainer, this session's chat, 2026-08-10 ~01:5xZ — 「所有队列卡片都派发,v17 优先派发」.


    Generated by Claude Code

  5. os-help commented on Aug 10, 2026

    @os-help
    Collaborator

    ACCEPT — PR #7209 (review of record, session session_016R9de1FqP7NvwKvqXi92Gh).

    What shipped: the bounds-vs-dialect fork at the producer — celEngine.compile's own kind === 'bounds' selects a size prescription naming the exceeded budget and its value (via parseCelToAstWithReason, deliberately without admitOverLimit), while genuine dialect/syntax faults keep the old trailer verbatim and the #1491 braces hint is untouched. cel-pushdown-limits.ts gets the ruled text-only completeness note (third gate named, benign-and-self-healing recorded, ⛔-don't-chase spelled out). 4 files, +215/−2, changeset patch, zero riders.

    Verified against GitHub: draft targeting main, first line Fixes #7073; CI converged at 98c1b78c2 — 26 checks, zero failures; ESLint success (02:52:14Z) and TypeScript Type Check success (02:59:24Z) personally confirmed.

    Review credits:

    1. Both directions pinned by design: three bounds shapes (maxAstNodes/maxDepth/maxListElements) × both roles go to the size prescription, and three dialect faults + the braces case keep the old trailer byte-for-byte — the refusal surface cannot shrink while appearing to widen. Reverse verification: 7 red, all in the predicted bounds direction, dialect pins green in the same run.
    2. The non-portability warning was honored with a pin, not a promise: fix(lint): 超预算的 RLS 谓词有了自己的规则 id rls-predicate-over-budget (#6778) #6831's RLS sentence is excluded and expect(message).not.toMatch(/widen|grant|permission/i) keeps it out.
    3. Consumer sweep is the real thing: all three direct consumers of validateExpression (lint / service-automation / mcp) run green; the one red encountered was correctly diagnosed as the stale-artefact trap and cleared by building the closure, not by touching code.
    4. Message-pin sweep covered E14/E23 spellings (literal, toMatch(/…/), line-leading +) — zero pins on the bounds class, none flipped.

    Out-of-scope finding verified filed: #7217 — the same defect shape at validate-visibility-predicates.ts's own gate (deliberately not routed through validateExpression, so unreachable from this PR). Measured, unlabeled, left for triage.

    Next: ready → merge queue.


    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

Labels

pm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions