- Types: ChatMessage, ConversationSummary, Conversation
- streamChat: POST /api/advisor/chat with ReadableStream SSE parsing
- fetchConversations: GET with 404-tolerant empty array fallback
- fetchConversation: GET by id
- Add web/src/api/test.ts with submitCableTest, getRecentTests, streamTestEvents
- Add web/src/pages/CableTestPage.tsx with three-panel layout (readout/label/recent)
- Register /test route in router.tsx
- Add Test nav link (Cable icon) in TopBar
- SSE live readout via EventSource, closed on unmount
- Print & Next mutation with react-hot-toast feedback
- Mobile-responsive: single col on mobile, 3-col on lg+
- ClickHouse design tokens throughout (#000000 bg, #faff69 accent)
- web/src/pages/ScanPage.tsx: camera QR scanner with volt reticle overlay
- extractHWID() parses both URL format and bare HW-XXXXX patterns
- rear camera preference (back/rear/environment label matching)
- debounce via lastScanned state prevents duplicate navigation
- graceful camera permission denied error state
- web/src/router.tsx: lazy-loads ScanPage with Suspense fallback spinner
- web/src/lib/api.ts: typed fetch wrappers (fetchInventory, fetchInventoryItem)
- web/src/components/layout/AppShell.tsx: minimal page wrapper (stub for plan 03-03)
- useIntakeStore: step tracking, photos (max 3), aiResult, editedName, error
- submitIntake(): multipart FormData POST /api/intake with IntakeResponse type
- DropZone: react-dropzone with camera capture, volt hover state, slot counter
- PhotoPreview: thumbnail grid with X remove button per photo
- Initialize module git.georgsen.dk/hwlab with Go 1.23
- Install chi v5.2.5, go-redis v9.18.0, viper v1.21.0, godotenv v1.5.1, uuid v1.6.0, go-netbox v4.3.0
- Create health handler GET /api/health returning {status:ok, version:0.1.0}
- Create chi router with Logger/Recoverer/RealIP middleware and SPA fallback
- Embed web/dist via assets.go at module root (go:embed cannot use .. paths)
- Create stub web/dist/index.html with ClickHouse dark theme
- TestHealth passes