Problem
The phone-preview path (http://192.168.1.167:5173) is a non-secure
origin. `window.crypto.randomUUID` is gated behind secure contexts
(HTTPS / localhost / 127.0.0.1 / file://) so it's undefined there,
and every optimistic-id call site crashed handleAdd with
`TypeError: crypto.randomUUID is not a function`.
Fix
apps/web/src/lib/utils/id.ts — generateId() uses crypto.randomUUID()
when available, otherwise falls back to crypto.getRandomValues()
+ RFC 4122 §4.4 byte assembly. getRandomValues IS exposed on
insecure contexts (unlike subtle), so the fallback is v4-compliant
and cryptographically acceptable for client-side optimistic ids.
Replaced crypto.randomUUID() at every call site:
stores/lists.ts, stores/tasks.ts, sync/queue.ts, sync/undoQueue.ts
routes/(app)/lists/[id]/+page.svelte
routes/(app)/tasks/[id]/+page.svelte
CLAUDE.md gotcha added: never call crypto.randomUUID() directly —
always import generateId from $lib/utils/id.
Tests (written first, confirmed failing on main)
src/lib/utils/id.test.ts (4 tests)
U-01 returns UUID v4 when randomUUID is available
U-02 falls back when randomUUID is undefined
U-03 50 fallback-generated ids are all unique
U-04 prefers randomUUID when present (doesn't drop into fallback)
tests/e2e/insecure-context.test.ts (1 test)
INS-01 stubs crypto.randomUUID = undefined via addInitScript,
drives the add-item flow, confirms the row renders (if the
old call site were still there, handleAdd would throw).
Verification
just test-all → exit 0, 233 green, 2 skipped:
34 pgTAP (unchanged)
140 Vitest integration + 2 skipped presence (unchanged)
15 Vitest unit (was 11, +4 generateId)
44 Playwright (was 43, +1 INS-01)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>