Skip to content

27 keys across 18 pairs have a zod mirror that ACCEPTS a spelling its TypeScript declaration refuses — each needs a narrow-the-mirror / widen-the-declaration ruling #7759

Description

@os-justin

Filed unassigned by the dev seat executing objectui#7069 (PR #7758), which added the fifth parity ledger WiderThanDeclared and SEEDED it at the debt this direction was already carrying. Seeding is what lets that PR be green on its own head; it is a floor, not a disposition. This card is the disposition lane, the shape objectui#6152 is for UnmirroredDeclared and objectui#6033 / objectui#5927 are for the NARROWER direction.

Recording and grouping only; ⛔ not proposing per-key remedies.

What was measured

packages/types/src/__tests__/zod-mirror-parity.test.ts, on origin/main e546222b merged at 8e037c81. Of the 157 registered pairs, the new operator reports 36 pairs / 54 keys. Those split into two classes that do NOT share a remedy, and only one of them is a defect:

  • SCHEMA-NODE — 27 keys / 21 pairs. An unknown survives into the mirror's input face from the recursion-breaking annotation on the schema-node mirrors. These record the ANNOTATION, not an accept-set gap: at runtime the lazy union does validate. ⛔ Not this card, and ⛔ not repairable per key. The instrument limitation behind them is its own card.
  • CONCRETE — 27 keys / 18 pairs. Both faces concrete, so the reading means what it says: an author can write the spelling, safeParse returns green, and tsc refuses it. That is this card.

The 27, grouped by why they diverge

A. INLINE-LOCALE widening — the mirror takes a locale map the declaration restates as a plain string

BaseSchema's mirror spells the label keys as a plain string OR an inline locale map; these declarations restate the key as a plain string, so the narrowing is on the DECLARED side and the forward comparison reads the pair as clean.

pair keys
app.zod.ts#AppComponentSchema label
objectql.zod.ts#ObjectGridSchema label, description
layout.zod.ts#PageNodeSchema aria (the nested aria label)
complex.zod.ts#DashboardComponentSchema header, globalFilters (both nested one level down)

B. SINGLE-OR-LIST where the declaration states the list alone — the objectui#6881 toolbar shape, alive

pair keys shape
form.zod.ts#SliderSchema defaultValue, value mirror z.union([z.number(), z.array(z.number())]), declaration number[]

⭐ This is the class objectui#7069 was filed to keep alive after PR #7066 retired its instance, and it turned out to have a live one on a pair nobody had looked at. A single number parses green and tsc refuses it.

C. An extra arm the declaration does not admit

pair key shape
form.zod.ts#FormSchema layout mirror z.enum(['vertical', 'horizontal', 'grid']), declaration states the first two
layout.zod.ts#ContainerSchema maxWidth mirror's arm is z.boolean(), declaration admits the FALSE literal alone, so true parses green

D. DISJOINT vocabularies — already in KnownDrift, now measured from the other side too

Each face refuses something the other admits, so the key is legitimately recorded in both ledgers. 12 keys across 8 pairs; the CONCRETE ones are:

pair keys
form.zod.ts#FormSchema fields, mode
form.zod.ts#CalendarSchema defaultValue, value
navigation.zod.ts#HeaderBarSchema variant
complex.zod.ts#FilterFieldSchema operators
complex.zod.ts#FilterBuilderSchema fields
complex.zod.ts#ChatbotSchema body
complex.zod.ts#DashboardComponentSchema header, globalFilters

⚠️ FilterFieldSchema.operators and FilterBuilderSchema.fields overlap objectui#7561, which measured the same operator-vocabulary divergence from the RENDERER side. Listed to prevent re-conflation, not re-filed.

E. FUNCTION-SLOT — 8 keys

pair keys
data-display.zod.ts#DataTableSchema renderCellEditor
data-display.zod.ts#TableColumnSchema cell
form.zod.ts#FieldConditionSchema custom
form.zod.ts#FieldConstraintsSchema validate
form.zod.ts#FormFieldSchema condition (nested)

zod 4 gives a function mirror an opaque input brand that no concrete signature equals, so the mirror accepts ANY callable where the declaration states one signature. ⚠️ These are mirrored runtime slots, so objectui#6152's RuntimeOnlyDeclared ruling does not reach them — that ledger is for keys the mirror never declares. Whether a runtime slot should carry a z.function() mirror at all is the question, and it may be one ruling for all of them rather than five. objectui#7351 records a related z.function() survivor of objectui#6124's sweep.

F. Structural — an inline element shape against a named declaration

pair keys
data-display.zod.ts#DataTableSchema columns
form.zod.ts#FormFieldSchema validation
views.zod.ts#DetailViewFieldSchema options
complex.zod.ts#DashboardComponentSchema dateRange — mirror takes a bare string where the declaration states a closed literal set

Why the default remedy is NARROW THE MIRROR, and why that is still a ruling

The mirror is the AUTHORING boundary — it validates AI-written metadata — so a wider key hands an author a green parse on a spelling the published types refuse, which is the contract-first direction to close at the producer. But each key needs the renderer read measured before the direction is chosen: group A may be a declaration that forgot the base's locale map rather than a mirror that over-accepts, and group D's disjoint pairs have a dead side to identify first. ⛔ Not decidable in bulk.

Dedup

One targeted semantic search over the parity / mirror / accept-set vocabulary, INCLUDING closed issues, returned 35 substantive results — a live instrument, not a silent zero. Reviewed: objectui#5927 / objectui#6033 are the NARROWER direction's per-key lane, objectui#6152 the unmirrored one, objectui#7069 this direction's class card, objectui#7561 / objectui#7465 / objectui#7735 per-key instances, objectui#6940 and objectui#5853 closed instance cards of this very direction. No card claims the wider direction's per-key dispositions. Channel note, declared: repo-scoped REST is refused for this seat (the session permission layer returns 403), so the dedup ran on the semantic-search channel.

One census card rather than 18 per-pair cards, stated as the dev seat's call: the groups above share remedies within themselves, and 18 cards would fragment a ruling that is really five or six.

Related

State (seat carry-over)

The thread is the record: ruling 5617465269, then groups E and A delivered and accepted (5818019754, 5852380316). Group C (FormSchema.layout) was measured by the dev report 5855353651: zero pull, and grid / inline render byte-identical to vertical. Under the ruling's principle 1 it waits on the objectstack enforce-or-remove card below. ⛔ Its arms are not deleted locally.

Blocked-by: objectstack-ai/objectstack#20221

Activity

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

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneenhancementNew feature or requestpriority:p2tracking

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions