# Plan: Provider blueprint (docs/providers.md) **Created:** 2026-09-16 08:41 ## Context The provider contract is spread across `docs/prompts/__initial-prompt.md` §5, the comments in `internal/provider/{provider,errors,check}.go`, `docs/architecture.md` §5 and §11, `docs/kvm-provider.md`, and plan Step 2/3/8. Many rules exist only in code: providerID must be self-contained, UID/CreatedAt must be read back, Create must survive a crash partway through, calls must stop when ctx is cancelled, decorators are wired in `cmd/main.go`, and so on. With "~5 providers soon", the next provider author needs one blueprint. It should say what a provider MUST, SHOULD or MAY do, show which features each existing provider (kubernetes, gcp, kvm) actually implements (with file:line evidence), give a checklist for adding one, and list the gaps. Decisions (user): store in `docs/providers.md`; scope = current state plus a separate "Gaps and proposed features" section (nothing implemented); wait for `feat/provider-health` to merge, then branch off `main`. ## Step 0 — Save the plan; wait for the merge - Right after ExitPlanMode, copy this plan to `docs/plans/2026-09-16-0841-provider-blueprint.md` and commit it on its own. The current branch is `feat/provider-health`, so the plan reaches `main` with that merge. CLAUDE.md requires the plan commit to come before any branching. - Stop and wait for the user to confirm that `feat/provider-health` is merged. Then `git switch main && git pull`, and `git switch -c feat/provider-blueprint`. ## Step 1 — Collect evidence (read-only, in parallel) Run 3 Explore agents at the same time, one each for `internal/provider/kubernetes`, `internal/provider/gcp` and `internal/provider/kvm`, including their tests. Each answers the **same questionnaire**, citing `file:line` for every answer and writing "not implemented" explicitly when that's the case: 1. **Identity**: providerID format and parsing; what it carries (zone, host, namespace). 2. **Create**: how it stays idempotent (name lookup, 409, DomainLookupByName); whether it returns before the VM is up or waits; what happens when an earlier attempt crashed partway; which Placement fields are used or ignored; how cloud-init is delivered; how the port is used; how GC labels/UID are attached. 3. **Get**: native state → InstanceState mapping; IP discovery rules; UID and CreatedAt readback; how NotFound is detected. 4. **Delete**: idempotency; what gets removed (disks, volumes, seeds); returns before or after the resource is gone; unknown/malformed providerID behaviour. 5. **ListByTag**: scope (namespaces, zones, hosts); partial-failure behaviour; how managed resources are recognised. 6. **Error classification**: table of backend error → NotFound/Quota/Transient/ Permanent; whether quota is detected at all. 7. **Health check** (`Checker`): targets, provider-specific Reasons, Details keys. 8. **Config**: YAML section, defaults, validation (`internal/provider/config*.go`), credentials/Secrets, which settings are read once per process. 9. **Connections and cancellation**: pooling, reconnects, timeouts, ctx honouring. 10. **Observability**: log keys and verbosity levels, spans (`kvm/trace.go`, gcp `wirelog.go`), anything beyond the shared metrics/tracing decorators. 11. **Testing**: fakes, envtest/kind, `//go:build integration` tests, what is not covered. 12. **Known gaps**: TODOs, stale comments, documented limitations. Meanwhile, read the shared layer myself: `internal/provider/{provider,errors,check, name,config,metrics,tracing}.go`, `internal/provider/registry/registry.go`, `cmd/main.go` (registry wiring, decorator order, Checker collection), and the places that consume the contract, so every MUST rule traces to a caller that depends on it: `internal/controller` (error-class branching, ErrNotFound → replace/adopt), `internal/gc` (UID, CreatedAt age skip, ListByTag errors), `internal/providerhealth`. Then cross-check against the existing docs listed in Context. ## Step 2 — Write `docs/providers.md` Sections: 1. **What a provider is**: a few sentences plus links to architecture.md §5/§11. 2. **The contract**: per method (Create/Get/Delete/ListByTag), MUST/SHOULD/MAY rules, each followed by *why*: the reconciler or GC behaviour it protects, linked to code. 3. **Error taxonomy**: what each class makes the reconciler do; how to map backend errors; "unclassified = transient" pitfall. 4. **Identity and GC labels**: providerID guidance, `LabelManaged`/`LabelUID`, CreatedAt, deterministic `NameFromUID`. 5. **Optional interfaces**: `Checker` (shared reasons, never fails as a whole, collected before decorators). 6. **Feature matrix**: one row per questionnaire item, columns kubernetes/gcp/kvm, cells `yes`/`partial`/`no` plus a short note. Put the file:line evidence in a compact per-provider notes subsection under the table, not in the cells. 7. **Adding a provider: checklist**: package layout, config section and validation, registry constructor, `cmd/main.go` wiring, Checker, error mapping with tests, `name`/label tests, integration test behind a build tag, provider doc page, architecture.md rows, demo script (optional). 8. **Gaps and proposed features**: current-state gaps per provider (e.g. no kvm quota detection, orphan volumes invisible to GC, Secret rotation needs a restart, ListByTag partial-failure inconsistency, stale `mock` references in `provider.go` comments), plus proposals that are clearly marked as not implemented. Only list them; don't design them. Also: link it from `docs/architecture.md` (Components table → provider contract row) and from the CLAUDE.md "Architecture" section. Leave per-provider docs where they are. Follow the plan-execution convention: create `docs/plans-executions/2026-09-16-0841-provider-blueprint.md` with the status checklist and step entries, and commit it with each step. ## Step 3 — Verify, then open the MR - A verification agent (Explore) re-checks each matrix cell and each MUST rule against the cited file:line and reports any mismatch. Fix what it finds. - Check that every relative link in the new and edited docs resolves (grep the `](` targets and `ls` each one). - `go build ./... && go test -short ./...` as a sanity check (docs-only change, expected no-op). - Push `feat/provider-blueprint` and open the MR against `main` via the Gitea API, following the project's push/MR memory note. After the user confirms, add a CHANGELOG.md entry. ## Critical files - New: `docs/providers.md`, `docs/plans/2026-09-16-0841-provider-blueprint.md`, `docs/plans-executions/2026-09-16-0841-provider-blueprint.md` - Edited: `docs/architecture.md` (link), `CLAUDE.md` (link) - Read-only sources: `internal/provider/**`, `cmd/main.go`, `internal/controller`, `internal/gc`, `internal/providerhealth`, `docs/prompts/__initial-prompt.md`, `docs/kvm-provider.md`, `docs/gcp-vm-validation.md`