From b1e4db0b6d1e52bbb60a4dc31020d94e22b0ea59 Mon Sep 17 00:00:00 2001 From: Mikkel Georgsen Date: Fri, 10 Apr 2026 01:11:41 +0000 Subject: [PATCH] docs(01): fix plan checker blockers (research resolution, catalog updater) --- .planning/phases/01-foundation/01-04-PLAN.md | 42 +++++++++++++++++++ .planning/phases/01-foundation/01-RESEARCH.md | 21 +++------- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/.planning/phases/01-foundation/01-04-PLAN.md b/.planning/phases/01-foundation/01-04-PLAN.md index 2c030b9..6afda97 100644 --- a/.planning/phases/01-foundation/01-04-PLAN.md +++ b/.planning/phases/01-foundation/01-04-PLAN.md @@ -10,6 +10,7 @@ files_modified: - internal/netbox/hwid_test.go - internal/inventory/quality_gate.go - internal/inventory/quality_gate_test.go + - internal/inventory/catalog_updater.go - internal/inventory/types.go - internal/netbox/tags.go - internal/netbox/tags_test.go @@ -424,6 +425,45 @@ func BuildCustomFieldsPatch(hwID, catalogStatus string, photoURLs []string) map[ } ``` + 2b. Create `internal/inventory/catalog_updater.go` — wires quality gate transitions to NetBox persistence: + ```go + package inventory + + import ( + "context" + "fmt" + + "git.georgsen.dk/hwlab/internal/netbox" + ) + + // CatalogUpdater persists quality gate transitions to NetBox. + type CatalogUpdater struct { + client *netbox.Client + } + + // NewCatalogUpdater creates a CatalogUpdater backed by the given NetBox client. + func NewCatalogUpdater(client *netbox.Client) *CatalogUpdater { + return &CatalogUpdater{client: client} + } + + // UpdateCatalogStatus validates the transition from current to next status + // and persists the result to NetBox via PatchCustomFields. + // Returns the new status on success. + func (u *CatalogUpdater) UpdateCatalogStatus(ctx context.Context, deviceID int, current, next CatalogStatus) (CatalogStatus, error) { + newStatus, err := Transition(current, next) + if err != nil { + return "", err + } + patch := map[string]interface{}{ + "catalog_status": string(newStatus), + } + if err := u.client.PatchCustomFields(ctx, deviceID, patch); err != nil { + return "", fmt.Errorf("persist catalog_status to NetBox: %w", err) + } + return newStatus, nil + } + ``` + 3. Create `internal/inventory/quality_gate_test.go`: ```go package inventory_test @@ -645,6 +685,8 @@ func BuildCustomFieldsPatch(hwID, catalogStatus string, photoURLs []string) map[ - `grep "StatusComplete.*{}" internal/inventory/quality_gate.go` returns the terminal state entry with empty transitions - `grep "invalid transition" internal/inventory/quality_gate.go` returns error string in Transition() - `ensureTag` in `internal/netbox/tags.go` is implemented with real go-netbox v4 TagRequest (not stub returning error string) + - `grep "UpdateCatalogStatus" internal/inventory/catalog_updater.go` returns the function that calls Transition() then PatchCustomFields + - `grep "PatchCustomFields" internal/inventory/catalog_updater.go` confirms NetBox persistence wiring - `go build ./internal/inventory/... ./internal/netbox/...` exits 0 diff --git a/.planning/phases/01-foundation/01-RESEARCH.md b/.planning/phases/01-foundation/01-RESEARCH.md index c9cf933..d89b65c 100644 --- a/.planning/phases/01-foundation/01-RESEARCH.md +++ b/.planning/phases/01-foundation/01-RESEARCH.md @@ -572,26 +572,15 @@ Create via DCIM API endpoints: --- -## Open Questions +## Open Questions (RESOLVED) -1. **Real NetBox API token** - - What we know: The `.env` file has a placeholder string `homelab-netbox-api-token-2024` - - What's unclear: The real token value; whether the current user account has admin permissions to create custom fields - - Recommendation: Wave 0 first task — generate real token via NetBox UI, update `.env` +1. **Real NetBox API token** — RESOLVED: Integration tests use `t.Skip()` when placeholder token detected. Operator generates real token via NetBox UI before running provisioning (Plan 03). -2. **netbox-inventory plugin installation (NB-03)** - - What we know: Plugin exists at github.com/ArnesSI/netbox-inventory; requires SSH to LXC 130 - - What's unclear: Whether the LXC 130 has internet access for pip install, or if the plugin must be transferred manually - - Recommendation: Treat as a manual pre-phase step with a Makefile verification target +2. **netbox-inventory plugin installation (NB-03)** — RESOLVED: Plan 03 implements runtime check via `/api/plugins/` endpoint with CLI warning if absent. Installation is a manual pre-requisite (SSH to LXC 130). -3. **photo_urls field type** - - What we know: The requirement lists `photo_urls` as a custom field - - What's unclear: NetBox doesn't have a native array-of-URLs field type. Options: JSON field (store as JSON array string), multiline-text (one URL per line), or multiple object attachments - - Recommendation: Use `json` field type and store as a JSON array string; parse in Go +3. **photo_urls field type** — RESOLVED: Plan 03 Task 1 uses `text` type with comma-separated URLs (simpler than JSON, sufficient for Phase 1). -4. **PRD vs. REQUIREMENTS.md design system discrepancy** - - What we know: PRD says "Tokyo Night theme"; CONTEXT.md and PROJECT.md say "ClickHouse design system (pure black + neon volt)" - - Recommendation: CONTEXT.md wins — use ClickHouse design system. The stub Phase 1 frontend needs no real styling. +4. **PRD vs. REQUIREMENTS.md design system discrepancy** — RESOLVED: CONTEXT.md and PROJECT.md specify ClickHouse design system. Plan 01 stub SPA uses ClickHouse colors. ---