- 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)
- 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
- 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
- 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