Measured while implementing objectui#8604 (the top-level columns narrowing), on the installed @objectstack/spec 17.4.0, by reading RecordDetailsProps's own shape and parsing probes against it — not transcribed from the card above. Filed here rather than carried on that branch: objectui#8604 is scoped to one key, and both items below are different dispositions.
Same interface as objectui#8583, one level up: that card reconciled RecordDetailsComponentProps.sections[] member-for-member. Nobody has done the same for the top level, and it diverges in both directions.
Item 1 — this type declares layout, which the spec REFUSES by name (a class-c trap)
// packages/types/src/record-components.ts
layout?: 'stacked' | 'inline' | 'compact';
@objectstack/spec declares the same top-level key as a retiredKey(...): removed in 17.0.0 under ADR-0087 D2 (objectstack#6946), because the published auto | custom semantics were never implemented.
Measured: RecordDetailsProps.safeParse({ layout: 'compact' }) is RED, invalid_type at path layout, and the message is the removal prescription itself, beginning "record:details property layout was removed in @objectstack/spec 17.0.0 (ADR-0087 D2)". The control on the same instrument, which would have fired: every other top-level key (aria, columns, fields, hideFields, inlineEdit, sections, showHeader) accepts a plausible value in the identical probe loop — so the refusal is about this key, not about a schema that refuses everything.
⇒ A TypeScript author writes layout: 'compact', tsc is green, and the contract refuses the document at publish. Identical shape and identical direction to objectui#8604, one key over, and it is the last live holdout of the spelling: objectui#3818 removed the renderer's dead branch, and the @object-ui/plugin-detail registry manifest deliberately publishes no layout input and says so in a comment. The published TypeScript face is the only layer still offering it. The spec's own docblock names this mirror by name as carrying "a third spelling, stacked | inline | compact".
⚠️ This is a retirement, not a narrowing — a removal of a published key, with its own conversion and pin obligations. That is why objectui#8604 did not ride it in. There is an in-repo precedent to copy for the changeset shape: .changeset/retire-record-details-section-collapsed.md, which retired sections[].collapsed on this same interface, minor, with FROM/TO prose.
⚠️ One in-repo caller writes it today: packages/types/src/__tests__/p1-spec-alignment.test.ts sets layout: 'stacked' on a RecordDetailsComponentProps literal. It needs to move in the same change.
Item 2 — this type OMITS three top-level keys the spec declares and the renderer honours
hideFields, inlineEdit, showHeader.
Measured the same way: all three are members of RecordDetailsProps's shape and all three accept a plausible value. RecordDetailsRenderer has read all three for some time (schema.inlineEdit ?? true gated by the object's editability, schema.showHeader ?? false passed to the body, and hideFields applied to fields and to every section's fields), and @object-ui/plugin-detail's registry manifest declares all three as inputs — objectui#3808 for hideFields and objectui#4668 for the other two, each filed as the same reverse-direction defect.
⇒ Every layer declares them except the published TypeScript face, so a TypeScript author writing spec-valid, renderer-honoured, registry-published metadata gets TS2353. That is objectui#8583 item 1 exactly, one level up.
Direction, if it is taken: contract-first (Commandment #0.1) says declare what the contract declares, with the spec's own authoring types.
Why one card, two items
That is the shape objectui#8583 used for the same reconciliation on sections[]: one measurement over one key set, two dispositions. Splitting them would put two halves of a single "reconcile this interface's top level against the contract" job in two queues.
⛔ Not claimed, ⛔ not graded — domain:* and priority are triage's.
Refs: objectui#8604 (where this was measured, and the key it narrows) · objectui#8583 / PR #8601 (the same reconciliation on sections[]) · objectui#3818 (the renderer half of layout) · objectui#3808, objectui#4668 (the registry half of the three omissions). None of those covers this surface: objectui#8583 and objectui#3818 are closed, and a targeted search of open cards for this interface returned 35 on-topic neighbours with no hit on the top-level key set — the neighbours are the control that the query was live.
Generated by Claude Code
Measured while implementing objectui#8604 (the top-level
columnsnarrowing), on the installed@objectstack/spec17.4.0, by readingRecordDetailsProps's own shape and parsing probes against it — not transcribed from the card above. Filed here rather than carried on that branch: objectui#8604 is scoped to one key, and both items below are different dispositions.Same interface as objectui#8583, one level up: that card reconciled
RecordDetailsComponentProps.sections[]member-for-member. Nobody has done the same for the top level, and it diverges in both directions.Item 1 — this type declares
layout, which the spec REFUSES by name (a class-c trap)@objectstack/specdeclares the same top-level key as aretiredKey(...): removed in 17.0.0 under ADR-0087 D2 (objectstack#6946), because the publishedauto|customsemantics were never implemented.Measured:
RecordDetailsProps.safeParse({ layout: 'compact' })is RED,invalid_typeat pathlayout, and the message is the removal prescription itself, beginning "record:detailspropertylayoutwas removed in @objectstack/spec 17.0.0 (ADR-0087 D2)". The control on the same instrument, which would have fired: every other top-level key (aria,columns,fields,hideFields,inlineEdit,sections,showHeader) accepts a plausible value in the identical probe loop — so the refusal is about this key, not about a schema that refuses everything.⇒ A TypeScript author writes
layout: 'compact',tscis green, and the contract refuses the document at publish. Identical shape and identical direction to objectui#8604, one key over, and it is the last live holdout of the spelling: objectui#3818 removed the renderer's dead branch, and the@object-ui/plugin-detailregistry manifest deliberately publishes nolayoutinput and says so in a comment. The published TypeScript face is the only layer still offering it. The spec's own docblock names this mirror by name as carrying "a third spelling,stacked|inline|compact"..changeset/retire-record-details-section-collapsed.md, which retiredsections[].collapsedon this same interface,minor, with FROM/TO prose.packages/types/src/__tests__/p1-spec-alignment.test.tssetslayout: 'stacked'on aRecordDetailsComponentPropsliteral. It needs to move in the same change.Item 2 — this type OMITS three top-level keys the spec declares and the renderer honours
hideFields,inlineEdit,showHeader.Measured the same way: all three are members of
RecordDetailsProps's shape and all three accept a plausible value.RecordDetailsRendererhas read all three for some time (schema.inlineEdit ?? truegated by the object's editability,schema.showHeader ?? falsepassed to the body, andhideFieldsapplied tofieldsand to every section'sfields), and@object-ui/plugin-detail's registry manifest declares all three as inputs — objectui#3808 forhideFieldsand objectui#4668 for the other two, each filed as the same reverse-direction defect.⇒ Every layer declares them except the published TypeScript face, so a TypeScript author writing spec-valid, renderer-honoured, registry-published metadata gets
TS2353. That is objectui#8583 item 1 exactly, one level up.Direction, if it is taken: contract-first (Commandment #0.1) says declare what the contract declares, with the spec's own authoring types.
Why one card, two items
That is the shape objectui#8583 used for the same reconciliation on
sections[]: one measurement over one key set, two dispositions. Splitting them would put two halves of a single "reconcile this interface's top level against the contract" job in two queues.⛔ Not claimed, ⛔ not graded —
domain:*and priority are triage's.Refs: objectui#8604 (where this was measured, and the key it narrows) · objectui#8583 / PR #8601 (the same reconciliation on
sections[]) · objectui#3818 (the renderer half oflayout) · objectui#3808, objectui#4668 (the registry half of the three omissions). None of those covers this surface: objectui#8583 and objectui#3818 are closed, and a targeted search of open cards for this interface returned 35 on-topic neighbours with no hit on the top-level key set — the neighbours are the control that the query was live.Generated by Claude Code