ADR 0002 — Provider per target, not Adapter per ecosystem
ADR 0002 — Provider per target, not Adapter per ecosystem
Section titled “ADR 0002 — Provider per target, not Adapter per ecosystem”Date: 2026-05-07 Status: Accepted Strategic frame: TapPass Strategy Memo v3 §07 (Pillar 2: Control Plane)
Context
Section titled “Context”Earlier drafts had an "Adapter per ecosystem" framing — one Adapter per ecosystem (Claude Code adapter, Cursor adapter, LangChain adapter, …). Each Adapter handled the entire mapping from ecosystem-agnostic Manifest to that ecosystem's runtime config.
This conflated two structurally different things:
- Per-target translation — each individual target (Claude Code config, OpenShell YAML, Monty manifest, gVisor profile) needs its own translator. Targets are concrete tools / runtimes.
- Per-deployment recipe — operators choose how to combine targets for one deployment. Recipes are operator decisions.
The "adapter per ecosystem" framing forced re-implementation: every ecosystem's adapter had to know how to render Claude Code's settings.json and OpenShell YAML and Monty manifest and …
Decision
Section titled “Decision”Separate the two cleanly, using the Terraform analogy:
- Provider = per-target translator (Claude Code provider, OpenShell provider, Monty provider, …).
- Pure function:
provider(manifest, capabilities) → target_config. - Like Terraform providers (aws / gcp / azure / kubernetes).
- Pure function:
- Runtime = per-recipe combination of providers (one per ring + cross-cutting).
- Operator-authored composition.
- Example:
claude-code-laptop = claude-code (harness) + nono (kernel) + monty (interpreter) + anthropic-gateway + mcp-broker.
A Runtime selects one provider per ring (or marks it absent). Sandboxes are bound to runtimes.
Consequences
Section titled “Consequences”- Many providers per ring. Harness ring: claude-code, codex, cursor, cline, aider, librechat plugin, element-bot, sdk-direct, … Kernel ring: OpenShell, nono, gVisor, Firecracker, bubblewrap, sandbox-exec, K8s netpol, … Interpreter ring: monty, V8 isolates, Wasmtime, …
- Runtimes are operator-authored compositions. TapPass ships curated default runtimes for common combinations; operators can compose any provider combination subject to compatibility checks.
- Same provider reused across many runtimes. The Claude Code provider is reused in
claude-code-laptop,claude-code-server,claude-code-eu-strict,collibra-steward-laptop, etc. No duplication. - Adding a new ecosystem ≈ adding one harness provider. Runtime / control plane / manifest are unchanged. Each provider is a 2-week project.
- Compatibility matrix becomes the buyer-facing artifact. Which runtime delivers what governance depth — answered by composing provider capabilities.
Alternatives considered
Section titled “Alternatives considered”One adapter per ecosystem (the rejected previous framing)
Section titled “One adapter per ecosystem (the rejected previous framing)”Rejected because the same target (e.g. Claude Code's settings.json) gets reused across many adapters. Forces re-implementation; doesn't compose.
Treat providers + runtimes as one concept ("adapter compositions")
Section titled “Treat providers + runtimes as one concept ("adapter compositions")”Rejected because the unit of expansion (provider, ~2 weeks per target) and the unit of deployment (runtime, operator-authored) operate on different rhythms. Conflating them obscures both.
One concept ("translator") that's both per-target AND composable
Section titled “One concept ("translator") that's both per-target AND composable”Rejected for the same reason: targets multiply along one axis (the ecosystem catalog grows) and recipes compose along another (operators mix and match). Two concepts, two axes.
Migration path
Section titled “Migration path”| File | Action | Status |
|---|---|---|
concept-cards/adapter.md | Delete | ✅ done 2026-05-07 |
concept-cards/provider.md | New card | ✅ done 2026-05-07 |
concept-cards/runtime.md | New card | ✅ done 2026-05-07 |
concept-cards/sandbox.md | Update: bound to a Runtime, not an Adapter | ✅ done 2026-05-07 |
components/ | Per-target component files (e.g. one per harness provider) reorganization | pending (post-MCS work; see roadmap) |
architecture/strategic/governed-agents.md | Replace "Adapter" → "Provider + Runtime"; provider-by-aspect rendering | ✅ done 2026-05-08 |
architecture/strategic/enforcement-layers.md | Provider/Runtime terminology in rewrite | ✅ done 2026-05-08 |
architecture/strategic/gateway.md | Tier 0/1/2 framing maps to Provider-per-position | ✅ done 2026-05-08 |
References
Section titled “References”- TapPass Strategy Memo v3 §07 — Pillar 2: Control Plane (manifest, providers, push/pull/reconcile)
- TapPass Strategy Memo v3 §07.2 — "Providers are translators as plug-ins... Adding a runtime = writing a provider. This is the Terraform analogy made literal."
../concept-cards/provider.md../concept-cards/runtime.md0001-rings-not-layers.md— related decision0003-manifest-by-aspect.md— related decision