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.

console validation 预览样例的 condition 语义写反了(spec 里 condition 为 TRUE 表示校验失败),另带两个会被静默剥掉的键 #3276

Closed

Description

@xuyushun441-sys

objectui#3266 在修 apps/console/src/preview-samples.ts 的 validation 样例(只改了 events 枚举)时顺手核到的,不在那个 PR 的 scope 内,单独记这里。

1. condition 语义写反(主要问题)

ScriptValidationSchema.condition 的契约是:

condition: ExpressionInputSchema.describe('Predicate (CEL). **If TRUE, validation fails.** e.g. P\record.amount < 0`')`

而样例是:

validation: {
  name: 'amount_positive',
  message: 'Order amount must be greater than zero.',
  condition: 'amount > 0',
  …
}

按契约,这条规则会在金额为正时判定失败 —— 和它的 name / message 正好相反。正确写法是 condition: 'amount <= 0'。

这个错误不会被 objectui#3257 建的守卫抓到(它只问「能不能 parse」,语义是对是反 schema 管不着),但危害恰恰更大:样例是设计器画廊里给作者(多数情况下是模型)照抄的那份范例,照抄出来的规则会静默地把合法记录拦下、把非法记录放行。spec 自己的 docstring 举的例子就是 P\record.amount < 0``,即「不合法的情形」,方向和样例相反。

2. 两个会被静默剥掉的键

ValidationSchema 不是 .strict(),所以下面两个键 parse 能过、但在解析结果里根本不存在:

  • expression: 'amount > 0' —— ScriptValidationSchema 只有 condition,没有 expression;
  • object: 'sales_order' —— 校验规则挂在 object 的 validations[] 上,宿主对象就是它的作用域,规则自身不带 object。

(顺带:skill 样例的 type: 'prompt' 同理 —— SkillSchema 没有 type 键,只有 surface,该值被静默剥掉。)

这类键的问题在 objectui#3266 正文里已有定性:守卫的强度取决于 spec 的严格度,非 .strict() 的 schema 会把未知键悄悄剥掉而不是报错,所以守卫对它们照样绿。样例里留着一个「写了等于没写」的键,教的还是错的写法。

建议

  1. condition: 'amount > 0' → 'amount <= 0';
  2. 删掉 expression 与 object(以及 skill 的 type);
  3. 如果希望这类语义错误将来能被机器发现,那是另一件事(需要的是规则语义的断言而非 schema 校验),不建议塞进 preview-samples-spec-valid.test.ts —— 那个守卫的职责是「能不能发布」,别把它变成什么都装的筐。

相关

⚠️ 未指派 —— Prime Directive #10 记录性建单。

Activity

  1. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    ContributorAuthor

    PM 验收:ACCEPT — PR #3282,已入合并队列

    CI 15 项全绿(含 Type Check / Lint / 4 个 shard / Changeset Fixed Group Check)。validation 与 skill 仍留在 SPEC_CLEAN。

    它先核对契约再翻谓词 —— 这正是我要求的

    我在派发里写了「先自己核对 spec 契约再翻,不许照我的话改谓词」。它照做了,并给出出处:

    node_modules/@objectstack/spec/src/data/validation.zod.ts:119
    'Predicate (CEL). If TRUE, validation fails. e.g. record.amount < 0'
    

    确认 condition 是失败谓词之后才把 'amount > 0' 翻成 'amount <= 0'。原样例确实在拒绝每一条合法订单、放行每一条非法订单,与自己的 name 和 message 完全相反。

    多找出一个键,而且是靠机械审计而不是照单执行

    单子列了三个静默剥掉的键(validation.object / validation.expression / skill.type),它逐样例机械审计后找出第四个:validation.field —— 该键只存在于 state_machine / format / json_schema 分支,不在 script 分支上。

    两条边界也守住了:没有往 preview-samples-spec-valid.test.ts 加语义断言(我的 ⛔),也没有把这些键塞进 RETIRED_KEYS(我的 ⚠️)—— 它明确区分了「已退役的键」与「在那个 schema 上从未存在过的键」,这两者混在一起会让台账失去意义。

    ⚠️ 两条越界发现,其中一条改变了我对既有守卫的信心

    #3281 —— 已并入在飞的 #3275(同目录、同缺陷族,我已把它作为范围追加发给那个 agent,避免多花一轮)。要点:ValidationPreview.tsx 读 celText(d.condition) ?? celText(d.expression),而 expression 在任何 validation 分支上都从未存在过。这解释了那个假键为什么一直没被发现 —— 预览两种写法都渲染得好好的。消费端的 ?? 兜底再一次充当了掩护。它还带着一整个 case 'unique' 分支,而 spec 已刻意移除该校验类型(理由是 TOCTOU),改用唯一索引。

    #3280 —— 已入队,而且它揭示了一件要紧的事:

    这条正好命中 #3257 那个守卫在自己文件头主动标注过的盲区(非 .strict() 的 schema 会静默剥离未知键,所以 PASS 只证明结构合法)。当时它诚实地写下了这个上限,现在我们知道那个上限已经在实际掩盖一个样例了。

    #3280 提的补救是结构性检查(parse 前后比较键集合),而不是我在本单否掉的语义断言 —— 这个区分是对的,而且它一次跑就找出了全部 9 个键。这条按 v17 判据属契约类(样例是给作者/AI 的范本),会在下一批排。

    ⚠️ view 那条不能靠删键"修好":ViewSchema 容器如何绑定对象是契约问题,与已在 KNOWN_STALE 里的 translation 同类,必须先定契约。


    Generated by Claude Code

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

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions