Found while implementing objectui#9341 (PR objectui#9356), which repairs exactly one leaf of this tree. ⛔ Not fixed there — widening the other declarations would be a second, unrequested move on published surfaces, and that PR already carries needs:contract-review for the one the ruling named.
The contract, and the line that contradicts it
packages/react/src/hooks/useNavigationOverlay.ts declares the option with one parameter:
export interface UseNavigationOverlayOptions {
/** External onRowClick callback — if set, takes full priority */
onRowClick?: (record: Record<string, unknown>) => void;
}
Its own handleClick then casts the declaration away to call it with two:
if (onRowClick) {
(onRowClick as (r: Record<string, unknown>, e?: HandleClickModifiers) => void)(record, event);
return;
}
The sibling member on the same interface file states what the second argument is for, so the intent is not in doubt:
handleClick: (record: Record<string, unknown>, event?: HandleClickModifiers) => void;
and the comment above the cast says why it matters — "Forward the modifier event so parent handlers (e.g. ObjectView) can still implement Cmd/Ctrl/middle-click → open in new tab."
So the declared contract says the callback receives a record; the implementation guarantees it receives a record and the modifier payload. The as is what makes the disagreement compile, and it is the only thing holding the two apart.
Why it is worth a card rather than a shrug
The understated spelling is copied outward. Each of these declares its own pass-through of the same channel with one parameter, while the value that flows through it is invoked with two:
packages/plugin-kanban/src/ObjectKanban.tsx — ObjectKanbanComponentProps.onRowClick?: (record: any) => void
packages/plugin-list/src/ObjectGallery.tsx — both onCardClick and onRowClick, feeding the identical onRowClick: props.onRowClick ?? props.onCardClick
packages/plugin-kanban/src/index.tsx — KanbanRendererProps.schema.onCardClick?: (card: any) => void
The consequence is not a crash; it is that the modifier event is invisible on the declaration a host reads. A host writing a Cmd/Ctrl/middle-click handler has to discover the second argument from the implementation, and must then spell its own parameter optional ((record: any, event?: any)) to stay assignable — which is exactly what ObjectView.tsx does at three call sites (:2734, :3120, :3163). That workaround is the tell: the repo already knows the second argument is there, in the one place that could not avoid knowing.
objectui#9341 is the same class reaching a user: the board's published onCardClick declared one argument while the channel delivered two, and the one-argument declaration turned out to describe a defective second call rather than the real channel.
What is not decided here
Whether to widen the hook's option and let the consumers follow, or to name the payload type on each consumer. HandleClickModifiers is exported from @object-ui/react, so consumers inside that dependency direction can name it — but @object-ui/types, where ObjectKanbanSchema.onCardClick lives, cannot: it is named in no dependency field of @object-ui/types, and @object-ui/react depends on @object-ui/types, so the import is a phantom dependency and a cycle. objectui#9341 resolved that one face as event?: any, matching BaseSchema's own onClick / onChange / onSubmit. Whether the whole family converges on that spelling or on the named interface where it is reachable is a ruling, not a mechanical edit.
⚠️ Any fix here moves several published declarations at once and should carry needs:contract-review.
Reported by the os-dev seat implementing objectui#9341, with Claude Code.
Found while implementing objectui#9341 (PR objectui#9356), which repairs exactly one leaf of this tree. ⛔ Not fixed there — widening the other declarations would be a second, unrequested move on published surfaces, and that PR already carries
needs:contract-reviewfor the one the ruling named.The contract, and the line that contradicts it
packages/react/src/hooks/useNavigationOverlay.tsdeclares the option with one parameter:Its own
handleClickthen casts the declaration away to call it with two:The sibling member on the same interface file states what the second argument is for, so the intent is not in doubt:
and the comment above the cast says why it matters — "Forward the modifier event so parent handlers (e.g. ObjectView) can still implement Cmd/Ctrl/middle-click → open in new tab."
So the declared contract says the callback receives a record; the implementation guarantees it receives a record and the modifier payload. The
asis what makes the disagreement compile, and it is the only thing holding the two apart.Why it is worth a card rather than a shrug
The understated spelling is copied outward. Each of these declares its own pass-through of the same channel with one parameter, while the value that flows through it is invoked with two:
packages/plugin-kanban/src/ObjectKanban.tsx—ObjectKanbanComponentProps.onRowClick?: (record: any) => voidpackages/plugin-list/src/ObjectGallery.tsx— bothonCardClickandonRowClick, feeding the identicalonRowClick: props.onRowClick ?? props.onCardClickpackages/plugin-kanban/src/index.tsx—KanbanRendererProps.schema.onCardClick?: (card: any) => voidThe consequence is not a crash; it is that the modifier event is invisible on the declaration a host reads. A host writing a Cmd/Ctrl/middle-click handler has to discover the second argument from the implementation, and must then spell its own parameter optional (
(record: any, event?: any)) to stay assignable — which is exactly whatObjectView.tsxdoes at three call sites (:2734,:3120,:3163). That workaround is the tell: the repo already knows the second argument is there, in the one place that could not avoid knowing.objectui#9341 is the same class reaching a user: the board's published
onCardClickdeclared one argument while the channel delivered two, and the one-argument declaration turned out to describe a defective second call rather than the real channel.What is not decided here
Whether to widen the hook's option and let the consumers follow, or to name the payload type on each consumer.
HandleClickModifiersis exported from@object-ui/react, so consumers inside that dependency direction can name it — but@object-ui/types, whereObjectKanbanSchema.onCardClicklives, cannot: it is named in no dependency field of@object-ui/types, and@object-ui/reactdepends on@object-ui/types, so the import is a phantom dependency and a cycle. objectui#9341 resolved that one face asevent?: any, matchingBaseSchema's ownonClick/onChange/onSubmit. Whether the whole family converges on that spelling or on the named interface where it is reachable is a ruling, not a mechanical edit.needs:contract-review.Reported by the
os-devseat implementing objectui#9341, with Claude Code.