You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
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:
the system-column family converges through a re-implementation, and it is the one that silently disagrees with its producer.
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/data — applySystemFields, the producer; stamps the tenant column and the index.
packages/metadata-core/src/injected-system-columns.ts — applyInjectedSystemColumns / resolveInjectedSystemColumns, the read-exit converger that handles the fields map only.
packages/metadata-protocol/src/protocol.ts — governServedItem 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
new SchemaRegistry({ multiTenant: true }), register an object with a title-eligible field as ownership: 'own'.
Put the raw declaration into the metadata service under get('object', name).
Read GET /meta/object/:name through a real RestServer and compare against registry.getObject(name).
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
Filed by the #8268 dev agent, measured while enumerating the stamps that cross the
/metaread-exit materialization seam. Unassigned.Blocked-by: #8268
Symptom
On a multi-tenant deployment,
SchemaRegistry.registerObjectstamps a tenant index onto every object it materializes:GET /meta/object/:nameserves the same object with noindexeskey at all when the by-name read is answered from themetadataservice or asys_metadataoverlay row. Same object, same moment, same host.The measurement
Real
SchemaRegistry+ realObjectStackProtocolImplementation+ realRestServer,metadataservice holding the artifact's raw declaration (serviceMode: 'artifact'),multiTenant: true, diffed key by key:nameFieldis #8268 and is fixed there.indexesis this issue. With #8268's fix in place the same host reduces to exactly one diverging key: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
d5031f6a1was aboutThe read exits converge the #6562 system-column stamp with
applyInjectedSystemColumnsinpackages/metadata-core/src/injected-system-columns.ts. That function is a second implementation of whatapplySystemFields(packages/spec/src/data) does at registration, and the two have drifted:applySystemFieldsinjects the tenant column and its index, whileapplyInjectedSystemColumnsconverges the fields map only.This is worth stating precisely, because it is the general lesson rather than one bug:
__search(/meta/object/:nameanswers the__searchquestion two different ways — drops it for all 22 package objects, keeps it for all 71 platform objects (and the list route keeps it for both) #8038) converges by delegating to the registry (provisionSearchCompanionOnto) and has not drifted.nameField(GET /meta/object/:namedropsnameFieldfor artifact-ingested objects — the third materialization stamp to diverge on this seam #8268) had no converger at all and was simply missing.d5031f6a1fixed the neighbouringgetMetaItemsread 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/data—applySystemFields, the producer; stamps the tenant column and the index.packages/metadata-core/src/injected-system-columns.ts—applyInjectedSystemColumns/resolveInjectedSystemColumns, the read-exit converger that handles the fields map only.packages/metadata-protocol/src/protocol.ts—governServedItemcomposes the converger at every read exit.stripInjectedSystemColumnsandstripMaterializedStampsFromare owed:indexesis a concatenated array undermergeObjectDefinitions, 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
new SchemaRegistry({ multiTenant: true }), register an object with a title-eligible field asownership: 'own'.metadataservice underget('object', name).GET /meta/object/:namethrough a realRestServerand compare againstregistry.getObject(name).byName.indexesisundefined; the registry's is[ { fields: ['organization_id'] } ].The harness in
packages/rest/src/meta-object-materialization-agreement.test.tsalready builds this host — itsmeasure({ serviceMode: 'artifact', multiTenant: true })is the reproduction.Why it matters
indexesis 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;applySystemFieldsstamping anindexedKEY thatFieldSchemarejects — a spec-validity defect on the field, not the table-levelindexesarray). Neither is a twin.nameFielddivergence 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 D9Generated by Claude Code