Observation-class finding, surfaced while fixing #4528. Nothing a user meets today; no fix proposed here.
The mismatch
packages/react/src/hooks/useNavigationOverlay.ts:48 derives its public alias from the spec schema and tightens one key:
export type NavigationConfig = Omit<
SpecAuthoredInput< typeof NavigationConfigSchema >,
'mode'
> & {
mode: NonNullable< SpecAuthoredInput< typeof NavigationConfigSchema >['mode'] >;
};
So mode is REQUIRED on the alias. The hook's own body, 140 lines below, does not require it:
const mode: NavigationMode = navigation?.mode ?? 'page';
The implementation is strictly more permissive than the type that fronts it, and the default it applies ('page') is meaningful behaviour, not a placeholder.
Why it surfaces now
The spec-derived ListViewSchema['navigation'] leaves mode optional, which is correct — authored metadata may set navigation: { view: 'x' } and let the mode default. ListView passes schema.navigation straight into the hook, and that has been a type error the whole time; it just could not be seen, because ListViewProps carried a [key: string]: any that erased the props type and made schema resolve to any inside the render function (#4528).
With the erasure removed, the call needs an assertion to compile:
navigation: schema.navigation as NavigationConfig | undefined,
#4528's PR takes that assertion, because it is a type-only card and the runtime value is unchanged either way. It is a workaround at a consumer, which is the shape AGENTS.md #0.1 says to fix at the producer instead.
ObjectGrid makes the identical call (ObjectGrid.tsx:1029) and compiles without an assertion only because ObjectGridSchema['navigation'] happens to be shaped differently — so the two sibling renderers disagree about whether the same hook needs a cast.
The decision worth making
Either
- Relax the alias to match the implementation (
mode?:, i.e. stop the Omit + re-add), so spec-shaped values are accepted as-is and the assertion in ListView (and any future one) disappears; or
- Keep the alias strict and make the requirement real — have the hook reject a missing
mode rather than defaulting it, and have callers resolve the default where the metadata is normalized.
(1) is the smaller change and matches what the code already does. (2) is the contract-first reading, but it moves a default that has been live for a while and would need every caller to supply mode explicitly. Whoever picks should also decide whether 'page' is the right default to have been applying silently.
Refs #4528, #2578, #2942.
Generated by Claude Code
Observation-class finding, surfaced while fixing #4528. Nothing a user meets today; no fix proposed here.
The mismatch
packages/react/src/hooks/useNavigationOverlay.ts:48derives its public alias from the spec schema and tightens one key:So
modeis REQUIRED on the alias. The hook's own body, 140 lines below, does not require it:The implementation is strictly more permissive than the type that fronts it, and the default it applies (
'page') is meaningful behaviour, not a placeholder.Why it surfaces now
The spec-derived
ListViewSchema['navigation']leavesmodeoptional, which is correct — authored metadata may setnavigation: { view: 'x' }and let the mode default.ListViewpassesschema.navigationstraight into the hook, and that has been a type error the whole time; it just could not be seen, becauseListViewPropscarried a[key: string]: anythat erased the props type and madeschemaresolve toanyinside the render function (#4528).With the erasure removed, the call needs an assertion to compile:
#4528's PR takes that assertion, because it is a type-only card and the runtime value is unchanged either way. It is a workaround at a consumer, which is the shape AGENTS.md #0.1 says to fix at the producer instead.
ObjectGridmakes the identical call (ObjectGrid.tsx:1029) and compiles without an assertion only becauseObjectGridSchema['navigation']happens to be shaped differently — so the two sibling renderers disagree about whether the same hook needs a cast.The decision worth making
Either
mode?:, i.e. stop theOmit+ re-add), so spec-shaped values are accepted as-is and the assertion inListView(and any future one) disappears; ormoderather than defaulting it, and have callers resolve the default where the metadata is normalized.(1) is the smaller change and matches what the code already does. (2) is the contract-first reading, but it moves a default that has been live for a while and would need every caller to supply
modeexplicitly. Whoever picks should also decide whether'page'is the right default to have been applying silently.Refs #4528, #2578, #2942.
Generated by Claude Code