Found while landing objectui#7772 (branch claude/issue-7772-page-block-kanban-groupby-control). Filed unassigned and NOT touched there — objectui#7772's triage comment fenced this off explicitly ("Whether that gate should cover block-config is its own card"). Measured on origin/main 0c8dbc49.
What
packages/app-shell/src/views/metadata-admin/previews/block-config.ts (BLOCK_CONFIG) is the page-block inspector's field table: one entry per authorable property, and its own file header states the rule it lives by —
Keep each field name aligned with the property name the corresponding renderer reads.
Nothing mechanical checks that.
scripts/check-designer-field-key-parity.mjs judges PAYLOAD_SHAPES — the field / object / permission payloads. Ran it on this branch: exit 0, and its own output names what it covers. It never reads BLOCK_CONFIG.
apps/console/src/__tests__/registry-inputs-spec-parity.test.ts judges the REGISTRY inputs declarations (objectui#8067 / objectui#8068), a different table on a different face.
- What does exist is per-block prose pins inside
previews/__tests__/block-config.test.ts, written one at a time as defects were found: page:header.icon (objectui#3829), page:accordion.title + items value (objectui#5212), the global_nav option (objectstack#6888), and now object-kanban (objectui#7772).
That is the same VOLUNTARY shape objectui#8068 argued was not good enough for the registry inputs: three keys did it right, the fourth did not, and nothing noticed the fourth. Here the pins are also voluntary, and each one exists only because a human or an agent tripped over the defect first.
Why this is worth a card rather than a note — the measured cost
objectui#7772 is not a stale-key defect. BLOCK_CONFIG['object-kanban'] offered four controls (objectName / groupField / titleField / cardFields) while ObjectKanbanSchema declared:
groupBy REQUIRED (packages/types/src/objectql.ts:2765, no ?; packages/types/src/zod/objectql.zod.ts:1001, no .optional()) — with no control at all;
groupField a retirementTombstone() (objectql.zod.ts:1002) and ?: never on the TS face since objectui#7322 — as the only control able to set grouping.
Measured, not inferred. ObjectKanbanSchema.safeParse on the node those four controls produce reports TWO issues:
{ code: 'invalid_type', path: ['groupBy'], message: 'Invalid input: expected string, received undefined' }
{ code: 'invalid_type', path: ['groupField'], message: 'RETIRED (objectui#7322) - ... author `groupBy`. ...' }
So the panel could not author a valid object-kanban node however it was filled in. The control landed 2026-06-21 (objectui#1831, released in @object-ui/app-shell@17.5.0 and every build after), and groupBy became REQUIRED with objectui#7322 — through all of it every derived check stayed green, because nothing in the repo reads this table against a schema.
⚠️ The page face does not catch it either, and that is worth stating because it is the intuitive place to look. Measured against the pinned @objectstack/spec: PageSchema.safeParse does not descend into a block's properties at all — a page carrying properties: { zzzTotallyBogusKey: 1 } parses green, control run in the same script. The refusal lives only on the NODE face (@object-ui/types/zod), which the designer never invokes.
Suggested shape
⚠️ First step is a measurement, not a gate — the same discipline objectui#8068 imposed on its own class. Answer first: across all BLOCK_CONFIG entries, how many field names are not accepted keys of the node schema the block validates against?
The join is not free, and scoping it is most of the work:
BLOCK_CONFIG is keyed by DESIGNER block type (object-kanban, page:header, element:button, record:details, ...), which is not one vocabulary — some map to @object-ui/types node schemas (ObjectKanbanSchema), some to @objectstack/spec/ui props shapes (PageHeaderProps, RecordDetailsProps), and some to neither.
- Blocks with no resolvable schema must be an EXPLICIT exemption with a reason and a card number, never a silent skip —
check-designer-field-key-parity.mjs already has that idiom and it should be copied rather than reinvented.
- The retired direction matters as much as the missing one: a tombstoned key is still a declared member, so an "is it declared?" check reads green while the parser refuses every value by name.
@object-ui/test-support's isShapeKeyTombstoned is the existing judge for that and block-config.test.ts already uses it.
If the count is small, close the gaps and turn the gate on. If it is large, use the self-deleting exemption idiom. Either way the number belongs in the report.
⚠️ Whether the REQUIRED direction (a required schema key with no control) should also be gated is a real question and probably the more valuable half — that is the half that made objectui#7772 a p2 rather than a p3. It is also the half most likely to need exemptions, since an inspector need not expose every declared key (objectui#7772's triage records that as a product decision, not a contract gap).
⛔ Ablation is the acceptance criterion: break one block's alignment and the new gate must go red and name that block, with a positive control on the unmodified tree in the same run.
Related
objectui#7772 (the defect that measured this gap) · objectui#5761 (the same class one designer over — the FIELD designer; closed) · objectui#8067 / objectui#8068 (the same voluntary-pin argument for registry inputs; both closed) · objectui#3829, objectui#5212 (two earlier BLOCK_CONFIG keys that outlived their renderers, each found by hand)
Filed by the dev of objectui#7772, session session_01YBWFb5YgMU5dw8p2VKj16S.
Found while landing objectui#7772 (branch
claude/issue-7772-page-block-kanban-groupby-control). Filed unassigned and NOT touched there — objectui#7772's triage comment fenced this off explicitly ("Whether that gate should cover block-config is its own card"). Measured onorigin/main0c8dbc49.What
packages/app-shell/src/views/metadata-admin/previews/block-config.ts(BLOCK_CONFIG) is the page-block inspector's field table: one entry per authorable property, and its own file header states the rule it lives by —Nothing mechanical checks that.
scripts/check-designer-field-key-parity.mjsjudgesPAYLOAD_SHAPES— the field / object / permission payloads. Ran it on this branch: exit 0, and its own output names what it covers. It never readsBLOCK_CONFIG.apps/console/src/__tests__/registry-inputs-spec-parity.test.tsjudges the REGISTRYinputsdeclarations (objectui#8067 / objectui#8068), a different table on a different face.previews/__tests__/block-config.test.ts, written one at a time as defects were found:page:header.icon(objectui#3829),page:accordion.title+ itemsvalue(objectui#5212), theglobal_navoption (objectstack#6888), and nowobject-kanban(objectui#7772).That is the same VOLUNTARY shape objectui#8068 argued was not good enough for the registry inputs: three keys did it right, the fourth did not, and nothing noticed the fourth. Here the pins are also voluntary, and each one exists only because a human or an agent tripped over the defect first.
Why this is worth a card rather than a note — the measured cost
objectui#7772 is not a stale-key defect.
BLOCK_CONFIG['object-kanban']offered four controls (objectName/groupField/titleField/cardFields) whileObjectKanbanSchemadeclared:groupByREQUIRED (packages/types/src/objectql.ts:2765, no?;packages/types/src/zod/objectql.zod.ts:1001, no.optional()) — with no control at all;groupFieldaretirementTombstone()(objectql.zod.ts:1002) and?: neveron the TS face since objectui#7322 — as the only control able to set grouping.Measured, not inferred.
ObjectKanbanSchema.safeParseon the node those four controls produce reports TWO issues:So the panel could not author a valid
object-kanbannode however it was filled in. The control landed 2026-06-21 (objectui#1831, released in@object-ui/app-shell@17.5.0and every build after), andgroupBybecame REQUIRED with objectui#7322 — through all of it every derived check stayed green, because nothing in the repo reads this table against a schema.@objectstack/spec:PageSchema.safeParsedoes not descend into a block'spropertiesat all — a page carryingproperties: { zzzTotallyBogusKey: 1 }parses green, control run in the same script. The refusal lives only on the NODE face (@object-ui/types/zod), which the designer never invokes.Suggested shape
BLOCK_CONFIGentries, how many fieldnames are not accepted keys of the node schema the block validates against?The join is not free, and scoping it is most of the work:
BLOCK_CONFIGis keyed by DESIGNER block type (object-kanban,page:header,element:button,record:details, ...), which is not one vocabulary — some map to@object-ui/typesnode schemas (ObjectKanbanSchema), some to@objectstack/spec/uiprops shapes (PageHeaderProps,RecordDetailsProps), and some to neither.check-designer-field-key-parity.mjsalready has that idiom and it should be copied rather than reinvented.@object-ui/test-support'sisShapeKeyTombstonedis the existing judge for that andblock-config.test.tsalready uses it.If the count is small, close the gaps and turn the gate on. If it is large, use the self-deleting exemption idiom. Either way the number belongs in the report.
p2rather than ap3. It is also the half most likely to need exemptions, since an inspector need not expose every declared key (objectui#7772's triage records that as a product decision, not a contract gap).⛔ Ablation is the acceptance criterion: break one block's alignment and the new gate must go red and name that block, with a positive control on the unmodified tree in the same run.
Related
objectui#7772 (the defect that measured this gap) · objectui#5761 (the same class one designer over — the FIELD designer; closed) · objectui#8067 / objectui#8068 (the same voluntary-pin argument for registry
inputs; both closed) · objectui#3829, objectui#5212 (two earlierBLOCK_CONFIGkeys that outlived their renderers, each found by hand)Filed by the dev of objectui#7772, session
session_01YBWFb5YgMU5dw8p2VKj16S.