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.

pairkeys
complex.zod.ts#DashboardComponentSchematitle
complex.zod.ts#DashboardWidgetSchemapagination, searchable
objectql.zod.ts#ObjectViewSchemaallowCreateView, defaultListView, defaultViewType, filterableFields, listViews, navigation, onNavigate, searchableFields, showViewSwitcher, viewActions, viewTabBar

Local mirror omissions (13 pairs, 107 keys)

paircountkeys
objectql.zod.ts#ObjectFormSchema26allowSkip, 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#DataTableSchema29disableInnerScroll, 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#ObjectGridSchema17aggregations, bulkActionDefs, bulkSpecActions, conditionalFormatting, emptyState, exportOptions, grouping, navigation, onNavigate, operations, reorderableColumns, resizableColumns, rowColor, rowHeight, rowSpecActions, singleClickEdit, title
views.zod.ts#DetailViewSchema14activities, autoDiscoverRelated, autoTabs, comments, defaultTab, highlightFields, history, onAddComment, onNavigate, onTabChange, primaryField, recordNavigation, sectionGroups, summaryFields
form.zod.ts#FormSchema9defaultFieldTab, fieldContainerClass, fieldPanes, fieldPanesOrientation, fieldPanesResizable, fieldTabs, fieldTabsPosition, mobileStickyActions, onDirtyChange
complex.zod.ts#ChatbotSchema3displayMode, floatingConfig, requestBody
reports.zod.ts#ReportComponentSchema3chartConfig, conditionalFormatting, reportType
data-display.zod.ts#ChartSchema1drillDown
form.zod.ts#FormFieldSchema1field
form.zod.ts#InputSchema1wrapperClass
form.zod.ts#LabelSchema1content
navigation.zod.ts#PaginationSchema1currentPage
views.zod.ts#DetailViewSectionSchema1hideEmpty

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).

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