Found while surveying #17232 (PR #17500), deriving the row schema of every type: 'repeater' form field. Out of that card's scope, which is about MISSING titles on live keys.
Same offer-vs-door family as #5280 (app's metadata form offered three retiredKey()-retired keys), one layer down: #5280 was the form's DECLARED field list, this is the JSON-Schema-derived row of a repeater table, which the form layout does not enumerate at all.
The measured half — the served schema carries a tombstone as a row property
retiredKey() (packages/spec/src/shared/retired-key.ts) is z.never({ error: () => guidance }).optional().describe('[REMOVED] ' + guidance). Authoring the key is a hard parse rejection.
z.toJSONSchema still emits a property node for it, and it lands in the repeater's items.properties. Measured on origin/main at e758131b39, flow.nodes:
items.properties keys = id, type, label, config, connectorConfig, position,
timeoutMs, inputSchema, outputSchema, waitEventConfig,
boundaryConfig
outputSchema.description = "[REMOVED] `flow.nodes[].outputSchema` was removed in
@objectstack/spec 17.0.0 (audit close-out) ..."
Lit control: inputSchema, the live sibling one line above it, is present and carries no [REMOVED] prefix. Dark control: a key that was never declared at all is absent from the map.
dashboard.widgets carries five more of them, so six tombstones sit in repeater rows across the registry today.
The unmeasured half, named as unmeasured
Whether Studio paints a column header for a { "not": {} } node is an objectui question and was NOT measured here — this session has no console to drive. The console reads items.properties[k].title ?? k to build the header row, and a tombstone is a member of items.properties, so the offer exists in the payload; what the renderer does with it is the half still owed. Both readings are worth having before anything is changed: if it does render, an author is invited to fill a key that is refused at publish; if it does not, the exclusion still belongs in the served schema rather than in each renderer.
Why the spec side is the place to look first
packages/spec/src/kernel/repeater-item-titles.test.ts (landing in PR #17500) already has to exclude these by hand — it filters on the [REMOVED] description prefix — precisely so it does not demand an authoring title for a key the parse door refuses. That exclusion is a local workaround for a payload that should arguably not carry the node at all. One place to decide it, not one per consumer.
Carrier: packages/spec/src/shared/retired-key.ts and packages/metadata-protocol/src/protocol.ts (toJsonSchemaSafe), with the render half in objectui.
Back-links: #17232, PR #17500, #5280.
Generated by Claude Code
Found while surveying #17232 (PR #17500), deriving the row schema of every
type: 'repeater'form field. Out of that card's scope, which is about MISSING titles on live keys.Same offer-vs-door family as #5280 (
app's metadata form offered threeretiredKey()-retired keys), one layer down: #5280 was the form's DECLARED field list, this is the JSON-Schema-derived row of a repeater table, which the form layout does not enumerate at all.The measured half — the served schema carries a tombstone as a row property
retiredKey()(packages/spec/src/shared/retired-key.ts) isz.never({ error: () => guidance }).optional().describe('[REMOVED] ' + guidance). Authoring the key is a hard parse rejection.z.toJSONSchemastill emits a property node for it, and it lands in the repeater'sitems.properties. Measured onorigin/mainate758131b39,flow.nodes:Lit control:
inputSchema, the live sibling one line above it, is present and carries no[REMOVED]prefix. Dark control: a key that was never declared at all is absent from the map.dashboard.widgetscarries five more of them, so six tombstones sit in repeater rows across the registry today.The unmeasured half, named as unmeasured
Whether Studio paints a column header for a
{ "not": {} }node is an objectui question and was NOT measured here — this session has no console to drive. The console readsitems.properties[k].title ?? kto build the header row, and a tombstone is a member ofitems.properties, so the offer exists in the payload; what the renderer does with it is the half still owed. Both readings are worth having before anything is changed: if it does render, an author is invited to fill a key that is refused at publish; if it does not, the exclusion still belongs in the served schema rather than in each renderer.Why the spec side is the place to look first
packages/spec/src/kernel/repeater-item-titles.test.ts(landing in PR #17500) already has to exclude these by hand — it filters on the[REMOVED]description prefix — precisely so it does not demand an authoring title for a key the parse door refuses. That exclusion is a local workaround for a payload that should arguably not carry the node at all. One place to decide it, not one per consumer.Carrier:
packages/spec/src/shared/retired-key.tsandpackages/metadata-protocol/src/protocol.ts(toJsonSchemaSafe), with the render half in objectui.Back-links: #17232, PR #17500, #5280.
Generated by Claude Code