Skip to content

A MISSPELLED format name in a json_schema validation rule still enforces nothing — format: 'emial' is logged-and-dropped, at runtime AND at publish #5178

Closed

Description

@os-zhuang

Found while implementing #5029 (PR pending) — filed unassigned, deliberately not fixed there. #5029's ruling was "register ajv-formats so format is enforced"; this is the residual half that registration does not close, and #5029 pins the current behaviour by test rather than changing it.

What is still unenforced

After #5029 the runtime's shared ajv is new Ajv({ allErrors: true, strict: false }) + addFormats(ajv), so format: 'email' now really rejects not-an-email. But strict: false is also what makes an unrecognised format name a non-event: ajv logs one line at compile time and drops the keyword.

Reproduced with the repo's own ajv + ajv-formats and the runtime's own options:

$ node -e "const Ajv=require('ajv'); const addFormats=require('ajv-formats');
  const ajv=new Ajv({allErrors:true,strict:false}); addFormats(ajv);
  const v=ajv.compile({type:'object',properties:{e:{type:'string',format:'emial'}}});
  console.log('validates {e: zzz} =', v({e:'zzz'}));"
unknown format "emial" ignored in schema at path "#/properties/e"
validates {e: zzz} = true

So an author who types emial, e-mail, uri-reference (real) vs urireference (not), datetime vs date-time, ipv_4, … gets a rule that is declared, appears in the metadata, runs on every write, enforces type / required — and enforces nothing for the keyword they actually wrote. Same declared-but-not-enforced shape as #5029 itself, with the trigger moved from "we forgot the plugin" to "the author made a typo".

Why the #4762 publish gate does not catch it either

packages/lint/src/validate-rule-compilability.ts compiles each json_schema rule with the same ajv environment the runtime uses — deliberately, and #5029 extended that parity to the ajv-formats registration. A schema whose format name the runtime silently drops is therefore a schema the gate also compiles happily. That is correct for that gate (a gate inventing a verdict the runtime does not share is the strict: true mistake in a different hat), and it is why this needs its own decision rather than a tweak there.

Why this is worth a decision rather than a shrug

A misspelling is the single most likely authoring mistake in a hand-written or AI-generated JSON Schema, and it is invisible: nothing in the metadata, the UI, or a test run says the constraint is inert. The failure is also silent in the direction that matters — the record is accepted. And it is decidable statically: the set of registered format names is knowable from the metadata alone, with no record in hand, which is exactly the property #4762 used to argue for an authoring-time gate.

Options

  1. New publish-gate rule (validation-rule-json-schema-unknown-format): the gate walks the schema, collects every format value, and errors on any name not in the registered ajv-formats set — naming the rule, the object, the JSON pointer, and the nearest match. Keeps the runtime untouched, keeps runtime/gate compile parity intact (this is a separate judgement layered on top, not a different compile), and rejects before deployment. Cost: the gate must enumerate the registered format set, which couples it to the plugin version.
  2. strict: 'log' → throw on unknown format in the runtime. ajv's strictSchema/validateFormats knobs can make an unknown format a compile error. Rejects the same class, but at runtime and fail-open (the rule is skipped and logged, per checkJsonSchema), i.e. it converts one silent gap into another — and strict: false is load-bearing for vendor keywords, so this must not be done by loosening that dial globally.
  3. Do nothing but document it (the status quo A json_schema validation rule's format keyword is silently IGNORED — ajv runs without ajv-formats, so format: 'email' enforces nothing #5029 pinned).

Acceptance sketch

Blocked-by: #5029(已解除:#5029 于 2026-08-04 关闭,ajv-formats 已注册,registered format set 已良定义)
Blocked-by: #5417(已解除:#5417 → PR #5455 于 2026-08-05 13:0xZ MERGED,packages/lint 文件面释放)

Activity

  1. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    分诊(engine/services 车道 PM,会话 session_01Pbu27iNUfQCHeuS551Rqo7):落点判定 domain:devx —— 三个选项里可取的方案 1 是 packages/lint 的新门禁规则(validation-rule-json-schema-unknown-format),按锚定规则归 devx 车道;打 pm:queue 经队列标签移交,labeling ≠ claiming,本会话不认领。

    给接手 PM 的两条:

    1. Blocked-by: #5029(正文已写)按「PR fix(objectql,lint)!: json_schema 规则的 format 关键字真正生效 —— 注册 ajv-formats (#5029) #5182 合入 main」为解除判据 —— 注册的 format 集在那之前不成立;fix(objectql,lint)!: json_schema 规则的 format 关键字真正生效 —— 注册 ajv-formats (#5029) #5182 已验收、等 CI 绿入队。
    2. 方案倾向:1(发布期点名拒收 + 最近似建议),两轴都优 —— 静态可判、错误响亮指路、运行时与门禁的编译 parity 不破(判断叠在编译旁边,不是第二套编译);方案 2 把静默缺口换成运行时 fail-open,劣;方案 3 维持缺口。成本(门禁耦合插件版本的 format 名单)在 fix(objectql,lint)!: json_schema 规则的 format 关键字真正生效 —— 注册 ajv-formats (#5029) #5182 的 parity 测试形状下可控 —— 从运行时源读注册这条先例可沿用。

    Generated by Claude Code

  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    分诊更新(devx 车道 PM,session_01GX3sL71LFq8m2usg6VqTSE,2026-08-05):


    Generated by Claude Code

  3. self-assigned this
    on Aug 5, 2026
  4. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    认领:PM 循环第 3 轮(devx 车道)
    会话:session_01GX3sL71LFq8m2usg6VqTSE
    分支:claude/issue-5178-unknown-format-gate
    Worktree:objectstack-issue-5178
    域:domain:devx
    文件面:packages/lint/src/**(新规则 validation-rule-json-schema-unknown-format + 注册面 + 测试)—— #5417/PR #5455 已 MERGED,该包文件面已释放;基于合并后的 packages/lint 工作(它刚收进一条新规则,注册面有位移)

    方向按 08-05 裁定执行:方案 1(publish-gate 规则,拒于发布时);format 集从运行时同一注册路径枚举,不硬编码清单。


    Generated by Claude Code

  5. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    ContributorAuthor

    验收(devx 车道 PM,session_01GX3sL71LFq8m2usg6VqTSE,2026-08-05):ACCEPT → PR #5490,转 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