Skip to content

refactor(spec)!: retire indexes[].typeindexes[].partial —— 两个零 DDL 消费者的声明键 (#5248, #4943) - #5842

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-5248-index-type-partial-retire
Aug 6, 2026
Merged

refactor(spec)!: retire indexes[].typeindexes[].partial —— 两个零 DDL 消费者的声明键 (#5248, #4943)#5842
os-zhuang merged 2 commits into
mainfrom
claude/issue-5248-index-type-partial-retire

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #5248
Closes #4943

按 2026-08-06 维护者裁决(#5248 评论区):typepartial 双双 remove,走 spec-property-retirement 全套。

修订(PM 裁定,已落地):conversion 的 toMajor 由 18 改为 17,并入既有 step17;墓碑处方改为 "removed in @objectstack/spec 17.0.0" + os migrate meta --from 16,与其余 toMajor-17 条目一致。changeset 维持 major。本 PR 正文已按修订令重写,不再有"待确认"项。


前提复核(rebase 到 origin/main @ 4615a1866)

issue 正文的前提成立,但有一处路径已过时:

issue 正文 实测
packages/plugins/driver-sql/src/sql-driver.ts 已迁移到 packages/drivers/driver-sql/
type/partial 无 DDL 消费者 ✅ 确认

逐站点核对:

  • SqlDriver.syncDeclaredIndexes(sql-driver.ts:5279)对每个声明索引只解构 const { name, columns, unique } = norm,再走 knex 的 table.unique(columns, { indexName: name }) / table.index(columns, name) —— knex 两个 builder 都无法表达访问方法或谓词。
  • normalizeDeclaredIndex(schema-drift.ts:970)只读 fields / unique / nullSafeColumns / name;DeclaredIndexInput(:946)的接口字段就是这四个,从来没有 type / partial
  • 全仓检索:声明面的这两个键在 packages/** / examples/** 无任何读取点。

消费者清点表

声明面消费者 结论
name syncDeclaredIndexes(索引名) 保留
fields syncDeclaredIndexes(列) 保留
unique syncDeclaredIndexes + ADR-0120 D3/D4 的 'organization' scope 保留
type ❌ 无 退役
partial ❌ 无 退役

type 是更响的一半:它带 .default('btree'),所以会出现在每一次 parse 的输出里 —— 一个从未影响过任何语句的访问方法旋钮,被渲染成生效配置(ADR-0078 形状)。这一点甚至被钉进了测试:service-realtimesys-presence.object.test.ts 断言 type: 'btree',而 sys-presence.object.ts 根本没声明过 type —— 断言钉的是默认值物化出来的幻影。

partial 是更危险的一半:它读起来像正确性控制。平台自己的 sys_metadata 就用它声明 overlay 唯一性,而声明单独物化出来的是无谓词的全量唯一索引。

⛔ 勿伤确认

sql-driver.tsintrospectIndexes / parseIndexDdl / schema-drift.tsisSyncReproducibleIndex 读的 partialpartial: boolean,从数据库自身的 CREATE INDEX DDL 反向解析而来,供漂移检测豁免 DB 自建的部分索引。方向相反(DB → 我们,而非我们 → DB),类型也不同(boolean vs string 谓词),一个字未动DeclaredIndexInput 本就无这两键,未动。


先证红 → 转绿闭环

方向预先写死:退役前两键 parse 照收(绿),墓碑落地后携带两键的 parse 拒收且报文含处方(红转绿)。

退役前(基线实测)

BEFORE both-keys accepted: true
BEFORE output: {"name":"idx_a","fields":["a"],"type":"gin","unique":false,"partial":"state = 'active'"}
BEFORE minimal output (materialized default): {"fields":["a"],"type":"btree","unique":false}

退役后

### type only: success=false
  [type] `indexes[].type` was removed in @objectstack/spec 17.0.0 (#5248, ADR-0049) — no driver ever read it. …
### partial only: success=false
  [partial] `indexes[].partial` was removed in @objectstack/spec 17.0.0 (#5248, #4943, ADR-0049) — …
### live keys still parse: true {"name":"idx_a","fields":["a"],"unique":"organization"}
### minimal output (no more btree default): {"fields":["a"],"unique":false}

反向验证(把删掉的肢体接回去) —— 预测方向是标准的 Red:把两键恢复成活键后,新增的钉子应当全红。实测 7 条全红,且失败原因正确(expected true to be false,键又被接受了),不是附带原因:

× REJECTS `type`, with the fix and the reason in the message
× REJECTS `partial`, naming the database-layer replacement
× points at the CLI conversion rather than naming the conversion id
× the live keys are untouched — the retirement is surgical
× no longer materializes a phantom `btree` into every parsed index
× rejects the retired keys through a whole object too, not just the sub-schema
× the console drift outlived BOTH spellings: `where` strips, `partial` is now retired
Tests  7 failed | 171 passed (178)

恢复后重跑 178 passed。

DDL 等价性 —— 实测而非断言

生产者翻转的核心主张是"删声明零行为变化"。这条主张被钉成了常驻测试(packages/drivers/driver-sql/src/declared-index-retired-keys.test.ts),对真实 SQLite 存储的 DDL 逐字节比对,而不是比对中间结构:

WITH partial    -  CREATE UNIQUE INDEX `idx_sys_metadata_overlay_active` on `t` (`type`, `name`, `organization_id`, `package_id`)
WITHOUT partial -  CREATE UNIQUE INDEX `idx_sys_metadata_overlay_active` on `t` (`type`, `name`, `organization_id`, `package_id`)
WITH type=gin   -  CREATE INDEX `idx_tags` on `t` (`tags`)
WITHOUT type    -  CREATE INDEX `idx_tags` on `t` (`tags`)

两两完全相同,且都不含 WHERE —— 这正是谓词一直无效的原因。

生产者翻转清单

文件 索引 处理
packages/metadata-core/src/objects/sys-metadata.object.ts idx_sys_metadata_overlay_active partial;注释改写为诚实版本
packages/metadata-core/src/objects/sys-view-definition.object.ts idx_sys_view_def_active partial;注释改写(两个 issue 都没点名的第二个生产者)
packages/services/service-realtime/.../sys-presence.object.test.ts 去掉 type: 'btree' 幻影断言
packages/drivers/driver-sql/.../sql-driver-overlay-index-drift.test.ts 声明面 fixture 跟随生产者(跨包,按规则的消费半径扫出来的)

sys_metadata 的注释此前写着"this declaration is the fallback shape for drivers without the runtime migration",隐含 fallback 也是活跃行范围的 —— 实际不是。已改写为:声明一直物化的就是无限制唯一索引,真正交付活跃行范围的是 metadata-protocolensureOverlayIndex 运行时迁移。

sys_view_definition 情况更重:它没有任何等价的运行时迁移,所以"活跃行唯一"在任何一层都没实现。这是既有行为缺口、修它属行为变更,不在本单,已单独立 #5839

退役套件

未动 docs/adr/0005-...md:其 partial 代码块是 ADR 当时的历史快照(同一块里还留着早已退役的 project_id/scope),且紧邻的正文本就写着"Drivers ignore indexes declarations on synced tables today, so a new idempotent migration is provided"—— 与本次退役理由一致。按 Prime Directive #13,历史决策记录不在代码 PR 里改写。


验证(retarget 后复跑)

结果
spec 十一道闸门(liveness / empty-state / authorable-surface / docs / api-surface / spec-changes / upgrade-guide / skill-refs / skill-docs / skill-examples / generated) 11/11 PASS
pnpm --filter @objectstack/spec test 320 files / 8192 tests passed
driver-sql / metadata-core / service-realtime test 75 files / 996 tests passed(4 files / 46 skipped)
typecheck(spec / driver-sql / metadata-core) 全部 Done
pnpm check:i18n OK — 9 packages 全部 in sync
node scripts/check-nul-bytes.mjs OK(另对改动文件做了越过闸门盲区的 [\x00-\x08\x0b\x0c\x0e-\x1f] 自扫,零命中)

链路落点确认(生成物自证):spec-changes.json 的 17 段新增本条 conversion;升级指南 object-index-type-partial-removed 行标记 retired — migrate meta only;step17 rationale 已含本次退役段落。

check:i18n 首跑报 9 个 "extract failed — no output",追查为新建工作树里 @objectstack/lintpackages/cli 未构建(闸门用的是 bin/run.js 即构建产物),补构建后全绿 —— 与本改动无关。表单面无 indexes[] 子键输入项,故 i18n bundle 本就无需变更。


相关

🤖 Generated with Claude Code

https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D

@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 6, 2026 8:41am

Request Review

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/metadata-core, @objectstack/spec.

110 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-core, packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/metadata-core, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/metadata-core, @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation protocol:data tests tooling labels Aug 6, 2026
claude added 2 commits August 6, 2026 07:47
…, #4943)

Both keys were authorable with zero DDL consumers. `syncDeclaredIndexes`
creates every declared index through knex's `table.index()` /
`table.unique()`, and the drift differ's `DeclaredIndexInput` carries
`name`/`fields`/`unique`/`nullSafeColumns` — so an authored `type` selected
no access method and an authored `partial` produced a FULL index with the
predicate silently discarded. `type` additionally carried `.default('btree')`,
materializing an inert knob into every parse output (ADR-0078).

Retirement kit: `retiredKey()` tombstones at the bottom of the IndexSchema
shape (#5606 renderer note), ADR-0087 conversion
`object-index-type-partial-removed` opening the protocol-18 step, both real
producers flipped (`sys_metadata`, `sys_view_definition`) with their comments
corrected, published skill + docs + liveness note + generated baselines.

DDL equivalence is proven against the statements SQLite actually stores, not
asserted: packages/drivers/driver-sql/src/declared-index-retired-keys.test.ts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
`toMajor: 18` → `17`; `step18` deleted and
`object-index-type-partial-removed` wired into the existing step-17
chain (its rationale extended). Tombstone prescriptions now say
"removed in @objectstack/spec 17.0.0" and point at
`os migrate meta --from 16`, matching every other toMajor-17 entry.
Skill, docs, liveness note and changeset retargeted; generated
artifacts (spec-changes.json, upgrade guide, reference docs)
regenerated. Changeset stays `major`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
@os-zhuang
os-zhuang force-pushed the claude/issue-5248-index-type-partial-retire branch from 1e37ac2 to 62ec212 Compare August 6, 2026 08:41
@os-zhuang
os-zhuang marked this pull request as ready for review August 6, 2026 08:53
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 72c3c86 Aug 6, 2026
26 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5248-index-type-partial-retire branch August 6, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/l tests tooling

Projects

None yet

2 participants