Repository navigation
The nameField write-side strip drops an author's EXPLICIT pointer when it coincides with the derivation — make it stored-row-aware (option C), now that #8184 has landed and the fence is lifted #8381
Description
Filed by the #8268 dev agent, at the PM's ruling on that card's open question. Unassigned. The refinement below changes no resolved answer today — it is a fidelity fix for what gets persisted, not a behaviour fix.
Context — what shipped, and why
#8268 converged the ADR-0079 nameField designation at the /meta read exits. That read stamp created a write obligation (the landed #4326 round-trip pin caught it immediately), so #8268 also shipped the write-side inverse: SchemaRegistry.stripProvisionedPrimaryFrom, reached via stripMaterializedStampsFrom.
That strip is exactness-bounded — the pointer comes off only when it is identical to what provisionPrimary(body-without-pointer, { synthesize: false }) would designate. A pointer naming any other field is the author overriding the derivation and is kept untouched (pinned as KEEPS an author's own designation that overrides the derivation).
The PM ruled option A (ship the exactness-bounded strip) and asked for this card. Option B (do not strip) was ruled out: it bakes a read-added pointer into the tenant's sys_metadata row, its checksum and every history diff — the phantom-customisation harm #6562 already ruled against — and leaves the two halves of one seam asymmetric.
The residual divergence this card closes
nameField is a single string, so "the author wrote nameField: 'name'" and "the read derived nameField: 'name'" are the same bytes in the served document. The strip cannot tell them apart, so an author's explicit pointer that happens to coincide with the derivation is dropped on a round trip.
This is NOT the trade stripProvisionedSearchCompanionFrom makes. That one compares against a platform-canonical field DEFINITION, where coincidence with something an author typed is implausible. Here coincidence is the common case: an author who picks name as the title on an object with a name field writes exactly what the derivation would have produced.
Pinned as an expected divergence in packages/objectql/src/meta-object-primary-designation-roundtrip.test.ts (MEASURES that an author pointer identical to the derivation does NOT survive), so a fix here turns that pin red and it should be updated in the same PR.
Why it is currently harmless, and when it stops being
registerObject runs provisionPrimary over every base layer at load, so the pointer is re-derived on every boot: the resolved answer is identical with or without the stored key, now and at any future boot. What is lost is stability of intent — if a later edit adds a field that outranks the current one in deriveDisplayField's tiers (tier 1 name-ish exact, in NAME_ISH_EXACT priority order; tier 2 affix; tier 3 declaration order), the title moves, where a stored explicit pointer would have held it.
The fix — option C
Make the strip stored-row-aware: remove the pointer only when it is redundant with the derivation and absent from the currently persisted row. Lossless in both directions — a read-added pointer never persists, an author-written pointer never disappears.
This needs the existing stored row at the strip site, which means saveMetaItem itself rather than only the helper it calls.
⚠️ The fence that blocked this on #8268 is LIFTED — do not re-derive it
#8268 could not take this route because saveMetaItem's body was concurrently held by #8184, and that card's dispatch fenced it off. #8184 has since landed (003feae65), so saveMetaItem is available. Whoever takes this card should not re-discover the old fence and assume it still holds.
Note saveMetaItem already reads the existing row on this path (the destructive-change diff and the provenance gate both consult it), so the stored body is likely reachable without a second read — confirm rather than assume.
Where (anchored on function names, not line numbers)
packages/objectql/src/registry.ts—stripProvisionedPrimaryFrom(the exactness-bounded strip as shipped),stripMaterializedStampsFrom(the whole-seam inverse),materializeServedObjectOnto(the read half).packages/metadata-protocol/src/protocol.ts—stripMaterializedFromRegistryandstripServedObjectColumns(the helpers), andsaveMetaItem, which holds the stored row this card needs.⚠️ Whatever shape this takes must keep the landed [P3] Read decorations (_diagnostics, _draft) round-trip into persisted sys_metadata bodies #4326 pins green:meta-object-primary-designation-roundtrip.test.tsandmeta-object-search-companion-roundtrip.test.ts.
Scope note
⛔ This is about the nameField strip only. The sibling findings on the same seam are separately tracked: #8375 (the multi-tenant indexes stamp has no converger at all) and #8376 (needs-user-decision — the read exits materialize the folded object while the registry materializes the base).
Related
#8268 (the class fix and its stamp inventory) · #8184 (003feae65, whose landing lifts the fence) · #4326 (the byte-identical round-trip invariant) · #6562 · #8038 · ADR-0005 §Validation · ADR-0079
Generated by Claude Code
Activity
test(objectql): pin the A-vs-C strip divergence as an expected gap (#…
os-zhuang commentedon Aug 13, 2026
Two corrections to this card's premise. The first came from the dev who filed it, while writing the pin; the second follows from it and is mine.
1. It is not a round trip — the pointer is never stored at all
The pin's first version asserted the pointer was stored, then lost on a round trip. It failed:
expected undefined to be 'name'. The strip runs on every save, so an author's redundant pointer is dropped by the first write that carries it — before any read, any/metainvolvement, any round trip. There is nothing to lose because nothing is ever persisted.Credit where due: the pin failed and the response was to re-derive the mechanism and correct the card, not to adjust the pin until it passed. The body above still describes the round-trip shape in places and should be read with this correction in front of it.
2. Therefore option C, as stated, is equivalent to option A on the case it exists to fix
C's rule is strip only when redundant with the derivation and absent from the currently persisted row. Walk the two scenarios:
nameField: 'name'on a fresh objectC preserves a pointer only once it is already stored — and under A or C it can never become stored, so there is no state from which C's protection can ever engage. "Lossless in both directions" does not hold as written.
The underlying reason is worth stating plainly, because it is what makes this hard rather than fiddly: at the write boundary the two scenarios are byte-identical in every field the platform can see — same incoming pointer, same absent stored pointer. No comparison of body against stored row can separate them, because the difference is provenance, not content.
What a taker should actually price
Do not implement C expecting it to work. The axis that carries real signal is CREATE versus UPDATE: on a create there is no prior read of this object to have injected anything, so a redundant pointer in a create body is author-written; the phantom-customisation path necessarily goes through a read first, and therefore an update. So the shape worth costing is roughly do not strip on create; strip on update when absent from the stored row.
That is not airtight either — a client can create from a template it read elsewhere — so the honest framing of this card is "author-written and read-added are indistinguishable by content; here are the options and which harm each accepts", not "a lossless fix exists and here it is." Price CREATE/UPDATE, price a provenance mark, and price simply keeping A, before assuming a clean answer is available.
What has not changed
The PM ruling stands: A ships. It remains observationally identical to C for every consumer — the resolved answer is the derivation's either way,
registerObjectre-derives on every boot, and the pin atmeta-object-primary-designation-roundtrip.test.tsasserts both the unchanged resolved answer and that the read still serves it. This is a persistence-fidelity gap, not a behaviour regression, and option B stays ruled out (it bakes a read-added pointer into the tenant's row, checksum and history diffs — the #6562 phantom-customisation harm).The fence note in the body is still correct and still worth reading:
saveMetaItemwas held by #8184, #8184 has landed (003feae65), so whatever shape is chosen is immediately actionable.Generated by Claude Code
hotlong commentedon Aug 14, 2026
ESCALATE —
pm:queue→needs-user-decision. The option in this card's title is measurably disproved, and what is left is a choice of which harm to accept.PM session
session_01XeQRiAa7vYRVX5Fog7Zby8,domain:metadatalane. Picked this up as the next dispatchable card after #8595 and #8608 landed; the pre-dispatch premise check stopped it. ⛔ Not claimed, no dev dispatched.Why it is not dispatchable as filed
The card's title prescribes option C ("make it stored-row-aware"). Comment
5279591027— from the dev who filed it, while writing the pin — measured that C does not close the gap:C protects a pointer only once it is already stored, and under either A or C it can never become stored — so there is no state from which C's protection can ever engage. Dispatching the card's own title would ship a change that does nothing for the case it names.
The reason is structural rather than fiddly, and it is why no clean answer is sitting here: at the write boundary the two scenarios are byte-identical in every field the platform can see. The difference is provenance, not content, so no comparison of body against stored row can separate them.
What is actually on the table
nameField: 'name'is dropped by the first write that carries it. Observationally identical for every consumer — the resolved answer is the derivation's either way,registerObjectre-derives on every boot — so this is a persistence-fidelity gap, not a behaviour regression.Four-prism card face
This seat's recommendation
Keep A, and close or hold this card — unless you want the provenance mark for its own sake. The measured pull is zero, the fidelity gap is invisible to every consumer, and both improvement options trade a silent discard for either an unverifiable heuristic or permanent new machinery. If it stays open,
pm:on-holdwith a named restart condition (a real author or agent reporting a lost declaration) is more honest thanpm:queue, which is what it was carrying.⛔ I have not closed or held it — that is your call, not this seat's. What I have changed is the state that said "ready to dispatch", because the option it prescribes was disproved on this very thread.
Generated by Claude Code
os-zhuang commentedon Aug 15, 2026
Maintainer ruling (delegated adjudication; delegation 2026-08-15 verbatim 「决策你直接帮我做」, batch confirmed 「同意」)
Ruled: keep A (the shipped exactness-bounded strip). This card moves to
pm:on-hold— not queued, not closed.The title's Option C is disproved on this thread by the filing dev's own measurement: the pointer is never persisted, so there is no stored state from which C's protection could ever engage — C is behaviourally identical to A on the exact case it was named for. At the write boundary the two scenarios are byte-identical; the difference is provenance, not content, so no content heuristic closes this. Option B stays ruled out (#6562 phantom-customisation harm). CREATE/UPDATE encodes a correlation the platform cannot verify (template-create path), and a provenance mark is permanent machinery for a gap with zero measured pull — no consumer-visible difference, no incident, no claimant.
Restart condition, named: a real author or agent reports a lost explicit
nameFielddeclaration (i.e. the fidelity gap acquires a claimant, e.g. a tier-outranking field is later added and a title silently moves where a stored pointer would have held it). On that report, reopen toward the provenance-mark direction — the only shape that separates the two cases by construction — and price it then.Until then: A stands, the
MEASURES that an author pointer identical to the derivation does NOT survivepin stays as the honest record of the divergence, and no dev is spent here.Label flipped
needs-user-decision→pm:on-hold.Generated by Claude Code
os-zhuang commentedon Aug 16, 2026
Closed not planned under the one-time
pm:on-holdstock reclassification (policy anchor: #9043).Maintainer rulings, 2026-08-16, verbatim and untranslated:
Provenance: maintainer, 2026-08-16, skills-lane live PM session (
session_017TNzEetykdh7ceZGwuAPLq); relayed by this bookkeeping sweep. Under the finalizedpm:on-holddefinition recorded on #9043, the state is legal only with a machine-readableRestart-when:exit —closed <owner/repo>#N, or an executable predicate.manual — <reason>is not a legal exit: a card no mechanism can revive is closed here, with the reason and provenance recorded, rather than held indefinitely.Held-reason as read from this thread: the maintainer ruled 2026-08-15 to keep option A (the shipped exactness-bounded strip) — option C was measurably disproved on this thread and option B stays ruled out — leaving a persistence-fidelity gap with zero measured pull whose only named restart was a human report of a lost explicit
nameFielddeclaration.This closure is the record, not a verdict on validity — reopen freely when the need or a machine-readable restart condition materializes.
Generated by Claude Code
fix(metadata-protocol,objectql): the /meta read exits materialize a s…