Skip to content

finding: nothing mechanically compares the page-block designer's BLOCK_CONFIG field names against the node schemas — measured cost on objectui#7772, where a REQUIRED key had no control for three months #8216

Description

@os-justin

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repofindingpriority:p2tooling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions