# v2.3 Provider lifecycle — tasks

Implement in phase order from [`design.md`](./design.md). Do not commit unless Sam asks.

## Phase 1 — Config + registry

- [ ] 1.1 Add `CODER_PROVIDERS_PATH` to `apps/coder/src/config.ts` (default `/data/coder-providers.json`)
- [ ] 1.2 Add `data/coder-providers.json` example + wire in `apps/coder/.env.host`
- [ ] 1.3 Implement `provider-config.ts` (Zod schema + load/merge/save)
- [ ] 1.4 Implement `provider-config-registry.ts` (`buildResolvedRegistry`, module singleton + reload)
- [ ] 1.5 Unit tests: built-in override, custom ACP add, enabled false, invalid entry skipped
- [ ] 1.6 Update `agent-probe.ts` to iterate resolved registry (include custom ids, respect enabled)

## Phase 2 — Snapshot lifecycle

- [ ] 2.1 Extend `ProviderSnapshotEntry` / status union in coder + web types (`loading`, `unavailable`, `enabled`)
- [ ] 2.2 Add `command-availability.ts` (`isCommandAvailable`)
- [ ] 2.3 Rewrite `buildProviderEntry`: never return null; handle disabled/uninstalled/loading
- [ ] 2.4 Implement tier-2 skip using `available_agents.last_probed_at` + `PROVIDER_PROBE_TTL_MS`
- [ ] 2.5 Return `loading` entries synchronously on cache miss; complete via inflight promise
- [ ] 2.6 Extend `provider-snapshot.test.ts` for disabled, uninstalled, fresh DB skip, force refresh
- [ ] 2.7 Verify warm cache: second snapshot call does not invoke `probeAcpProvider` (mock assert)

## Phase 3 — Generic dispatch

- [ ] 3.1 Add `resolveLaunchSpec()` to `acp-spawn.ts`
- [ ] 3.2 Wire `acp-dispatch.ts` to use launch spec + env merge
- [ ] 3.3 Wire `dispatcher.ts` to load resolved def by agent name
- [ ] 3.4 Unit test: custom command argv reaches spawn
- [ ] 3.5 Smoke: task dispatch for one custom catalog provider (if installed on host)

## Phase 4 — HTTP API

- [ ] 4.1 `GET /api/providers/config`
- [ ] 4.2 `PATCH /api/providers/config` (merge + write file + reload registry + clear snapshot cache)
- [ ] 4.3 `POST /api/providers/refresh` optional body `{ providers?: string[] }`
- [ ] 4.4 `GET /api/providers/:id/diagnostic` (plaintext report)
- [ ] 4.5 Extend `apps/web/src/api/client.ts` coder namespace
- [ ] 4.6 Confirm BooChat proxy forwards new routes (or document direct :9502)

## Phase 5 — Web UI

- [ ] 5.1 Create `apps/web/src/data/acp-provider-catalog.ts` (5–10 curated entries)
- [ ] 5.2 `AddProviderModal.tsx` — search, install → patch + refresh subset
- [ ] 5.3 `ProviderSettingsDrawer.tsx` — list, status, toggle, refresh, link to add
- [ ] 5.4 Entry point from CoderPane / AgentComposerBar (gear or settings link)
- [ ] 5.5 Filter `AgentComposerBar` selectable providers (`enabled && ready`)
- [ ] 5.6 Loading state while snapshot entries `loading` (poll or one-shot refetch)
- [ ] 5.7 `npx tsc -p apps/web/tsconfig.app.json --noEmit`

## Phase 6 — Docs, deploy, closeout

- [ ] 6.1 `BOOCODER.md` — config file, refresh contract, enable/disable
- [ ] 6.2 Update `docs/DEFERRED-WORK.md` — mark tier-2 cold-probe item addressed
- [ ] 6.3 `CHANGELOG.md` entry when tagged
- [ ] 6.4 `pnpm -C apps/coder test && pnpm -C apps/coder build`
- [ ] 6.5 `sudo systemctl restart boocoder`
- [ ] 6.6 Smoke via Tailscale:
  - `curl http://100.114.205.53:9502/api/providers/snapshot`
  - PATCH disable goose → absent from composer, visible in settings
  - POST refresh → models repopulate
  - Add catalog entry → appears after refresh

## Optional (same batch if time)

- [ ] O.1 WS frame `provider_snapshot_updated` (skip polling)
- [ ] O.2 `available_agents.enabled` column mirror
- [ ] O.3 Diagnostic sheet UI (row click → modal)

## Explicitly out of scope

- Port Paseo `ACPAgentClient` / per-provider SDK clients (see design §12)
- Full 30+ ACP catalog
- MCP `list_providers` tools
- Voice providers
