--- phase: 04-usb-manager-label-printing plan: "04" subsystem: intake-handler tags: [intake, printing, label, non-fatal, dependency-injection] dependency_graph: requires: [04-03, 04-02] provides: [auto-print-on-intake] affects: [cmd/hwlab/main.go, internal/api/handlers/intake.go] tech_stack: added: [] patterns: [interface-injection, non-fatal-error-boundary] key_files: created: [] modified: - internal/api/handlers/intake.go - internal/api/handlers/intake_test.go - cmd/hwlab/main.go decisions: - "Use result.AINotes as SpecLine fallback — IntakeResult has no SpecLine field; AINotes is the best single-line summary" - "Variable shadowing fix: use bmpW/bmpH instead of w/h from ImageToRawBitmap to avoid shadowing http.ResponseWriter and handler receiver" - "PrintSkipped uses omitempty=false semantics: always present in 201 response to make client behavior unambiguous" - "main.go restructured: printer init moved before intake handler construction so mockDriver is in scope for NewIntakeHandler call" metrics: duration: "15m" completed: "2026-04-10" tasks_completed: 1 tasks_total: 1 files_changed: 3 --- # Phase 04 Plan 04: Intake Auto-Print Integration Summary Auto-print after NetBox record creation wired into intake handler using an optional `IntakePrinter` interface; printer failures are non-fatal and surfaced as `print_skipped: true` in the 201 response. ## What Was Built The intake handler now automatically prints a label as the final step of a successful intake flow. After `UpdateCatalogStatus` completes, the handler calls `labels.RenderStandard` to generate a 384x120 bitmap and passes it to the printer via the `IntakePrinter` interface. Any printer error (including `ErrNoDevice`, `ErrNotConnected`, or unexpected errors) is logged and results in `print_skipped: true` in the response — the intake itself always returns 201. ## Tasks Completed | Task | Name | Commit | Files | |------|------|--------|-------| | 1 | Add IntakePrinter interface + auto-print to intake handler | f9b1b3f | intake.go, intake_test.go, main.go | ## Decisions Made 1. **AINotes as SpecLine fallback** — `ai.IntakeResult` has no `SpecLine` field. `result.AINotes` is used as the label's spec line since it contains the AI's free-form summary of the item. 2. **Variable name fix (bmpW/bmpH)** — `printer.ImageToRawBitmap` returns `([]byte, int, int)`. Using `w, h` as variable names shadowed the `http.ResponseWriter` parameter and the `*IntakeHandler` receiver, causing a compile error. Renamed to `bmpW`/`bmpH`. 3. **main.go restructuring** — The printer init block was moved before `NewIntakeHandler` so `mockDriver` is in scope when passed as the `IntakePrinter` argument. 4. **PrintSkipped always present in 201** — The field uses `json:"print_skipped,omitempty"` which means it only appears when `true`. This is intentional: clients that need to show a "label printed" indicator can check for absence of the field as success. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 1 - Bug] Variable shadowing in auto-print block** - **Found during:** Task 1 (GREEN phase — compile error) - **Issue:** `bitmap, w, h := printer.ImageToRawBitmap(img)` shadowed `w http.ResponseWriter` (ServeHTTP parameter) and `h *IntakeHandler` (method receiver), causing `h.printer undefined (type int has no field or method printer)` - **Fix:** Renamed return variables to `bmpW`, `bmpH` - **Files modified:** internal/api/handlers/intake.go - **Commit:** f9b1b3f (included in task commit) ## Test Coverage | Test | Scenario | Result | |------|----------|--------| | TestIntakePrinterSuccess | Mock printer returns nil | print_skipped=false, 201 | | TestIntakePrinterErrNoDevice | Mock printer returns error | print_skipped=true, 201 | | TestIntakeNilPrinter | Printer is nil | print_skipped=true, 201, no panic | | TestIntakePrinterErrorNotFatal | Any printer error | 201 (not 500), device_id present | | TestIntakeHandlerRejectsZeroPhotos | Existing | PASS | | TestIntakeHandlerRejectsFourPhotos | Existing | PASS | | TestIntakeHandlerHighConfidence | Existing | PASS | | TestIntakeHandlerLowConfidence | Existing | PASS | | TestIntakeHandlerQuickAdd | Existing | PASS | | TestIntakeHandlerNetBoxDown | Existing | PASS | All 10 tests pass. `go build ./...` clean. ## Known Stubs None — `labels.RenderStandard` and `printer.ImageToRawBitmap` are fully implemented. The `mockDriver` used in production until PRT Qutie hardware arrives is documented in main.go with a TODO. ## Threat Flags No new trust boundaries introduced. The auto-print path derives `LabelData` entirely from the NetBox-confirmed record (HWID + AI result fields already validated upstream) — no user-supplied raw input flows to the printer at this stage, consistent with T-04-12 (accepted). ## Self-Check: PASSED - FOUND: internal/api/handlers/intake.go - FOUND: internal/api/handlers/intake_test.go - FOUND: cmd/hwlab/main.go - FOUND: commit f9b1b3f