Surfaced while sweeping @object-ui/collaboration's frozen vi.mock factories for #6892 slice 4 (PR #8082). Filed unassigned. Deliberately not fixed there — that PR converts factories to inherit the real export surface, and changing a stub's value shape is a behaviour change it was not dispatched to make.
What was measured
useRecordPresence returns an array. packages/collaboration/src/PresenceProvider.tsx:101:
export function useRecordPresence(
objectName: string | undefined,
recordId: string | undefined,
): PresenceUser[] {
Its only consumer reads .length on that array — packages/app-shell/src/views/RecordDetailView.tsx:2358:
{recordPresence.length > 0 && (
<PresenceAvatars users={recordPresence} size="sm" maxVisible={3} showStatus />
)}
Across the 22 packages/app-shell test files that stub useRecordPresence, the stubs disagree with each other and 13 of them disagree with the hook:
| stub |
files |
matches the real return type |
useRecordPresence: () = arrow [] |
9 |
✅ yes |
useRecordPresence: () = arrow ({ viewers: [], others: [] }) |
13 |
❌ no — the hook never returns an object |
Why it is a defect and not a style nit
The 13 tests pass, and they pass for the wrong reason. An object has no length, so recordPresence.length is undefined, undefined > 0 is false, and the presence row is never rendered. Those 13 files therefore exercise the "no presence" branch through a shape mismatch, not through the empty array the branch is actually about. Two consequences:
- They cannot fail in the direction they look like they cover. No value of the
{ viewers, others } stub can ever make the row render, so nothing in those files pins the row-visible path even accidentally.
- Nothing catches the divergence. A
vi.mock factory is untyped, so tsc never compares the stub against PresenceUser[]; the drift is invisible to every gate in the tree. If the hook's contract changes, the 9 correct files move and the 13 stay green.
The { viewers, others } shape appears to be borrowed from a different presence surface — no export of @object-ui/collaboration returns it.
Scope
Test-only. packages/app-shell/src/views/RecordDetailView.*.test.tsx, 13 files. The repair is presumably to make the 13 stubs return an array like the other 9, then check whether any assertion in them was silently relying on the row being unrenderable — that second half is the actual work, and it is why this is a card rather than a rider on the sweep.
Related: #6892 (the sweep worklist whose slice 4 surfaced this). The sweep's conversion leaves each override exactly as it was, so this PR neither introduces nor repairs the mismatch.
Generated by Claude Code
Surfaced while sweeping
@object-ui/collaboration's frozenvi.mockfactories for #6892 slice 4 (PR #8082). Filed unassigned. Deliberately not fixed there — that PR converts factories to inherit the real export surface, and changing a stub's value shape is a behaviour change it was not dispatched to make.What was measured
useRecordPresencereturns an array.packages/collaboration/src/PresenceProvider.tsx:101:Its only consumer reads
.lengthon that array —packages/app-shell/src/views/RecordDetailView.tsx:2358:Across the 22
packages/app-shelltest files that stubuseRecordPresence, the stubs disagree with each other and 13 of them disagree with the hook:useRecordPresence: () =arrow[]useRecordPresence: () =arrow({ viewers: [], others: [] })Why it is a defect and not a style nit
The 13 tests pass, and they pass for the wrong reason. An object has no
length, sorecordPresence.lengthisundefined,undefined > 0isfalse, and the presence row is never rendered. Those 13 files therefore exercise the "no presence" branch through a shape mismatch, not through the empty array the branch is actually about. Two consequences:{ viewers, others }stub can ever make the row render, so nothing in those files pins the row-visible path even accidentally.vi.mockfactory is untyped, sotscnever compares the stub againstPresenceUser[]; the drift is invisible to every gate in the tree. If the hook's contract changes, the 9 correct files move and the 13 stay green.The
{ viewers, others }shape appears to be borrowed from a different presence surface — no export of@object-ui/collaborationreturns it.Scope
Test-only.
packages/app-shell/src/views/RecordDetailView.*.test.tsx, 13 files. The repair is presumably to make the 13 stubs return an array like the other 9, then check whether any assertion in them was silently relying on the row being unrenderable — that second half is the actual work, and it is why this is a card rather than a rider on the sweep.Related: #6892 (the sweep worklist whose slice 4 surfaced this). The sweep's conversion leaves each override exactly as it was, so this PR neither introduces nor repairs the mismatch.
Generated by Claude Code