Skip to content

A format rule with an invalid regex, and a json_schema rule ajv cannot compile, still fail OPEN — the same trap #4649 closed, one rule type over #4762

Closed

Description

@os-zhuang

Found while implementing #4649 (PR #4761). Filed unassigned, not fixed there — #4649's ruling scoped itself to the object-level predicates, and widening it in the same PR would have mixed two blast radii.

What is still fail-open

packages/objectql/src/validation/rule-validator.ts:

  • checkFormat — a regex that new RegExp(...) throws on is logged and skipped:
    Validation rule '<name>' has an invalid regex — skipped
    
  • checkJsonSchema — a schema ajv.compile(...) throws on is logged and skipped:
    Validation rule '<name>' has an uncompilable JSON Schema — skipped
    

Both are the exact shape #4649 was filed about: the rule is declared, appears in the metadata, appears in any "what protects this object" listing, and enforces nothing — the only signal being a WARN in a log nobody reads. #4761 flipped the CEL predicates (script / cross_field / conditional.when) to fail closed and left these two deliberately unchanged, with a test pinning that they still fail open so the gap is visible rather than assumed closed:

packages/objectql/src/validation/rule-fail-closed.test.ts › #4649 — unchanged neighbours.

Why it is a smaller problem than #4649 was, but still a problem

A broken regex / schema is static — it is broken for every record, from the moment it is authored, not only for records of a particular shape. So it is far more likely to be noticed in dev than #4649's data-dependent fault was. That is an argument about when it is noticed, not about whether the rule enforces anything: on a deployed system it enforces nothing, silently, exactly as before.

Two ways to close it, and they are not exclusive

  1. Authoring/publish time (preferred, contract-first). A regex that does not compile and a JSON Schema ajv rejects are both decidable without a record. Rejecting them at publish — packages/lint — kills the rule class outright and never risks bricking a running deployment. This is the "declared = enforced" fix rather than a runtime policy fix.
  2. Write time, to match Script validation rules are silently skipped when their predicate fails to evaluate — fail-open is the wrong direction for a validation #4649. Reuse unevaluableRuleError from rule-validator.ts: reject with reason: 'unevaluable', naming the rule. Note the blast radius differs from Script validation rules are silently skipped when their predicate fails to evaluate — fail-open is the wrong direction for a validation #4649's in an important way — an unevaluable predicate rejects only writes whose data triggers the fault, whereas a broken regex would reject every write touching that field, for as long as the bad metadata is deployed. That is a strong argument for doing (1) first, and only then deciding whether (2) is still worth having as a backstop.

Acceptance sketch

  • A format rule with regex: '([' is rejected at publish/lint with the rule name and the regex error.
  • Same for a json_schema rule whose schema ajv cannot compile.
  • If (2) is also taken: the #4649 — unchanged neighbours tests in rule-fail-closed.test.ts flip, and the module header's "Deliberately NOT changed here" paragraph is updated in the same PR — it is the record of this decision.

Activity

  1. claude commented on Aug 3, 2026

    @claude
    Contributor

    🔒 认领:PM 循环第 5 轮(跨域借用 —— 修复落点经裁定移至 packages/lint,domain:devx)
    会话:session_01NrmBxj8rK2uGCnh9aipjwX(账号 xuyushun441-sys,登记车道 {engine, services})
    分支:claude/issue-4762-static-rule-publish-gate
    Worktree:objectstack-issue-4762
    域:原标 domain:engine,裁定走 issue 路线 1 后落点为 packages/lint —— 按 Domain lanes 借用条款认领:devx 车道 PM 未认领本单、文件面全量申报如下、全局在飞检查已跑。
    文件面:packages/lint/src/validate-expressions.ts(或该包内更合适的规则校验入口,以现状为准)+ 同目录测试;packages/objectql/src/validation/rule-validator.ts 零改动(路线 2 本单不做,既有 fail-open pin 保留)

    方案裁定:路线 1(publish 期拒绝)本单全量;路线 2(运行时 fail-closed)不做,理由用 issue 自己的话:坏 regex 的运行时拒绝会砸掉该字段全部写入,先关作者期大门;运行时背档是否还需要,留维护者定(不挂标签,低紧急度)。
    ⛔ spec/** 零改动;releases/ 零触碰。@xuyushun441-sys 的 devx 会话若对本借用有异议,以你为准,我让行。


    Generated by Claude Code

  2. claude commented on Aug 3, 2026

    @claude
    Contributor

    🔁 接手记录:原 dev agent 因会话额度耗尽被杀(不可恢复),worktree objectstack-issue-4762 与未提交改动完好。按 worktree 接手协议派新 agent 继续 —— 同一认领、同一分支,本条为交接记录,原认领评论继续有效。会话:session_01NrmBxj8rK2uGCnh9aipjwX。


    Generated by Claude Code

  3. claude commented on Aug 4, 2026

    @claude
    Contributor

    ✅ 验收通过 —— PR #5027;落地暂被 #5032 挡住(非本 PR 之过)

    对照实际 diff 复核(9 文件,+880/-7,全部 packages/lint + changeset + 3 行 lockfile;⛔ objectql / spec / protocol.ts / releases 零触碰 —— 运行时 #4649 pin 与模块头原样保留,逐一核实):

    1. 接手纪律:逐 hunk 审计死亡 agent 的遗留(模块头声称的 ajv 选项、RegExp 用法逐字对上运行时源码;「未完成」的嵌套标签修复实际已完成且有测试钉住)—— 保留而非重写,正确。
    2. 裁定路线 1 如实落地且对得硬:门禁用真编译器,且 ajv 选项与运行时同参(有 parity 测试钉住 —— 不同参的门禁会放过运行时拒绝的东西,这正是要防的高一层 declared≠enforced);报错带 rule + object + 配置路径 + 编译器原文。
    3. 两处自主加固都在题内:ajv 进 LAZY_DEPS(内核启动路径契约);MAX_RULE_NESTING_DEPTH=16 防 r.then = r 挂死 lint(os lint 不 parse,walker 吃作者模块的任意对象)。
    4. 端到端非空转:showcase 的 account 对象本身带真 format+json_schema 规则,三个示例 validate 全绿;真 CLI 上坏 regex/坏 schema 各拿到点名报错,exit 1。lint 1080 全过,全仓 typecheck 123 任务全绿。
    5. 衍生已核实:A json_schema validation rule's format keyword is silently IGNORED — ajv runs without ajv-formats, so format: 'email' enforces nothing #5029(ajv 无 ajv-formats,format: email 编译通过但不校验 —— 本 PR 的选项同参性天然抓不到「编译得过」的这半,单开正确);Validate Package Dependencies is red on main — 8 FIXABLE OSV advisories (undici / hono / fast-uri), so every PR inherits a red required-ish check #5032(见下)。

    落地条件:唯一红检是 Validate Package Dependencies,已证实是 main 侧的过期基线(该工作流 main 上每周日定时跑,上次绿于 OSV 通报发布之前;只有碰 lockfile 的 PR 会即时撞上)。#5032 修复合入后,本分支 merge origin/main 重跑该门禁(⚠️ 不可只 rerun —— rerun 复用旧 merge ref,拿不到修好的 lockfile),绿后入队。


    Generated by Claude Code

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

Metadata

Metadata

Labels

bugSomething isn't workingpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loop

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions