> ## Documentation Index
> Fetch the complete documentation index at: https://docs.driftless.icu/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent platform release gates

# Agent Platform — current state & remaining gates

Operator + reviewer note for the agent platform that shipped in PR #192 (`claude/model-gateway-v2`)
and the follow-up unification. What is live, what is still dark, the rollback story, and how to
validate. No changelog, no marketing — just clarity. Companion to `agent-platform-boundary.md`.

> Update (post-launch unification): PR #192's pre-merge readiness framing (a `gateway_enabled`
> flag with a parallel legacy path) is obsolete. The owner ruled ONE model system before launch —
> the flag and the legacy resolver were deleted from source (candado-pinned). This note reflects
> the shipped reality: the gateway is the only model path, Chat v0 is live, and the internal
> agents run on the same tool platform as Chat.

## What's live

### Model Gateway — THE model path (one system)

* Every run (Auditor/Architect/Librarian/Chat) resolves `{base, auth}` per provider, per attempt,
  through `buildGatewayResolver` over `ProviderCredentialService.resolveAuth` — server-side only,
  never returned by the API. There is **no `gateway_enabled` flag and no legacy resolver**
  (`pr-hardening.guard.spec.ts` pins their absence).
* **OpenCode is one provider class inside the gateway.** A workspace's legacy subscription key
  (`settings.agents.opencode_key_enc`) resolves for BOTH `opencode` and `opencode-go` through the
  credential shim, wire-identical to the pre-gateway path (`buildChatRequest` candado,
  `agentic-loop.spec.ts`).
* `provider_credentials` is the single credential store (entity registered in BOTH
  `libs/db/data-source.ts` and `apps/api/app.module.ts` — the parity candado
  `db-entity-registration.guard.spec.ts` keeps them in sync). Migration
  `BackfillOpencodeCredential1715200000100` copies each legacy key into a real row (additive,
  idempotent, reversible); the legacy field + shim are retained as the rollout safety net.
* **Dashboard:** one always-on "Model providers" section (Settings → Agents) — save/test/revoke a
  credential for any of the ten provider classes, and a provider-aware model picker (OpenCode via
  the models.dev catalog; every other adapter\_ready provider via the live `GET /models` probe).

### Chat v0 — live, read-only

* `apps/api/src/chat` (module/controller/service + chat-tools), `ChatThread`/`ChatMessage`
  entities + migrations, the `chat` skill, and the dashboard wiring. A Chat assistant turn IS an
  `agent_run` (single ledger). v0 is READ-ONLY — governed writes are B4 and need a new owner ruling.

### One tool platform, used everywhere

* The `DriftlessTool` registry (policy gate + observability + citations) backs Chat AND — via
  `cognitive/agent-tools.ts` (`buildAgentToolExecutor`) — the Auditor/Architect (`SANDBOX_TOOLS`)
  and Librarian (`TOPIC_TOOLS`). The canonical sets are parity-locked to the live literals so the
  flip is a provable no-op; the agents present `caller:'internal_agent'` and their in-loop tools
  are all read (the policy engine is a transparent pass-through). Their governed writes stay
  post-loop Action blocks. `SANCTIONED_LIVE_IMPORTERS` pins exactly who consumes the platform.

## What is NOT live (still dark, candado-pinned)

* `DriftlessGatewayModel` (the `@ai-sdk/provider` V4 adapter) — constructed by nothing live
  (`mastra-boundary.spec.ts`).
* Native Anthropic/Google adapters — `adapter_ready:false` + `base_url:null`; the resolver refuses
  them (`provider-registry.contract.spec.ts`). Reach them today via a custom OpenAI-compatible
  endpoint. Priority live providers: OpenAI-compatible, OpenRouter, DeepSeek, Qwen, custom
  OpenAI-compatible, local, and OpenCode (Zen/Go).
* Mastra runtime — **no `@mastra/*` dependency, no import** (static or dynamic). Deferred.
* External-caller (MCP) write/act tool exposure — hard-denied even with the exposure flag on
  (`tool-policy.spec.ts`).
* Chat governed writes (B4) — Chat is structurally read-only.

## Rollback / no-op story

* Per workspace: **revoke the offending provider credential** (`DELETE
  /workspaces/:slug/provider-credentials/:provider_class`) and/or point `settings.agents.model`
  back at `opencode/…` refs. There is no flag to flip — the gateway is the only path.
* The legacy `opencode_key_enc` + model string stay valid and authoritative through the shim; the
  backfill migration is reversible (`down()` removes only the rows it created).
* The `provider_credentials` table + `agent_config_events.provider_class` column migration is
  additive and reversible.

## Remaining gates

* Mastra real runtime — deferred (its adoption card gates it; `@mastra/core` go/no-go).
* Native Anthropic/Google native-auth wire — a dedicated card, kept dark until then.
* Drop the legacy `opencode_key_enc` field + the resolveAuth shim — a follow-up, only once the
  backfill is proven complete in staging/prod.

## Validate (local)

```
pnpm install --frozen-lockfile
bash scripts/harness/check.sh   # build + per-package typecheck + react-doctor + guardrails + tests
# integration (needs a Postgres on PATH or TEST_DATABASE_URL):
pnpm --filter @driftless/api test:integration
```
