Development record
A transport-agnostic Go core behind thin front doors
Context
abcd's old surface was welded to one host: behaviour lived in slash-commands, hooks, and in-session Python that assumed Claude Code was driving. Adding a second entry point (an MCP server, a scriptable CLI, a markdown plugin surface) meant re-implementing logic against a different caller, because there was no neutral place the logic lived independent of how it was invoked.
The rebuild (ADR-21) needs several front doors over time — a Cobra CLI first, an MCP server and a markdown plugin surface later — and cannot afford to re-express its decisions per surface.
Decision
abcd's logic lives in a transport-agnostic core (internal/core) that does
deterministic work and returns structured results. It knows nothing about
who called it — no terminal formatting, no MCP framing, no slash-command
parsing. Front doors are thin: each parses its own input, calls the same core,
and renders the structured result in its own idiom.
- CLI (Cobra) is the first front door and ships in the MVP.
- MCP server and a markdown plugin surface that shells to the binary are later front doors — each is a new adapter over the unchanged core, not a reimplementation.
Adding a transport is therefore an additive change: write a thin door, map its inputs to core calls, render the results. "Add MCP later" is trivial precisely because the core never assumed a transport in the first place.
Alternatives Considered
- Logic in the CLI layer, MCP added by refactor later. The obvious path. Rejected: it defers the seam to the moment it is most expensive to cut, and invites CLI-shaped assumptions (exit codes, stdout formatting) to leak into logic that MCP then has to unpick.
- A shared library that still emits presentation. A core that returns rendered strings. Rejected: presentation is transport-specific; a core that formats for a terminal cannot serve MCP's structured responses without each caller re-parsing text. Structured results in, structured results out.
- Chosen: deterministic core returning structured results, thin front doors over it. One place for behaviour; transports are cheap and additive.
Consequences
- The core is directly testable without a transport — table tests over structured inputs and outputs, no terminal or protocol harness.
- The oracle and adapter seams (ADR-22, ADR-25) plug into the core, so every front door inherits the same adapters for free.
- Each front door owns its own input validation at the system boundary; the core trusts the structured values it receives from them.
- A behaviour is "wired" only when a front door reaches it — core code no front door calls is dead scaffolding, and the CLI is the surface that proves the MVP core is live.
Frontmatter
| id | adr-23 |
|---|---|
| type | adr |
| status | accepted |
| date | 2026-07-06 |
.abcd/development/decisions/adrs/0023-transport-agnostic-core.md
open on GitHub ↗ · commit history ↗
References2
Referenced by4
mentions16
- adr-24the companion harness is a peer integrated via conventions and MCP, not a code dependency
- dcn-26081500000000052026-08-15 — Multi-harness support is IN SCOPE, reversing (`reverses`) the "obsolete under no-hard-deps" annotation the 06-delivery out-of-scope index carried on itd-22; the intent stays in drafts/ as the lineage carrier pending rework. The commitment: every supported harness drives the same abcd CLI functionality — behaviour lives once in the transport-agnostic core (adr-23) and is never double-written for one host. Per-harness delivery follows an adaptor ladder, most-native first: (1) the host's own plugin/packaging format, (2) any other native seam (lifecycle/hook wiring, config), (3) an MCP server as the universal fallback floor every MCP-capable host gets when rungs 1–2 are unavailable. Where a host exposes a native work-plane (goal/work-item tracking, a canonical wiki or knowledge base, trajectory judging), abcd's concepts (intents, brief, reviews, capture, lifeboat) map onto those native concepts rather than running as a parallel artefact set; hosts without such concepts get the adaptor rungs without the mapping. Candidate harnesses exist but commitment records name no external tool before per-host adoption is decided (2026-07-30 convention); each host adoption files as its own intent per the itd-22 lineage's own rule. Rejected: the reversed annotation's claim that a second harness is "just another host over the same core" needing no record — the core stays single, but hook/payload portability, packaging, and concept mapping are real per-host work the record must carry. The itd-84 decomposition hand-run routing this into intent/principle/brief rows precedes any filing.
- iss-2608230752354926ADR-23: Transport Agnostic Core (probe)
- iss-2610150749052356The generated ADR and drafts indexes dropped hand-written relationship notes the old rows carried ('refines adr-50' on adr-55, 'amends adr-19' and 'extends adr-19, adr-20' on adr-31, adr-39/adr-23 links on itd-22, a reframe note on itd-16), because a row is derived from the record's H1 and status only.
- iss-2610262031126042The platform page says the markdown plugin surface is a later front door, but the configuration chapter describes it as part of the shipped plugin shape
- iss-2610262031127666The platform page says an MCP server is a later front door, but universal patterns says abcd interoperates with the companion harness as an MCP server
- iss-2610262039030685itd-113 calls MCP abcd's second front door; 03-configuration calls an mcp/ door adr-23's third
- itd-22abcd Reaches Any Harness Through an Adaptor
- itd-100Alice Reads One Page and Knows Exactly Where abcd Stands in the Agentic-AI Landscape
- itd-113The MCP Front Door Opens — abcd's Core Verbs From Any MCP-Capable Harness
- itd-161record-lint cannot see a decision-shaped document filed outside the record stores
- itd-183The cold reading sees exactly what the assembler passes — positive inclusion, field projection, and a per-run manifest
- spc-32`abcd update` — fetch, verify, swap, in one verb
- spc-34The managed-repo identity gate — detect the wrong author/committer, establish only on confirm
- spc-53record-lint-cannot-see-a-decision-shaped-document-filed-outs
- spc-61The cold-reading input assembler: positive inclusion at field granularity, a pathless bundle, and a hashed per-run manifest