# Execution: provider blueprint Plan: [docs/plans/2026-09-16-0841-provider-blueprint.md](../plans/2026-09-16-0841-provider-blueprint.md) - [x] Step 0 — Save the plan; wait for the merge - [x] Step 1 — Collect evidence (read-only, in parallel) - [x] Step 2 — Write `docs/providers.md` - [x] Step 3 — Verify, then open the MR ## Step 0 — Save the plan; wait for the merge The plan was committed on `feat/provider-health` (`80b2e9c`) and reached `main` through PR #6. After the user confirmed the merge, the branch was cut from the updated `main`: ```bash git switch main && git pull --ff-only git switch -c feat/provider-blueprint ``` Worth noting: `.claude/settings.json` has local, uncommitted permission edits that carried over through the switch. They are deliberately left out of this branch. ## Step 1 — Collect evidence (read-only, in parallel) Three Explore agents answered the plan's 12-item questionnaire, one each for kubernetes, gcp and kvm. Meanwhile I read the shared layer: `internal/provider/*.go`, `registry.go`, the provider wiring in `cmd/main.go`, `reconcileManaged`/`replaceInstance`/`reconcileDelete` in the controller, `internal/gc/gc.go`, and how the monitor uses `Checker`. Before relying on the agents' claims, I spot-checked them against the code. One was wrong and did not go into the doc: the kvm agent said `Delete` skips volume removal when `DomainDestroy` returns `ErrNoDomain`. In fact `removeDomain` returns nil there, and `Delete` still calls `removeVolumes` afterwards (`kvm.go:477-482`). Rules that exist only in the callers, and not in any comment, were found by reading the controller and GC. They went into the doc as MUST rules: - A zero `CreatedAt` makes an instance immediately collectable (`gc.go:120`). - `Get` returning `ErrPermanent` on the deletion path makes the reconciler call `Delete` anyway. - Replacement waits for `Get` to return NotFound before creating an instance with the same name. - `Stopped` and `Terminated` both lead to delete-and-recreate. Worth noting, found outside this plan's scope and not changed here: - `docs/gcp-vm-validation.md` filters on `labels.proxy-operator-managed=yes`, but the value is `true`. - According to the gcp agent, `docs/gcp-in-specific-project.md` holds a real project ID, IAM output and an egress IP. The user was told. ## Step 2 — Write `docs/providers.md` Wrote the eight sections the plan lists. Linked the new doc from the Provider contract row in `docs/architecture.md` and from the CLAUDE.md "Architecture" section. Judgment calls: - The file:line evidence sits in per-provider notes under the matrix, as planned. The contract rules in §2 cite the reconciler and GC lines each rule protects. - The gap list includes documentation drift (kvm-provider.md's ARP order and log keys, gcp-vm-validation.md's label value and its claim that async errors reach status), as well as code gaps. Both would mislead the next provider author in the same way. - "Proposed features" stays a flat list with no design, as the user decided. Worth noting: the line numbers in the doc are a snapshot. The doc says so and asks readers to look for the named function when a line has moved. ## Step 3 — Verify, then open the MR An Explore agent re-checked the doc against the code: about 150 claims, including every matrix cell. It found four mismatches, and all four were fixed: - **Startup dial:** I had listed kvm-provider.md's "connection kept open from startup" as doc drift. It isn't: the pool dials lazily, but the provider health monitor runs a check as soon as the leader starts (`monitor.go:147`, default interval 1m), and that check opens the connection. The bullet was removed and the kvm notes now say this. - **Checker wording:** §5 said the monitor type-asserts `Checker`. It is `cmd/main.go:216`; the monitor only sees nil or not. - **Reasons, not conditions:** §3 called `QuotaExceeded` and `PermanentError` conditions. They are reasons on `Provisioned`. - **Latch lines:** the line range is `:133-139`, not `:130-135`. It also noted that none of the providers assert `var _ provider.Provider`, and that the kubernetes provider's RBAC markers live in the controller package. The §7 checklist now says both. Link check (every relative `](…)` target and anchor in the new and edited docs resolves), then the sanity build: ```bash go build ./... && go test -short ./... # all packages ok ``` Worth noting: no CHANGELOG entry yet. Per CLAUDE.md it goes in after the user confirms the doc.