Skip to content

GET /meta/object/:name drops nameField for artifact-ingested objects — the third materialization stamp to diverge on this seam #8268

Description

@huangyiirene

Filed by the PM on behalf of the #8038 dev agent, which measured this but could not file it: its container was 403 on every GitHub API call. Recovered over the git transport as .os-handover/8038-handover.md on branch claude/issue-8038-handover. The text below is the dev's, verbatim in substance.

Unassigned. ⚠️Read "Why this may not be a separate card" before assigning.

Symptom

For an object whose by-name read is answered from the metadata service (an artifact-booted deployment), GET /meta/object/:name serves nameField: undefined, while GET /meta/object and the SchemaRegistry's own resolved schema serve nameField: 'name'same object, same moment, same host. Nothing in the response tells a caller which answer they got.

nameField is the ADR-0079 display/title designation. A consumer reading the by-name answer concludes the object has no designated title field, and every title-rendering decision derived from it (forms, record headers, lookup labels, resolveDisplayField consumers) is made against a document the platform itself does not agree with.

The measurement

Found incidentally while measuring #8038, on that card's reproduction host: a real SchemaRegistry, a real ObjectStackProtocolImplementation, a real RestServer, with the metadata service holding the artifact's raw declaration (serviceMode: 'artifact').

⭐ The dev diffed the two served bodies key by key rather than comparing only the fields map — "precisely so a second divergence could not hide behind the one I was sent for." That is why this exists:

### artifact host
byName vs listed diverge on keys: fields,nameField
byName vs registry diverge on keys: _diagnostics,fields,nameField
byName.nameField = undefined registry.nameField = 'name'

fields was #8038 (__search). nameField is this issue. (_diagnostics is a read decoration, benign.)

Control, same run, same probe: on the bridged host (seeded from registry.getAllObjects()) and the absent host (no metadata service, read falls through to the registry), the same comparison printed diverge on keys: (none) and byName.nameField = name. ⇒ The probe can see agreement when agreement exists, and the divergence tracks the service copy specifically.

Root cause — identical to #8038

The by-name read resolves sys_metadata overlay → MetadataService → SchemaRegistry, and only the last link has been through the registry's object-materialization seam.nameField is written by provisionPrimary(schema, { synthesize: false }) — the line immediately aboveprovisionSearchCompanion inside SchemaRegistry.registerObject, under the same base-layer gate. The artifact copy is the author's declaration captured before that seam runs, so it carries neither stamp.

Where (anchored on function names, ⛔ not line numbers)

Reproduction

  1. Build a SchemaRegistry, register an object with a title-eligible field as ownership: 'own'.
  2. Put the raw declaration (⛔ not registry.getObject(name)) into the metadata service under get('object', name) — the artifact-ingest shape.
  3. Read both routes through a real RestServer.
  4. byName.item.nameField is undefined; GET /meta/object and registry.getObject(name).nameField are both 'name'.

The harness in packages/rest/src/meta-object-search-companion-agreement.test.ts (landed with #8038 as a6cd2c152f) already builds exactly this host — its measure({ serviceMode: 'artifact' }) is a one-line extension from asserting nameField agreement too.

⚠️ Why this may not be a separate card — reconcile with #8037 before assigning

The dev's own caveat, and it is a good one: nameField is a scalar property that mergeObjectDefinitions resolves last-writer-wins, so it may already fall inside #8037's enumeration of every property class the fold touches (landed as 3a27c46edb, PR #8258). provisionSearchCompanion is deployment-gated (OS_SEARCH_PINYIN_ENABLED) while provisionPrimary runs unconditionally, and they land in different parts of the document — but the overlap with #8037 is real.

⭐ It deliberately did not fix this inside #8038's PR: "fixing it in two places concurrently is worse than fixing it in one." Correct call.

⭐ Suggested framing — rule on the CLASS, not the third instance

Three materialization stamps have now been found diverging on this one path, one at a time:

#StampStatus
#6562injected system columns (created_at, owner_id, organization_id, …)ruled 2026-08-08, Option B — converge at read exits
#8038__search companion columnlanded a6cd2c152f
thisnameField (provisionPrimary)unfiled until now

A single "materialize this base the way the registry materializes its own" seam — provisionPrimary + provisionSearchCompanion behind one registry method, applied at the read exits — closes the class and stops a fourth stamp arriving as a fourth card. #8038 built half of that seam already.

This is the same lesson the #7556#8027#8037 family taught in the other direction: pinning one property class proves nothing about the others.

Dedup

  • Dev side (repo only, GitHub search was 403 for it):nameField in metadata-protocol/rest → 7 hits, none about route agreement; provisionPrimary → exactly one production call site (SchemaRegistry.registerObject), no read-exit convergence anywhere; changesets reviewed. Nearest neighbour .changeset/adr0029-d9-object-overlay-contributor-layer.md examined and ruled not a twin — it concerns the registration-side gate (which layers get stamped at registerObject time), not a read serving a copy that never went through registerObject.
  • PM side (GitHub search):nameField → 0 open issues; provisionPrimary / provisionSearchCompanion / materialization → 5 hits, all unrelated (permission-model parking lot, CEL binding fullness, Studio coverage, two seat posts).

No twin found.

Related

#8038 (a6cd2c152f) · #8037 (3a27c46edb) · #7556 · #8027 · #6562 · ADR-0079 · ADR-0029 D9

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions