You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding(types): zod-mirror-parity pairs navigation.zod's two Breadcrumb mirrors with the data-display declaration, so their green says nothing about the mirror it names #6720
Observed while wiring objectui#6646 / #6645 (branch claude/issue-6646-breadcrumb-family-declared-keys). Filed unassigned, not fixed there — see "Why it was not fixed in that PR" below.
Sub-issue of #6349: this is the BreadcrumbSchema / BreadcrumbItem row of that card's table, measured at the one place the duplication is already doing damage.
Measured
packages/types/src/__tests__/zod-mirror-parity.test.ts states its own pairing rule in its header:
MIRRORS pairs a mirror with the declaration it restates. The PAIRING is the only hand-maintained part … Pairing by name alone is not safe
Two of its pairings do not follow it:
line 124 import { BreadcrumbItemSchema, BreadcrumbSchema, ... } from '../zod/navigation.zod.js';
line 136 import type { ..., BreadcrumbItem as Ts_BreadcrumbItem,
BreadcrumbSchema as Ts_BreadcrumbSchema } from '../data-display';
line 444 'navigation.zod.ts#BreadcrumbItemSchema': BreadcrumbItemSchema,
line 445 'navigation.zod.ts#BreadcrumbSchema': BreadcrumbSchema,
line 606 'navigation.zod.ts#BreadcrumbItemSchema': Ts_BreadcrumbItem; <- from ../data-display
line 607 'navigation.zod.ts#BreadcrumbSchema': Ts_BreadcrumbSchema; <- from ../data-display
The mirror half comes from navigation.zod.ts; the declaration half comes from data-display.ts. They are not the same contract, because BreadcrumbSchema and BreadcrumbItem are each declared twice in this package (#6349's table):
packages/types/src/navigation.ts
packages/types/src/data-display.ts
BreadcrumbSchema
type · items · separator · maxItems
type · items · separator
BreadcrumbItem
label · href · icon · onClick · siblings
label · href
re-exported by index.ts
✅ line 272, from ./navigation.js
⛔ only through the DataDisplaySchema union (data-display.ts:1623)
mirrored in zod/
✅ navigation.zod.ts:40 / :97 — the only zod breadcrumb in the package; data-display.zod.ts has none
⛔
So the mirror restates the navigation declaration (it is the one that carries maxItems, and its BreadcrumbItemSchema carries icon), while the file compares it against the data-display one.
Why the mis-pairing is currently silent, and why that is the bad half
The comparison runs over the union of the mirror's keys and the declaration's, and reports two classes: a key the mirror REFUSES (KnownDrift) and a key the mirror has never heard of (UnmirroredDeclared). A key the MIRROR has and the DECLARATION does not is neither. maxItems, icon, onClick and siblings are exactly that shape against the narrow declaration, so:
neither Breadcrumb pair appears in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared;
both read as fully clean pairs;
and that clean reading is about a declaration no consumer of this package resolves.
This is #6349's hazard realised inside a gate, in the wording that card's own triage used: anyone who thinks the mirror is checked against BreadcrumbSchema is reading a check against the one nobody uses, and TypeScript reports nothing because the two declarations are different files with the same name.
Which declaration each consumer actually resolves (verified on 98188c284)
consumer
resolves
renderers/data-display/breadcrumb.tsx (import type ... from '@object-ui/types')
data-display.ts:1578 — publicly reachable through DataDisplaySchema / ComponentSchema, so narrowing either union on type: 'breadcrumb' yields a member with no maxItems and items with no icon
__tests__/zod-mirror-parity.test.ts
data-display.ts⚠️
Why it is worth a card now rather than before
objectui#6646 makes ui:breadcrumb read maxItems, and objectui#5931 / PR #6644 made it read BreadcrumbItem.icon. Both keys live only on the navigation declaration, so the two published keys most recently given behaviour are precisely the ones this pairing cannot see. The parity file exists to catch a mirror drifting narrow against a declaration that widened; on this pair it would not.
Not decided here
Two routes, and the second is not this card's to take:
Re-point the pairing to ../navigation. Mechanically two import specifiers. But the comparison then runs against a declaration with three more keys (icon, onClick, siblings) plus maxItems, and whatever that reveals may need new KnownDrift / UnmirroredDeclared / RuntimeOnlyDeclared entries — writing new debt into governed ratchet ledgers, which is a ruling, not an edit. (onClick is the callback-shaped class objectui#6152 split into its own ledger.)
⛔ Nothing was stripped and no ledger was touched. Note also the standing fence from #6646's triage: the un-exported-from-index BreadcrumbSchema in data-display.ts must not simply be deleted — data-display.ts:1623's union references it, so any route 2 work re-points that union rather than removing the reference.
Observed while wiring objectui#6646 / #6645 (branch
claude/issue-6646-breadcrumb-family-declared-keys). Filed unassigned, not fixed there — see "Why it was not fixed in that PR" below.Sub-issue of #6349: this is the
BreadcrumbSchema/BreadcrumbItemrow of that card's table, measured at the one place the duplication is already doing damage.Measured
packages/types/src/__tests__/zod-mirror-parity.test.tsstates its own pairing rule in its header:Two of its pairings do not follow it:
The mirror half comes from
navigation.zod.ts; the declaration half comes fromdata-display.ts. They are not the same contract, becauseBreadcrumbSchemaandBreadcrumbItemare each declared twice in this package (#6349's table):packages/types/src/navigation.tspackages/types/src/data-display.tsBreadcrumbSchematype·items·separator·maxItemstype·items·separatorBreadcrumbItemlabel·href·icon·onClick·siblingslabel·hrefindex.ts./navigation.jsDataDisplaySchemaunion (data-display.ts:1623)zod/navigation.zod.ts:40/:97— the only zod breadcrumb in the package;data-display.zod.tshas noneSo the mirror restates the navigation declaration (it is the one that carries
maxItems, and itsBreadcrumbItemSchemacarriesicon), while the file compares it against the data-display one.Why the mis-pairing is currently silent, and why that is the bad half
The comparison runs over the union of the mirror's keys and the declaration's, and reports two classes: a key the mirror REFUSES (
KnownDrift) and a key the mirror has never heard of (UnmirroredDeclared). A key the MIRROR has and the DECLARATION does not is neither.maxItems,icon,onClickandsiblingsare exactly that shape against the narrow declaration, so:KnownDrift,UnmirroredDeclaredorRuntimeOnlyDeclared;This is #6349's hazard realised inside a gate, in the wording that card's own triage used: anyone who thinks the mirror is checked against
BreadcrumbSchemais reading a check against the one nobody uses, and TypeScript reports nothing because the two declarations are different files with the same name.Which declaration each consumer actually resolves (verified on
98188c284)renderers/data-display/breadcrumb.tsx(import type ... from '@object-ui/types')navigation.ts:205packages/types/src/registry.ts:87,176('breadcrumb': BreadcrumbSchema)navigation.ts(importedfrom './navigation.js')packages/types/src/index.ts:272public exportnavigation.tszod/navigation.zod.ts:97navigation.tsdata-display.ts:1623DataDisplaySchemauniondata-display.ts:1578— publicly reachable throughDataDisplaySchema/ComponentSchema, so narrowing either union ontype: 'breadcrumb'yields a member with nomaxItemsand items with noicon__tests__/zod-mirror-parity.test.tsdata-display.tsWhy it is worth a card now rather than before
objectui#6646 makes
ui:breadcrumbreadmaxItems, and objectui#5931 / PR #6644 made it readBreadcrumbItem.icon. Both keys live only on the navigation declaration, so the two published keys most recently given behaviour are precisely the ones this pairing cannot see. The parity file exists to catch a mirror drifting narrow against a declaration that widened; on this pair it would not.Not decided here
Two routes, and the second is not this card's to take:
../navigation. Mechanically two import specifiers. But the comparison then runs against a declaration with three more keys (icon,onClick,siblings) plusmaxItems, and whatever that reveals may need newKnownDrift/UnmirroredDeclared/RuntimeOnlyDeclaredentries — writing new debt into governed ratchet ledgers, which is a ruling, not an edit. (onClickis the callback-shaped class objectui#6152 split into its own ledger.)KNOWN_COLLISIONSline in the same PR.⛔ Nothing was stripped and no ledger was touched. Note also the standing fence from #6646's triage: the un-exported-from-index
BreadcrumbSchemaindata-display.tsmust not simply be deleted —data-display.ts:1623's union references it, so any route 2 work re-points that union rather than removing the reference.Adjacent, not this
zod-mirror-parity's SPEC_DERIVED_PAIRS scans raw text betweenexport constboundaries, so a Spec-token mentioned in PROSE is attributed to the neighbouring export #6705 — a differentzod-mirror-paritymechanism (SPEC_DERIVED_PAIRSscanning raw text betweenexport constboundaries). Same file, unrelated defect.FormField,MarkdownSchema,KanbanSchema— and one doc assertsFormFieldis declared once #6172 / finding(types):KanbanCard/KanbanColumnare declared four times in this tree and the published copies disagree (cardsvsitems,badgesvslabels) #6155 / finding(plugin-timeline): thetimelinerenderer reads seven keysTimelineSchemadoes not declare, and declares three it never reads #6170 — the family batches for other rows of 46 exported type names carry a second authority — 42 of them are named by no family card #6349's table; none of them namesBreadcrumbSchema(read on 2026-08-29).