Found while repairing packages/mobile/README.md for the objectui#5174 UNGATED_DOCS burn-down (batch 14). Out of that PR's scope — its file surface is the README plus the ledger removal — so it is filed here instead of ridden along.
The two halves
1. The shipped @example does not compile. packages/mobile/src/useSpecGesture.ts (around line 70) documents:
config: { type: 'swipe', enabled: true, swipe: { direction: 'left', threshold: 80 } },
SwipeGestureConfig.direction is declared in @object-ui/types as SpecSwipeDirection[] — an ARRAY:
export interface SwipeGestureConfig {
direction: SpecSwipeDirection[];
threshold?: number;
velocity?: number;
}
A reader who copies that example gets TS2322 ('left' is not assignable to SpecSwipeDirection[]). Nothing catches it today: check-doc-snippet-types.mjs reads fenced blocks in Markdown documents, not JSDoc @example blocks in .ts sources, so this example has never been compiled by anything.
2. The implementation reads BOTH shapes, which is why nobody noticed. Same file, the 'swipe' case:
const dir = Array.isArray(config.swipe?.direction)
? config.swipe?.direction[0]
: (config.swipe?.direction as string | undefined);
That is a consumer-side tolerant fallback of exactly the class AGENTS.md commandment #0.1 bans: the declared contract says array, the runtime silently accepts a scalar through an as cast, and the wrong example therefore "works" when hand-tested. Declared-and-not-enforced in one direction, enforced-and-not-declared in the other.
Why the two halves are one card
Repairing only the example leaves the lenient cast, which will let the next author write the scalar again. Removing only the cast turns the shipped example from "will not compile" into "will not compile AND would have thrown"; the example has to move in the same change. Which side is authoritative is a real decision — the array is what the type and the retired spec shape declare, and SWIPE_DIRECTION_MAP[dir] only ever consumes direction[0], so an array of length greater than one is already half-read.
Repro
# the declared type
sed -n '/export interface SwipeGestureConfig/,/^}/p' packages/types/dist/mobile.d.ts
# the example and the cast
sed -n '62,80p;100,112p' packages/mobile/src/useSpecGesture.ts
Not asserted here
- Whether any caller in this repo or in
examples/ passes a scalar today (not measured).
- Whether
@example blocks in .ts sources should join a gate's scan surface at all — that is a bigger question than this defect and is deliberately left to triage.
Unassigned and unlabelled on purpose: triage owns the priority. Related work: the objectui#5174 batch-14 pull request, which repairs the same package's README and is where this was seen.
Filed by an agent seat while working the batch-14 dispatch; see that pull request for the measurement context.
Found while repairing
packages/mobile/README.mdfor the objectui#5174UNGATED_DOCSburn-down (batch 14). Out of that PR's scope — its file surface is the README plus the ledger removal — so it is filed here instead of ridden along.The two halves
1. The shipped
@exampledoes not compile.packages/mobile/src/useSpecGesture.ts(around line 70) documents:SwipeGestureConfig.directionis declared in@object-ui/typesasSpecSwipeDirection[]— an ARRAY:A reader who copies that example gets TS2322 (
'left'is not assignable toSpecSwipeDirection[]). Nothing catches it today:check-doc-snippet-types.mjsreads fenced blocks in Markdown documents, not JSDoc@exampleblocks in.tssources, so this example has never been compiled by anything.2. The implementation reads BOTH shapes, which is why nobody noticed. Same file, the
'swipe'case:That is a consumer-side tolerant fallback of exactly the class AGENTS.md commandment #0.1 bans: the declared contract says array, the runtime silently accepts a scalar through an
ascast, and the wrong example therefore "works" when hand-tested. Declared-and-not-enforced in one direction, enforced-and-not-declared in the other.Why the two halves are one card
Repairing only the example leaves the lenient cast, which will let the next author write the scalar again. Removing only the cast turns the shipped example from "will not compile" into "will not compile AND would have thrown"; the example has to move in the same change. Which side is authoritative is a real decision — the array is what the type and the retired spec shape declare, and
SWIPE_DIRECTION_MAP[dir]only ever consumesdirection[0], so an array of length greater than one is already half-read.Repro
Not asserted here
examples/passes a scalar today (not measured).@exampleblocks in.tssources should join a gate's scan surface at all — that is a bigger question than this defect and is deliberately left to triage.Unassigned and unlabelled on purpose: triage owns the priority. Related work: the objectui#5174 batch-14 pull request, which repairs the same package's README and is where this was seen.
Filed by an agent seat while working the batch-14 dispatch; see that pull request for the measurement context.