Commit graph

120 commits

Author SHA1 Message Date
712a0a39b8 docs(07-02): complete natural language search plan summary
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 07:56:51 +00:00
7db093c696 feat(07-02): frontend NL search bar wired to GET /api/search
- web/src/lib/api.ts: add fetchSearch(q) returning Promise<InventoryItem[]>
- web/src/components/inventory/FilterBar.tsx: NL search input with 400ms debounce,
  volt accent styling, Sparkles icon, Loader2 spinner during search
- web/src/pages/DashboardPage.tsx: nlQuery state + useQuery hook (enabled >2 chars),
  displays searchResults when NL active, local filter unchanged when NL empty
2026-04-10 07:56:02 +00:00
9db7707a64 feat(07-02): SearchHandler — NL query to NetBox filter with Tier 1 LLM
- internal/api/handlers/search.go: SearchHandler, NewSearchHandler, SearchDevices
- Sanitizes query (non-printable stripped, 200 char max) per T-07-05
- LLM extracts catalog_status/name_contains/tag; falls back to substring on parse failure
- internal/api/handlers/search_test.go: 4 tests covering 400, fallback, status filter, combined
- internal/api/router.go: wires GET /api/search with nil guard (503)
- cmd/hwlab/main.go: constructs searchHandler and passes to NewRouter
2026-04-10 07:55:07 +00:00
cbe411996f docs(07-01): complete research agent plan — SearXNG client, ResearchAgent, trigger endpoint 2026-04-10 07:51:58 +00:00
0072aa41bd feat(07-01): ResearchAgent worker, trigger endpoint, main.go wiring
- internal/research/agent.go: Agent with RunOnce+Start, sanitizeQuery, interface adapters
- internal/research/agent_test.go: stub-based unit tests (sanitize, enrich, skip, empty)
- internal/ai/client.go: TierClient.TextComplete for text-only LLM calls
- internal/api/handlers/research.go: POST /api/research/trigger handler (202 Accepted)
- internal/api/router.go: researchHandler param + /api/research/trigger route
- cmd/hwlab/main.go: research agent goroutine started with 10min interval
2026-04-10 07:51:13 +00:00
30cd279f49 feat(07-01): SearXNG client, ListDevicesWithStatus, SearXNGURL config
- internal/research/searxng.go: SearXNGClient implementing ai.ResearchClient
- internal/research/searxng_test.go: httptest mock server tests (4 pass)
- internal/netbox/client.go: ListDevicesWithStatus client-side filter
- internal/config/config.go: SearXNGURL field with default + env binding
2026-04-10 07:48:22 +00:00
34e0803661 docs(07): create phase 7 plans — research agent and NL search
2 plans, 2 waves: SearXNG client + ResearchAgent (wave 1),
NL search endpoint + dashboard search bar (wave 2). Covers AI-04 + UI-03.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 07:45:56 +00:00
987dc4b97c docs(07): auto-generated context (research + search) 2026-04-10 07:42:21 +00:00
16a469bfdd docs(phase-6): complete phase execution 2026-04-10 07:41:49 +00:00
84b761fdda docs(06): phase 6 verification + human UAT (OpenRouter key needed) 2026-04-10 07:41:49 +00:00
f34cf401a0 docs(06-03): complete AdvisorPage plan — streaming chat UI, /advisor route, TopBar link 2026-04-10 07:40:39 +00:00
bcc360892c feat(06-03): add AdvisorPage, /advisor route, and TopBar link
- AdvisorPage: two-panel layout (sidebar + chat), streaming SSE tokens
- Sidebar: conversation list with refetchInterval 5s, New Chat button
- Chat: optimistic user messages, streaming cursor, model dropdown
- router.tsx: lazy AdvisorPage import + advisorRoute at /advisor
- TopBar.tsx: MessageSquare icon + Advisor link between Test and Scan
- AppShell.tsx: noPadding prop for full-height layouts
2026-04-10 07:39:56 +00:00
811223ddf7 feat(06-03): add advisor API wrappers (streamChat, fetchConversations, fetchConversation)
- 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
2026-04-10 07:39:50 +00:00
ae34bc73fe docs(06-lab-advisor-02): complete advisor backend plan
- 06-02-SUMMARY.md: SSE streaming chat, InventoryContextBuilder, 3 endpoints
2026-04-10 07:37:15 +00:00
0190e8583c feat(06-lab-advisor-02): AdvisorHandler SSE streaming + router wiring
- internal/advisor/handler.go: StreamChat (SSE, token-by-token),
  GetConversations, GetConversation; body limited to 64KB, message
  truncated to 8000 chars (T-06-02-03); API key never echoed (T-06-02-02)
- internal/api/router.go: /api/advisor/{chat,conversations,conversations/{id}}
  with nil-guard returning 503 when DB not configured
- internal/config/config.go: Tier3 defaults + HWLAB_AI_TIER3_* env bindings
- cmd/hwlab/main.go: store init from HWLAB_DATABASE_URL, RunMigrations,
  InventoryContextBuilder, AdvisorHandler wired into NewRouter
2026-04-10 07:36:16 +00:00
7b02e67365 feat(06-lab-advisor-02): add InventoryContextBuilder with 60s cache
- internal/advisor/context.go: BuildContext assembles compact NetBox
  summary (item count, category breakdown, recent 20 items)
- Caches result under sync.Mutex for defaultTTL=60s
- Stale cache returned on NetBox error rather than propagating failure
- internal/ai/types.go: add Tier3 TierConfig field to AIConfig
  (required by AdvisorHandler for OpenRouter Claude Opus access)
2026-04-10 07:35:43 +00:00
8237077728 docs(06-01): complete PostgreSQL store package plan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 07:31:59 +00:00
623cff0d76 feat(06-01): add conversation and message CRUD methods with integration tests
- CreateConversation, AddMessage, GetConversation, ListConversations on *Store
- ErrNotFound sentinel for unknown conversation IDs
- Message, Conversation, ConversationSummary types
- LIMIT 100 soft guard on ListConversations (T-06-01-04)
- Integration tests cover full round-trip, invalid role, ErrNotFound, ordering
2026-04-10 07:31:12 +00:00
4bc22dc7b9 feat(06-01): add pgx/v5 store with connection pool and migrations
- Store struct wrapping pgxpool.Pool with NewStore/Close/Pool
- RunMigrations creates conversations + messages tables idempotently
- DSN never logged to avoid credential exposure (T-06-01-02)
- All queries parameterized (T-06-01-01)
2026-04-10 07:31:08 +00:00
7a304c8cc4 docs(06): create phase 6 lab advisor plans (3 plans, 3 waves) 2026-04-10 07:28:20 +00:00
ef20f8e83f docs(06): auto-generated context (lab advisor) 2026-04-10 07:23:51 +00:00
78b1dbb6ba docs(phase-5): complete phase execution 2026-04-10 07:23:20 +00:00
e48d53ed94 docs(05): phase 5 verification + human UAT (hardware blocked) + vitest deps 2026-04-10 07:23:20 +00:00
9e7e2c7cd8 docs(05-03): complete Cable Test Station plan summary 2026-04-10 07:21:50 +00:00
4e5a3547f9 test(05-03): add CableTestPage unit tests with Vitest setup
- Install vitest, @testing-library/react, jsdom (Rule 3: missing test infra)
- Add vitest.config.ts with jsdom environment and @ alias
- Add src/test/setup.ts with jest-dom matchers and matchMedia stub
- Add CableTestPage.test.tsx: 4 tests (no-tester, recent list, print success, print_skipped)
- Mock AppShell to avoid TanStack Router context in tests
- All 4 tests pass; npm run build exits 0
2026-04-10 07:21:08 +00:00
499dbb9929 feat(05-03): Cable Test Station page at /test
- 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)
2026-04-10 07:19:24 +00:00
c9f874b7f4 docs(05-02): complete NetBox CreateCable + TestHandler plan
- SUMMARY.md: 2 tasks, 2 commits, threat mitigations T-05-03/04/05 applied
- Deviation: go-netbox Cable model required url/display fields in test mock
2026-04-10 07:16:50 +00:00
1764c7b06a feat(05-02): add TestHandler with 3 cable-test endpoints + router wiring
- TestHandler: POST /api/test/cable, GET /api/test/events, GET /api/test/recent
- POST /api/test/cable: DisallowUnknownFields (T-05-03), creates NetBox cable,
  auto-prints with 1s rate limit (T-05-05), prepends to 20-item ring buffer
- GET /api/test/events: SSE, 30s keepalive, exits on context cancel (T-05-04)
- GET /api/test/recent: thread-safe ring buffer, returns [] when empty
- AttachStream() wires StreamingTesterDriver channel to SSE broadcaster
- Router: three /api/test/* routes added to NewRouter signature
- main.go: constructs TestHandler, wires USB Manager event loop stub
- All handler tests pass race-clean (7 test cases)
2026-04-10 07:15:30 +00:00
7908d40af3 feat(05-02): add CreateCable method and CableRecord type to netbox client
- CableRecord type added to types.go (ID, HWID, Label, TestData, CatalogStatus)
- CreateCable(ctx, label, assetTag, testDataJSON) uses DcimCablesCreate
- Sets test_data and catalog_status custom fields; hw_id if assetTag non-empty
- Rejects empty label with sentinel error message
- Unit tests use httptest.NewUnstartedServer (201 success, 422 error, empty label)
2026-04-10 07:12:00 +00:00
db287c238f docs(05-01): complete TesterDriver interface + mock drivers plan
- TesterDriver, StreamingTesterDriver interfaces defined
- 4 mock drivers: MockUSBDriver, MockDPDriver, MockHDMIDriver, MockFNB58Driver
- KnownDevices extended with 4 RoleCableTester placeholder entries
- All tests pass, race detector clean
2026-04-10 07:08:46 +00:00
11aea60aee feat(05-01): TesterDriver interface, 4 mock drivers, KnownDevices tester entries
- TesterDriver interface: Connect/Read/Disconnect
- StreamingTesterDriver interface: embeds TesterDriver, adds Stream()
- TestResult struct: CableType, versions, speed, power, continuity, eMarker, resistance
- LiveReading struct: Voltage, CurrentAmps, PowerWatts, PDProtocol, Timestamp
- MockUSBDriver: deterministic USB 3.2 Gen 2 result, ErrNotConnected guard
- MockDPDriver: deterministic DP 1.4 result
- MockHDMIDriver: deterministic HDMI 2.1 result
- MockFNB58Driver: 3 LiveReading samples at 100ms, context-cancelled via Disconnect()
- KnownDevices: 4 RoleCableTester placeholder entries (dead0:0001-0004)
2026-04-10 07:07:27 +00:00
384ffac870 test(05-01): add failing tests for TesterDriver, mock drivers, FNB58 streaming 2026-04-10 07:06:23 +00:00
984f67834f docs(05): create phase 5 plans — tester drivers, backend endpoints, cable test UI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 07:04:57 +00:00
468f8930d1 docs(05): auto-generated context (cable test phase) 2026-04-10 07:00:24 +00:00
ac190abafe docs(phase-4): complete phase execution 2026-04-10 06:59:45 +00:00
93d26ebf55 docs(04): phase 4 verification + human UAT (hardware blocked) 2026-04-10 06:59:45 +00:00
36f7e92cf2 Merge branch 'worktree-agent-a3099381' 2026-04-10 06:58:42 +00:00
de807ba931 docs(04-05): complete frontend USB status bar and print label plan 2026-04-10 06:58:21 +00:00
47f351b40a docs(04-04): complete intake auto-print integration plan 2026-04-10 06:58:16 +00:00
f9b1b3ff29 feat(04-04): integrate auto-print into intake handler
- Add IntakePrinter interface to intake.go (optional, nil-safe)
- Add printer field to IntakeHandler, update NewIntakeHandler signature
- Add PrintSkipped bool to IntakeResponse (json: print_skipped)
- Auto-print label after NetBox record creation using labels.RenderStandard + printer.ImageToRawBitmap
- Printer errors are non-fatal: logged and surfaced via print_skipped=true
- Update main.go to pass mockDriver as IntakePrinter
- Add 4 new tests covering success, ErrNoDevice, nil printer, and non-fatal error
- All 10 intake tests pass (6 existing + 4 new)
2026-04-10 06:57:27 +00:00
4d2d35e277 docs(04-03): complete printer driver + HTTP endpoints plan — MockDriver, SSE, rate limiter 2026-04-10 06:53:59 +00:00
9f57cbdf6c feat(04-03): LabelHandler, USBEventsHandler, router wiring, main.go USB+printer init
- LabelHandler: POST /api/labels/:deviceID/print with 1/s rate limit (T-04-09)
- USBEventsHandler: GET /api/usb/events SSE stream, exits on context cancel (T-04-11)
- router.go: two new parameters + routes wired
- main.go: USB Manager started with ctx, MockDriver connected, handlers passed to router
2026-04-10 06:52:52 +00:00
dd381eefa3 test(04-03): add failing handler tests for label print and USB SSE (TDD RED) 2026-04-10 06:50:51 +00:00
1156eff896 feat(04-03): PrinterDriver interface, MockDriver, PrtQutieDriver stub
- PrinterDriver interface: Connect/Print(bitmap,w,h)/Disconnect
- ImageToRawBitmap(): 1-bit packed row-major converter from image.Image
- MockDriver: saves PNG to SaveDir (/tmp default) for visual inspection
- PrtQutieDriver: stub returns ErrNoDevice — safe before hardware arrives
- ErrNoDevice, ErrNotConnected, ErrEmptyBitmap sentinel errors
2026-04-10 06:50:12 +00:00
b223b54a87 test(04-03): add failing printer driver tests (TDD RED) 2026-04-10 06:49:31 +00:00
2d6765077c Merge commit 'bec54df' 2026-04-10 06:47:55 +00:00
bec54df2e0 docs(04-02): complete label rendering plan — QR code + bitmap renderers 2026-04-10 06:47:13 +00:00
63b66f7a94 docs(04-01): complete USB manager plan — goroutine-per-device, VID/PID enumeration
- 10/10 tests pass with -race flag
- goroutine count stable across 5 replug cycles
- T-04-01 read buffer cap mitigated
- Known stubs: VID/PID placeholder pending hardware 2026-04-13
2026-04-10 06:46:07 +00:00
7800917500 feat(04-02): cable label renderer and IsCableDevice detection
- Add CableLabelData struct and RenderCable() producing 384x180 bitmap
- Cable template: HW ID, name, USB version/speed, wattage/test date
- IsCableDevice() detects cable records by name or AINotes heuristic
- T-04-07 mitigation applied: HWID format validated in RenderCable too
- All 5 cable tests pass (10 total in package)
2026-04-10 06:45:48 +00:00
82eaf6bed7 feat(04-01): USB Manager goroutine-per-device, poll loop, reconnect, leak-safe teardown
- Manager with injectable enumerateFunc and serialOpener for test isolation
- goroutine-per-device model: deviceLoop owns one serial port per device
- context+done-channel teardown: inner read goroutine exits on ctx.Done()
- Read buffer capped at 4096 bytes (T-04-01 threat mitigation)
- Poll loop reconciles prev/current snapshots for connect/disconnect events
- ErrDeviceNotConnected returned by Send() when device absent
- All 5 manager tests pass with -race flag; goroutine count stable across 5 replug cycles
- mockPort test helper blocks Read until Close() unblocks it (realistic behavior)
2026-04-10 06:45:26 +00:00