Skip to content

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

Description

@xuyushun441-sys

发现于 #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

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions