Skip to content

GET /meta/object/:name drops the multi-tenant indexes stamp — the fourth materialization stamp on the #8268 seam, and the one whose converger is a second implementation #8375

Description

@os-zhuang

Filed by the #8268 dev agent, measured while enumerating the stamps that cross the /meta read-exit materialization seam. Unassigned.

Blocked-by: #8268

Symptom

On a multi-tenant deployment, SchemaRegistry.registerObject stamps a tenant index onto every object it materializes:

registry.getObject('showcase_account').indexes
=> [ { fields: ['organization_id'] } ]

GET /meta/object/:name serves the same object with no indexes key at all when the by-name read is answered from the metadata service or a sys_metadata overlay row. Same object, same moment, same host.

The measurement

Real SchemaRegistry + real ObjectStackProtocolImplementation + real RestServer, metadata service holding the artifact's raw declaration (serviceMode: 'artifact'), multiTenant: true, diffed key by key:

byName vs registry diverge on keys: indexes, nameField
byName.indexes = undefined
registry.indexes = [ { fields: ['organization_id'] } ]

nameField is #8268 and is fixed there. indexes is this issue. With #8268's fix in place the same host reduces to exactly one diverging key:

byName vs registry diverge on keys: indexes

which is pinned as an EXPECTED divergence in packages/rest/src/meta-object-materialization-agreement.test.ts (MEASURES the un-converged multi-tenant indexes stamp), so this issue's fix will turn that pin red and it should be updated in the same PR.

Root cause — and it is the shape d5031f6a1 was about

The read exits converge the #6562 system-column stamp with applyInjectedSystemColumns in packages/metadata-core/src/injected-system-columns.ts. That function is a second implementation of what applySystemFields (packages/spec/src/data) does at registration, and the two have drifted: applySystemFields injects the tenant column and its index, while applyInjectedSystemColumns converges the fields map only.

This is worth stating precisely, because it is the general lesson rather than one bug:

d5031f6a1 fixed the neighbouring getMetaItems read merge by deleting a second implementation and calling the canonical one. The same move applies here.

Where (anchored on function names, not line numbers)

  • packages/spec/src/dataapplySystemFields, the producer; stamps the tenant column and the index.
  • packages/metadata-core/src/injected-system-columns.tsapplyInjectedSystemColumns / resolveInjectedSystemColumns, the read-exit converger that handles the fields map only.
  • packages/metadata-protocol/src/protocol.tsgovernServedItem composes the converger at every read exit.
  • ⚠️ The write path owes any fix here a counterpart, the same way stripInjectedSystemColumns and stripMaterializedStampsFrom are owed: indexes is a concatenated array under mergeObjectDefinitions, not a last-writer-wins scalar, so a strip that is not exactness-bounded will duplicate entries on a GET to PUT round trip. The landed [P3] Read decorations (_diagnostics, _draft) round-trip into persisted sys_metadata bodies #4326 round-trip pins catch this.

Reproduction

  1. new SchemaRegistry({ multiTenant: true }), register an object with a title-eligible field as ownership: 'own'.
  2. Put the raw declaration into the metadata service under get('object', name).
  3. Read GET /meta/object/:name through a real RestServer and compare against registry.getObject(name).
  4. byName.indexes is undefined; the registry's is [ { fields: ['organization_id'] } ].

The harness in packages/rest/src/meta-object-materialization-agreement.test.ts already builds this host — its measure({ serviceMode: 'artifact', multiTenant: true }) is the reproduction.

Why it matters

indexes is not decoration. A consumer reading the by-name answer concludes the object has no tenant index, which is the input to migration planning, to index-advice tooling, and to any consumer reasoning about query cost. The platform does create the index; only this read denies it.

Dedup

GitHub search: applySystemFields indexes / multi-tenant index / read-exit divergence — nearest neighbours are #8323 (tenant-scoped UNIQUE indexes going global — a driver DDL concern, not a read-serving one) and #6810 (closed; applySystemFields stamping an indexed KEY that FieldSchema rejects — a spec-validity defect on the field, not the table-level indexes array). Neither is a twin. nameField divergence is #8268.

Related

#8268 (the seam and its inventory) · #8038 · #6562 (the 2026-08-08 Option B ruling this converges under) · d5031f6a1 (#8332, the same "two implementations of one job" shape) · ADR-0029 D9


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions