Skip to content

console 的内嵌索引编辑器与 IndexSchema 漂移:where vs partial、枚举里的 brin —— 阻塞 #4001 对 IndexSchema 的收紧 #5247

Description

@xuyushun441-sys

Blocked-by: #5248

发现于 #4001 批 20(data/object.zod.ts 内层块收紧)的逐站点测量。本 issue 只记录,不在批 20 里修——批 20 因此有意保留IndexSchema 未收紧,是该批 14 个站点里唯一没关的一个。

症状

objectui 的 console 为 object.indexes[] 里的条目自带了一份手抄的 JSON-Schema:

  • packages/app-shell/src/views/metadata-admin/EmbeddedItemEditor.tsxFALLBACK_SCHEMAS.index

它存在的原因是合理的:index内嵌专用子类型,没有自己的元数据类型,框架没有槽位发布它的 schema(文件里的注释也这么写:"Once / if the framework grows an embedded-sub-type registry, this can move server-side too.")。注释同时声称这些 fallback "mirror the framework's Zod definitions" —— 而它已经不再 mirror 了:

console 表单提供IndexSchema 声明后果
where(标题 "Partial-index predicate")partial键名不同,当前被静默丢弃
枚举含 brin['btree','hash','gin','gist','fulltext']值非法,今天就会被拒

为什么它现在是静默失败

编辑器的保存路径(同文件,spliceEmbeddedclient.save(parentType, …))把表单结果拼回父对象再 PUT 整个对象。服务端 saveMetaItem(packages/metadata-protocol/src/protocol.ts)对 body 做 safeParse 并在失败时 422,但逐字保留原 body

IndexSchema 目前是 zod 默认的 .strip,所以管理员在 "Partial-index predicate" 里填的内容:

  1. 保存成功,没有任何提示;
  2. where 作为未声明键被 spec 在每次后续 parse 时丢掉;
  3. 即使它没被丢掉也没用 —— 见下。

两个方向都是死的

partialtype都没有任何 DDL 消费者driver-sqlsyncDeclaredIndexes 只读 name / fields / unique(以及 ADR-0120 D3/D4 之后的 nullSafeColumns);DeclaredIndexInput(packages/plugins/driver-sql/src/schema-drift.ts)的字段列表就是这四个。

注意区分:sql-driver.ts 里确实有读 partial 的地方(introspectIndexes / parseIndexDdl),但那是从数据库自身的 DDL 解析出来做漂移检测的,和声明式元数据里的 IndexSchema.partial 无关。

为什么这会阻塞 #4001

关掉 IndexSchema 会把上面第 1 步变成 422,而 422 打在 console 自己渲染的控件上 —— 这正是 #5114 那一类(区别是这次在发布之前抓到)。

但"先修生产者再关"还不够。因为 partial 本身也是死的,收紧后把作者指向 partial,是一条声称超出平台实际交付的 guidance —— 台账 finding 18 记录的正是本战役已经发过的四条假 guidance。

所以关掉 IndexSchema 至少需要两件事先落地:

  1. 生产者侧(contract-first):objectui 把 where 改成 partial,并把枚举对齐 spec(去掉 brin,或在 spec 里声明它——后者需要有驱动读它)。
  2. ADR-0049 enforce-or-remove:给 IndexSchema.typeIndexSchema.partial 一个结论——要么接上驱动,要么退役。

顺带值得考虑的第三条:这类 fallback schema 本身就是漂移源。框架长一个内嵌子类型注册表(把 index 的 schema/form 发布出去,像 validation 已经通过 HAND_CRAFTED_SCHEMAS.validation 做的那样)能一次性消灭这个复制品。

现状记录在三个地方

  • packages/spec/src/data/object.zod.tsIndexSchema JSDoc(明确写了"不要顺手补完");
  • packages/spec/src/data/object-strictness-batch20.test.ts §4 —— 把当前的 strip 行为钉住,使它改变的那天是有意改变;
  • docs/audits/2026-07-unknown-key-strictness-ledger.mddata/ 行。

复现(当前 main + 批 20 分支上行为一致):

ObjectSchema.parse({name: 'x',label: 'X',fields: {name: {type: 'text',label: 'N'}},indexes: [{fields: ['name'],where: "status = 'open'"}],}).indexes[0].where// => undefined,静默丢弃

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions