Skip to content

121 declared-but-unmirrored keys across 16 schema pairs — the lane #6058's new UnmirroredDeclared ledger made visible #6152

Description

@yinlianghui-tw

Filed unassigned, no pm:* label — triage owns grading and routing. Recorded by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe, from the handback of #6058 (PR #6149).

What this is

#6058 fixed a blind guard: zod-mirror-parity's forward comparison mapped over the intersection of mirrored keys and keyof the declaration, so a key declared on the TypeScript side and absent from the mirror did not compare unequal — it left the comparison entirely. PR #6149 lands the union comparison and seeds a second, separately-ratcheted ledger, UnmirroredDeclared, at the measured debt.

This card is that debt. It is not a defect in the guard and not a regression — every fact below was already true; nothing was watching. KnownDrift and NarrowerThanDeclared are untouched at 12 entries / 17 keys, and #5927 / PR #6032's citable 17 → 13 shrink stays comparable.

16 pairs carry 121 declared-but-unmirrored keys, out of 158 registered pairs. Each one is a key the published TypeScript invites an author to write and the published validator has never heard of — the "declared ≠ enforced" shape this whole family exists to close.

⚠️ Note on the arithmetic, because two different counts are in circulation: #6058's round-1 report counted 23 red pairs under the union comparison, of which 7 were red only via their pre-existing KnownDrift entries. The 16 here are the pairs that actually carry unmirrored keys. The earlier 4-vs-19 spec split maps to 3-vs-13 below because PageNodeSchema's redness is entirely KnownDrift and it contributes zero unmirrored keys.

Spec-derived — ⛔ route to #2231, do not fix locally (3 pairs, 14 keys)

On these the mirror takes its shape by reference from @objectstack/spec, so an unmirrored declared key means the local declaration carries members the spec schema does not model. That is the unification question, not a local mirror edit — and SPEC_DERIVED_PAIRS' own doc notes a spec bump legitimately moves these sets. They are marked in the ledger rather than exempted in the instrument.

pair keys
complex.zod.ts#DashboardComponentSchema title
complex.zod.ts#DashboardWidgetSchema pagination, searchable
objectql.zod.ts#ObjectViewSchema allowCreateView, defaultListView, defaultViewType, filterableFields, listViews, navigation, onNavigate, searchableFields, showViewSwitcher, viewActions, viewTabBar

Local mirror omissions (13 pairs, 107 keys)

pair count keys
objectql.zod.ts#ObjectFormSchema 26 allowSkip, buttons, defaultTab, defaults, drawerSide, drawerWidth, formType, mobile, modalCloseButton, modalSize, nextText, onCancel, onError, onOpenChange, onStepChange, onSuccess, open, prevText, sections, showStepIndicator, splitDirection, splitResizable, splitSize, subforms, submitHandler, tabPosition
data-display.zod.ts#DataTableSchema 29 disableInnerScroll, editable, manualPagination, manualSearch, manualSorting, onAddRecord, onBatchSave, onCellChange, onColumnReorder, onColumnResize, onPageChange, onPageSizeChange, onRowActionDef, onRowClick, onRowSave, onSearchChange, onSortChange, page, rowActionDefs, rowClassName, rowCount, rowStyle, search, selectionResetKey, selectionStyle, showAddRow, showSelectionCount, singleClickEdit, sort
objectql.zod.ts#ObjectGridSchema 17 aggregations, bulkActionDefs, bulkSpecActions, conditionalFormatting, emptyState, exportOptions, grouping, navigation, onNavigate, operations, reorderableColumns, resizableColumns, rowColor, rowHeight, rowSpecActions, singleClickEdit, title
views.zod.ts#DetailViewSchema 14 activities, autoDiscoverRelated, autoTabs, comments, defaultTab, highlightFields, history, onAddComment, onNavigate, onTabChange, primaryField, recordNavigation, sectionGroups, summaryFields
form.zod.ts#FormSchema 9 defaultFieldTab, fieldContainerClass, fieldPanes, fieldPanesOrientation, fieldPanesResizable, fieldTabs, fieldTabsPosition, mobileStickyActions, onDirtyChange
complex.zod.ts#ChatbotSchema 3 displayMode, floatingConfig, requestBody
reports.zod.ts#ReportComponentSchema 3 chartConfig, conditionalFormatting, reportType
data-display.zod.ts#ChartSchema 1 drillDown
form.zod.ts#FormFieldSchema 1 field
form.zod.ts#InputSchema 1 wrapperClass
form.zod.ts#LabelSchema 1 content
navigation.zod.ts#PaginationSchema 1 currentPage
views.zod.ts#DetailViewSectionSchema 1 hideEmpty

Three were verified by hand against both sources during #6058, as a spot-check that these are defects rather than instrument noise: InputSchema.wrapperClass (declared in src/form.ts, absent from the mirror), DetailViewSectionSchema.hideEmpty, ChartSchema.drillDown.

⭐ The sorting that should come before any batching — 23 of the 121 are callback-shaped

23 keys are on* props: 12 on DataTableSchema, 5 on ObjectFormSchema, 3 on DetailViewSchema, and 1 each on FormSchema, ObjectGridSchema, ObjectViewSchema.

⚠️ For those, "mirror it" may be the wrong remedy. The right one may be to narrow the declaration so it stops advertising a runtime callback as authorable metadata — which shrinks the ledger without touching any mirror, and is a scope reduction. objectui#4453 already picked that direction once, narrowing to typeof === 'function' so an authored string handler is dropped; and #6122 / PR #6142 landed the documentation half of the same judgement an hour ago, after measuring that no renderer reads any of 16 schema-level event slots.

⛔ So this subset is worth one ruling covering all 23, not 23 per-key decisions and not a blanket mirroring sweep. Deciding it first also shrinks whatever remains.

What a taker needs to know

  • Growing KnownDrift is not the remedy for anything here — that ledger means "mirrors that refuse a declared spelling" and is a different, honest 12. UnmirroredDeclared is the ledger these belong to, and it is shrink-only: any new declared-but-unmirrored key on any of the 158 pairs reddens immediately, so the debt cannot grow while it is worked off.
  • The ledger is pinned in both directions by ReconcileAgainstLedger with synthetic-pair tests, so a fact deleted from the seed is not silently re-acceptable and a stale entry names itself.
  • ⚠️ --noErrorTruncation will not show you the whole list through a named alias — TS prints the alias name and elaborates exactly one member with no ellipsis. finding: the #5684 zod-mirror-parity guard is BLIND to a key declared on the TS side but absent from the mirror — measured on ObjectGanttSchema, and it is the guard #5927 just used as authoritative #6058 measured that an inline spelling resolves the union and prints a real ... N more ..., and wrote both readings into the file. Read the ledger, not the compiler error, to see the set.
  • The measurement instrument is the TypeScript compiler API walking each pair's union members via isStringLiteral().value; its non-vacuity control is that it read all 158 pairs and returned empty for 142.

Refs: #6058 / PR #6149 (where this was measured and where the ledger lands) · #6141 (the stale 163/13 prose, corrected by that PR) · #5927 / PR #6032 (the 17 → 13 shrink this preserves) · #2231 (spec unification, where the 3 spec-derived pairs go) · #5155 (what an index signature does and does not cap) · #4453 / #6122 (the callback-narrowing precedent).

Activity

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

Metadata

Metadata

Labels

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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions