Commit graph

29 commits

Author SHA1 Message Date
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
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
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
8cbd840cba feat(04-02): standard label renderer with QR code generation
- Add internal/labels package with LabelData struct and RenderStandard()
- QR encodes http://mac-mini.mg:8080/hw/HW-XXXXX (LBL-01)
- Label is 384x120px NRGBA, white background, basicfont text
- T-04-07 mitigation: validate HWID format before qrcode.New()
- Install github.com/skip2/go-qrcode and golang.org/x/image
- All 5 renderer tests pass
2026-04-10 06:44:11 +00:00
f5b1d3156c feat(04-01): USB device types, KnownDevices registry, VID/PID enumeration
- DeviceSpec, DeviceRole, DeviceState, DeviceEvent, Command types
- KnownDevices registry with PRT Qutie placeholder (0525:a4a7)
- enumerateConnected() with system_profiler JSON parsing + injectable test seams
- ParseVIDPID() helper with error on malformed input
- All 5 device_test.go tests pass
- go.bug.st/serial v1.6.4 added to go.mod
2026-04-10 06:43:43 +00:00
743611f488 feat(03-02): wire GET /api/inventory and GET /api/inventory/{id} routes
- NewRouter signature extended to accept *handlers.InventoryHandler
- GET /inventory and GET /inventory/{id} registered in /api route group
- main.go constructs handlers.NewInventoryHandler(nbClient) and passes to NewRouter
2026-04-10 06:15:12 +00:00
b0b6153b24 feat(03-02): InventoryHandler with list+detail endpoints and 7 unit tests
- InventoryNetBoxClient interface (ListDevices + GetDevice) for testability
- ListInventory returns 200 JSON array (limit=200, 502 on NetBox error)
- GetInventoryItem returns 200/404/400/502 based on ID validity and NetBox response
- deviceToResponse maps netbox.Device to InventoryItemResponse (nil PhotoURLs → [])
- 7 TDD tests: empty list, 2-item list, NetBox error, found, not found, invalid ID, server error
2026-04-10 06:14:43 +00:00
59aa89b199 feat(02-03): wire POST /api/intake route, real WAQ handler, and NetBox defaults in config
- router.go: NewRouter accepts intakeHandler http.Handler, registers POST /api/intake
- config.go: adds NetBoxDefaultDeviceTypeID/RoleID/SiteID fields with defaults and env bindings
- main.go: creates netbox.Client, ai.Orchestrator, inventory.CatalogUpdater, handlers.IntakeHandler
- main.go: replaces NoOpHandler with NewNetBoxOpHandler(nbClient) for WAQ worker
- main.go: uses typed interface variable for WAQ to avoid nil-interface-wrapping bug
2026-04-10 05:55:41 +00:00
4fc9362519 feat(02-03): POST /api/intake handler with orchestrator and NetBox wiring
- IntakeHandler with IntakeOrchestrator/IntakeNetBoxClient/IntakeCatalogUpdater/IntakeWAQ interfaces
- Validates 1-3 photos, base64-encodes, calls Analyze, allocates HW-ID
- Quick-add mode: confidence >= threshold skips review, creates NetBox record immediately
- WAQ enqueue on NetBox failure returns 202 with queued=true
- nil WAQ + NetBox down returns 503
- Six unit tests: reject-0, reject-4, high-confidence, low-confidence, quick-add, netbox-down
- [Rule 1 - Bug] PatchCustomFields signature changed int -> int64 to match NetBoxOpsClient interface
- [Rule 1 - Bug] UpdateCatalogStatus signature changed int -> int64 for consistency with CreateDevice return type
2026-04-10 05:54:33 +00:00
73eab561cf feat(02-02): WAQ real NetBox op handler replacing NoOpHandler
- NewNetBoxOpHandler routes create_device → CreateDevice, patch_custom_fields → PatchCustomFields
- NetBoxOpsClient interface enables test injection without importing netbox package
- Unknown op types return error (re-queued by worker, not silently dropped — T-02-08)
- JSON payloads decoded into typed structs (T-02-07 tampering mitigation)
- 6 handler tests all passing (TDD green); NoOpHandler untouched in worker.go
2026-04-10 05:48:30 +00:00
799acd26ef feat(02-02): three-tier orchestrator with confidence routing and research stub
- Orchestrator.Analyze: tier1 → confidence check → tier2 escalation if < threshold
- CatalogStatus mapped from confidence: >= threshold → StatusIndexed, else StatusNeedsResearch
- Both tiers fail gracefully: returns zero-value IntakeResult + StatusNeedsResearch, err nil
- ResearchClient interface + NoOpResearchClient stub for Phase 7 SearXNG
- 5 TestOrchestrator* tests all passing (TDD green)
2026-04-10 05:47:41 +00:00
8c03780230 feat(02-01): AI package foundation — types, interface, mock, prompts, config extension
- internal/ai/types.go: IntakeRequest, IntakeResult, TierConfig, AIConfig domain types
- internal/ai/client.go: AIClient interface + TierClient (go-openai, BaseURL tier-routing)
- internal/ai/mock.go: MockAIClient test double with HighConfidenceResult/LowConfidenceResult fixtures
- internal/ai/prompts/intake.go: BuildIntakePrompt() JSON-extraction prompt template
- internal/config/config.go: Config.AI AIConfig field, tier defaults, env bindings, ai_config.json merge
- ai_config.json: template config with placeholder Tier2 API key
- .gitignore: add ai_config.local.json pattern for real keys (T-02-01 mitigation)
- All tests pass: TestMockAIClient, TestMockAIClientError, TestTierClientConstruction, TestAIConfigDefaults
2026-04-10 05:45:13 +00:00
6040ecc3cc feat(02-01): install go-openai and add CreateDevice to NetBox client
- go get github.com/sashabaranov/go-openai v1.41.2
- Add CreateDevice(ctx, name, assetTag, deviceTypeID, roleID, siteID) → (int64, error)
- Add DeleteDevice(ctx, id) for test cleanup
- Use Int32As* oneOf helpers for go-netbox v4 FK fields
- TestCreateDeviceValidation PASS; TestCreateDeviceLive SKIP (no live token)
2026-04-10 05:42:51 +00:00
28e2f5a879 Merge branch 'worktree-agent-a86e8ac9' 2026-04-10 05:23:48 +00:00
1f9621fcaa feat(01-04): quality gate state machine, tag sync, catalog updater
- CatalogStatus type with forward-only state machine (draft→indexed→…→complete)
- Transition() enforces valid transitions, returns error with 'invalid transition' message
- ParseCatalogStatus() validates known status strings
- HardwareRecord domain type composing netbox.Device with quality gate state
- CatalogUpdater.UpdateCatalogStatus() validates transition then PatchCustomFields
- SyncTags() normalizes tags (slug form) and ensures they exist in NetBox
- normalizeTags deduplicates across case/whitespace/space-vs-hyphen variants
- ensureTag uses go-netbox v4 NewTagRequest(name, slug) / ExtrasTagsCreate
- All 12 state machine table-driven tests pass
2026-04-10 05:22:22 +00:00
d1192c3380 feat(01-05): WAQ retry worker and graceful shutdown wiring
- Add RunWorker: BLPOP loop with context cancellation and retryInterval backoff
- Add NoOpHandler: Phase 1 placeholder that drains ops with a log line
- Drop ops after maxAttempts with warning log (T-05-03 mitigation)
- Update main.go: non-fatal WAQ init, graceful HTTP shutdown on SIGINT/SIGTERM
2026-04-10 05:22:10 +00:00
e07ad922eb feat(01-05): write-ahead queue core (Enqueue, Dequeue, Len)
- Add PendingOp struct with UUID, type, payload, created_at, attempts
- Add WAQ type backed by DragonFlyDB via go-redis v9
- Implement Enqueue (RPUSH), Dequeue (BLPOP), Len (LLEN)
- Custom URL parser handles passwords with forward slashes
- Unit tests pass; integration test passes against DragonFlyDB at 10.5.0.10:6379
2026-04-10 05:21:28 +00:00
9b4cc9a661 feat(01-03): implement custom field provisioning with go-netbox v4
- Define hwlabCustomFields slice with all 8 HWLab custom field specs
- Implement ProvisionCustomFields with idempotent check-before-create
- Implement createCustomField using WritableCustomFieldRequest
- Implement customFieldSpec lookup for testing
2026-04-10 05:21:23 +00:00
e1cee31620 feat(01-04): HW-XXXXX sequential ID allocation
- formatHWID/parseHWID with HW-NNNNN regex validation
- AllocateNextHWID with optimistic-lock retry (3 attempts)
- getHighestHWIDNumber scans all devices for highest existing asset_tag
- hwIDExists checks specific asset_tag via DcimDevicesList filter
- Unit tests for format/parse covering valid and invalid cases
2026-04-10 05:20:41 +00:00
07130d2ceb test(01-03): add failing tests for custom field spec and provisioning 2026-04-10 05:20:37 +00:00
17a2eb6f9f feat(01-02): custom field read/write wrappers (NB-02)
- Add ParseCustomFields: safe type-assertion mapping from map[string]interface{}
- Add BuildCustomFieldsPatch: selective flat patch map (avoids clearing unset fields)
- Add BuildFullCustomFieldsPatch: full custom fields patch for initial record creation
- Add PatchCustomFields method on Client using PatchedWritableDeviceWithConfigContextRequest
- Add custom_fields_test.go with 5 unit tests and 1 skippable integration round-trip test
2026-04-10 05:17:11 +00:00
9f3ed9fddc feat(01-02): NetBox client wrapper with device CRUD (NB-01)
- Add internal/netbox/types.go with Device and CustomFields domain types
- Add internal/netbox/client.go with NewClient, Ping, ListDevices, GetDevice
- Add client_test.go with validation unit tests and skippable integration tests
- go-netbox v4.3.0 dependency added
2026-04-10 05:16:17 +00:00
6595e345a2 feat(01-foundation-01): viper config loader and SPA fallback fix
- Add internal/config/config.go with viper-backed Config struct
- Explicit BindEnv calls for reliable env var -> config mapping (mapstructure v2 compat)
- Config loads from config.json + .env, env vars take precedence
- Add config.json with non-secret defaults (port, timeouts, URLs)
- Fix SPA fallback: spaHandler serves index.html for unknown paths (client-side routing)
- All 5 tests pass: TestHealth, TestLoadDefaults, TestLoadEnvOverride, TestLoadNetBoxURL
- Add Makefile with build/dev/test/clean targets
2026-04-10 01:27:54 +00:00
77e5a78d5a feat(01-foundation-01): Go module init, chi server, go:embed SPA scaffold
- 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
2026-04-10 01:17:03 +00:00