Development record
abcd Reaches Any Harness Through an Adaptor
Widened on 2026-09-22 by the product thinker: "any harness" includes no harness. abcd run as the binary alone is a first-class way to manage a repository: it calls whichever harnesses are available for the roles that need one, and where a role's runner is unavailable the fallback is a host the operator configured rather than a host session that happens to be there (itd-2609201916056194). The adaptor ladder below gains that rung; the console that would drive it from outside a harness is reframed as an operator console served by the binary (research note, 2026-09-22) and is not this record.
Press Release
abcd runs wherever the user's harness runs. abcd's core is a transport-agnostic engine, and each harness reaches it through a thin adaptor built on one shared seam. An adaptor climbs a fixed ladder — the host's own plugin format first, any other native seam second, the MCP floor always — and one parity suite proves every host gets the same conventions, intents, and lifeboats. Adding a harness is one adaptor over an unchanged core, never a second copy of abcd.
"We switched harnesses and abcd came with us," said Carol, whose team moved for cost reasons. "Same record, same gates, byte-identical lifeboats. The adaptor was thin because the machinery was already shared."
Why This Matters
adr-23's portability promise — front doors over an unchanged core — needs shared machinery to be true in practice. Today the hook entrypoints hard-code one host's stdin payload schema, exit-code semantics, and tool naming; without a host profile seam, every port re-solves those, and without one parity suite, "supported on host X" is an unverifiable claim. adr-39 (host-tier policy) sets the tiers — MCP floor, reference host, an eventual open-source default gated on parity — and this intent builds the machinery all of those tiers stand on.
What's In Scope
- The host profile seam — the host-specific payload schema, exit-code semantics, and tool-name assumptions currently hard-coded in the CLI hook entrypoints move behind a host profile; the current host becomes profile #1 with behaviour unchanged.
- The adaptor ladder as testable behaviour — each rung declares its capabilities; a rung a host cannot support degrades to the next and reports what it cannot do (loud staging), never a silent no-op.
- The parity conformance suite, defined once and run per host — install parity, byte-identical lifeboats (modulo timestamps and oracle adapter ids), in-session subagent dispatch per itd-2's wire-protocol contract, and docs that treat every adopted host as a peer. Its per-check report is the adr-39 default-flip evidence.
- Docs structure — a host-neutral install/configuration skeleton that per-host pages slot into without editing existing pages.
What's Out of Scope
- The MCP front door itself — its own intent; it is the floor the ladder ends on.
- Per-host adoption — one intent per harness at its explicit adoption decision (adr-39), including any concept mapping onto a host's native work-plane.
- The default-host flip — adr-39's gate, exercised with the parity report as evidence; not built here.
Acceptance Criteria
BDD format, per
itd-1-acceptance-gates. Seeded criteria are proposals until the planning interview confirms them.
- Given the hook entrypoints run behind host profile #1, when the current host drives them across the existing hook corpus, then observable behaviour is identical to the pre-seam baseline — the seam lands with zero behaviour change.
- Given a second host profile is declared, when its adaptor exercises a rung the host cannot support, then the adaptor reports the degradation explicitly AND the parity report records it as a documented host difference, never a silent pass.
- Given the parity suite passes on the reference host, when the same suite runs against any declared profile, then it emits a comparable per-check report suitable as adr-39 default-flip evidence.
- Given the host-neutral docs skeleton, when a host's page is added at its adoption, then no existing page changes AND no committed page names a harness whose adoption is undecided.
SOTA
Surveyed on 2026-10-23; sources, evidence tiers and the local fit risks are in the harness, interop and lifeboats SOTA note.
- The three harnesses abcd sits beside share one hook idiom: a JSON object on stdin with
session_id,transcript_path,cwdandhook_event_name; exit 0 with JSON on stdout, exit 2 blocks with stderr as the reason; Claude Code and Gemini CLI treat any other code as a non-blocking warning, while Codex's page leaves other codes undefined (to be tested in the parity suite);hookSpecificOutput.additionalContextanddecision/reason(Claude Code, Codex and Gemini CLI hook references). Event names differ (PreToolUseagainstBeforeTool), and which events honour exit 2 differs, so a profile is logic, not data. - Plugin formats have converged on one shape under different manifest names:
.claude-plugin/plugin.json, a rootplugin.jsonwithextensions.com.openai(Codex),gemini-extension.json; each withskills/,hooks/hooks.jsonand an MCP server file; Codex plugin hooks bar a plugin from its public directory (vendor plugin references). - Codex's root
plugin.jsondeclares the open Agent Plugins 1.0.0 schema (agent-plugins.org), a vendor-neutral "interoperability floor": a portable core ofplugin.json,skills/(Agent Skills) andmcp.json, with client behaviour, hooks among it, under reverse-domain extension keys; its steering committee draws from Amazon, Cursor, Microsoft, OpenAI and Vercel. It is the obvious seam for the "host's own plugin format first" rung, and whether the adaptor's manifest emitter targets it is for the plan; hook profiles stay logic in code either way. - The Agent Skills
SKILL.mdformat is the one unit all three load unchanged (agentskills.io specification); its own client showcase page rendered empty, so adoption is read from the vendors' pages. - Hosts now import each other's configuration (
claude import codex --dry-run), which is the direction the AGENTS.md-only amendment already takes. - The parity suite has a working model in the MCP conformance framework: requirements per revision, a YAML baseline of accepted gaps scoped to individual checks,
checks.jsonand atier-checksummary that separates required from informative scenarios. - LSP's capabilities ("not every language server can support all features") are the precedent for a rung that declares what it cannot do; the Agent Client Protocol is a second stdio floor for abcd as an agent, recorded and not recommended.
- The AGENTS.md re-check's cited study (about 20% more cost, no gain in success from always-loaded context files) argues against any per-host prose layer in the docs skeleton.
Path 2 — native host profiles in code, shaped to the three documented hook contracts, with the MCP conformance framework as the model for the parity report, written as a dated lab artefact. No new dependency. The independent fit-challenge runs at plan time.
Open Questions
- Does a host profile live in code (a Go type per host) or in committed configuration the adaptor reads?
- Testing strategy for parity: run the entire acceptance matrix on every adopted host, or a pinned sample per release?
- Where does the parity report live — a dated research note per run, or a receipt-shaped artefact beside the release gates?
- (SOTA 2026-10-23) Each baseline entry in the parity report that accepts a host gap: does it need an issue id, so loud staging survives the baseline mechanism?
Evidence (2026-08-26, second-harness adaptor lab, local tier)
A local lab drove the existing hook entrypoints end to end from a second
host's native plugin runtime: prompt-router injection with per-domain dedup,
guard block-with-reason, session-start notices, and session-end capture all
worked over an unchanged core. The cost was exactly what this intent
predicts: the adaptor had to re-derive the stdin payload schema, the
exit-code semantics, and the stderr contracts by reading the CLI hook source
— the hand-derivation the host profile seam exists to remove. Two seam
requirements the lab surfaced now sit in the ledger: the prompt-router has no
removal signal for snapshotting clients (iss-2608261550580260), and
hook session-end has no machine-readable result channel, only a stderr
string contract (iss-2608261550596333). The lab's verification battery —
injection proven from logs rather than model claims, forged-heading and
separator red-team vectors, guard exit-mapping exhaustiveness, capture
watermark invariants — is a concrete seed for the parity conformance suite.
Amendments
Amended by itd-2610030814013772 (2026-10-03, its decision 7): abcd writes no tool-specific conventions file for any tool, and setup offers to retire one it finds that only repeats or links to AGENTS.md. This draft's portability work starts from that: each tool is pointed at AGENTS.md through its own configuration, never through a file abcd writes for it.
Audit Notes
Empty. Populated by intent-fidelity-reviewer when intent moves to shipped/.
Linkage note (2026-09-30)
blocked_by named itd-2 (in-session subagent dispatch), which is superseded by
itd-2609201916056194. The edge is dropped rather than relinked: the successor,
a delegated agent run through a command-line model runner, does not carry the
in-session dispatch contract this record waited on, and that dispatch is standing
practice the conventions router (AGENTS.md) states. Ruling CF2 of 2026-09-30
counts a blocker kept as a discipline as settled; the run's orchestrator read
the same of a practice the router carries, so the edge has nothing left to
wait on (recorded by the integration lane of autonomous run A).
Frontmatter
| id | itd-22 |
|---|---|
| type | intent |
| lifecycle | drafts |
| kind | standalone |
| severity | major |
.abcd/development/intents/drafts/itd-22-harness-portability.md
open on GitHub ↗ · commit history ↗
Referenced by6
- adr-39Host-tier policy — MCP floor, reference host, open-source defaultrelated
- adr-2610030814023326AGENTS.md is the one conventions file abcd writes; it never writes a host-specific copyrelated
- itd-33Multiple Agents Coordinate On Intent And Spec Work Without Duplicating Effort Or Producing Competing Artefactsbuilt on by
- itd-2609201916056194A role runs through the command-line harness the operator chose, and every fall back to the host is recordedrelated
- itd-2609212137128014abcd lab mechanises the lab conventions three hand-run experiments provedbuilt on by
- itd-2610120738075547Work handed to a cloud session comes back, or says why notrelated
mentions19
- adr-23A transport-agnostic Go core behind thin front doors
- dcn-26081500000000032026-08-15 — itd-111 planned at the live interview (spc-22 minted, `intent ready` exit 0). The itd-84 decomposition's first live hand-run rendered SPLIT, confirmed: the network trichotomy extracted to adr-38 ("implicit checks are disk-only — the network answers only an explicit ask") + brief invariant 7, which the intent now cites rather than declares; the SessionStart staleness notice vs the iss-206 skew-notice retirement was ruled a SCOPED REPLACEMENT (`refines`, not `reverses`) — steady-state skew machinery stays retired, itd-111 covers dev checkouts and failed provisioning. Interview rulings: explicit check at `abcd version --check` (ahoy stays disk-only); refusal-on-stale deliberately narrow (ahoy install only); unknown vintage (unstamped/dirty builds) is a first-class comparator outcome that fails closed at the refusal gate; harness portability of the session-start channel explicitly deferred to the itd-22 lineage. Independent SOTA fit-challenge UPHELD path 2 with three recorded caveats (seam = version-source provider interface, not a fixed-arity function; git's behind-upstream notice joins the anchors; BuildInfo stamping holes motivate the unknown outcome). AC 6 narrowed to the transition report (the fetch is itd-105/108's); one AC added (unknown vintage). Hand-run graded into the decomposition-calibration note (entry 2).
- 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.
- dcn-26081600000000012026-08-16 — the multi-harness routing ADOPTED at maintainer confirmation (itd-84 hand-run graded into the decomposition-calibration note, entry 3): adr-39 filed — the host-tier policy (MCP front door as the universal floor; the current plugin host's integration as the reference implementation, the assumed SOTA surface for the time being; the shipping default ultimately an open-source harness, flipping only on the parity suite plus a deliberate maintainer act; adaptor ladder most-native-first; per-host adoption one intent each at an explicit decision, nameless until adopted). itd-22 renamed `harness-portability` (id unchanged) and reworked to carry the shared machinery only — host profile seam, ladder semantics, parity conformance suite — with a retire-the-name ban (`retired-itd-22-slug`) on the old slug. itd-113 minted: the MCP front door intent (the floor is the first build). The never-double-write stance routed into adr-39 rather than a new principle — it restates one-canonical-primitive at the host boundary.
- dcn-26092200000000032026-09-22 — The control-programme idea is REFRAMED, not adopted as put (product thinker, after the ideate gauntlet; research/notes/2026-09-22-ideate-abcd-operator-console.md). Two kill attempts were fatal to the idea as stated: it named the product thinker as the configurer of operator-register material, which adr-2609151528057260 bars, and it made harness-less operation the destination, inverting the host-delegated boundary whose reversal itd-2609201916151817 records as opt-in only. The reframing, adopted: an operator console SERVED BY THE BINARY over local web (no new dependency), which is also the resident process that hosts the opt-in process driver, with a separate product-thinker pane in their own register (open stops, the plain-language verdict, the pace) — the web page itd-167 already decided on; configuration stays operator-side; many repositories waits for a registered-checkout store; a localhost surface that accepts secrets needs a per-launch token; the whole sequenced after the runner and routing intents; and it must carry a falsifier, since no evidence exists that a GUI control plane beats a CLI plus a status page. Ruled with it: where abcd runs as the binary with no host session, the fallback is a HOST THE OPERATOR CONFIGURED (their main connector), never nothing; and EVERY fall back to the host is recorded — a receipt per event and a count per runner and per role in the run record — as intel on what to improve (itd-2609201916056194, planned; itd-22 widened so that "any harness" includes none).
- iss-94The intent corpus still specifies the pre-adr-35 lifeboat model: itd-2, itd-8, itd-9, itd-10, itd-13, itd-15, itd-19, itd-22 and itd-24 variously use the retired 'disembark to home' signature, the in-tree .abcd/lifeboat/ home, or the in-tree .abcd/development/voyage/ path (itd-9's acceptance writes voyage provenance in-tree — the exact path that would fail abcd's own privacy-hygiene audit rule). The brief, glossary and roadmap were reconciled to adr-35; the intents were deliberately NOT rewritten, because an intent is a proposal with its own lifecycle and silently rewriting nine of them inside an unrelated change is worse than tracking the drift. Each reconciles when it is next planned.
- iss-2608261550580260The prompt-router emits only domains whose content changed this turn, so a client that snapshots injected rules — a host-side adaptor staging them into a system prompt, or a future MCP consumer — keeps a domain that was deleted or renamed mid-session until session end: there is no removal signal in the protocol. Emit the full active-domain-name set, or tombstones for removals, so a snapshotting client can prune. A local second-harness adaptor lab hit this and had to accept the lingering snapshot as a recorded gap.
- iss-2608261550596333hook session-end exits 0 on every path by design and reports its outcome only as human stderr text, so a programmatic caller can detect success solely by string-matching 'abcd history: staged' or 'already staged'. A local adaptor lab shipped a silent permanent-loss bug from exactly this: non-matching stderr was first treated as success, and finished sessions were watermarked as captured when staging had failed. Give the hook a machine-readable result channel — a JSON line on stdout, or a documented stable contract — without breaking the never-wedge-the-session exit-0 behaviour toward the host.
- iss-2609012040019014Lab finding lab-260831162806-976575f, probe p5b-live-session-retest: the PreToolUse guard payload carries only tool_input.command, and a destructive command moved into the tool's workdir slips the match. (1) Contract fact: `guardHookInput` (internal/surface/cli/guard.go) reads cwd, tool_name and tool_input.command, and cwd is used only for rulesRoot; a host whose shell tool has a second execution-shaping input (opencode's `workdir`; any host passing a per-call cwd or env) lets a model move meaning out of the string the guard reads. (2) Evidence: the lab's plugin-guard.log records 18:36:58 allow exit 0 for `rm -rf *`, then 18:37:40 block exit 2 for `cd scratch && rm -rf *` — the model had rewritten the chain into workdir=scratch plus `rm -rf *`. (3) The bypassed entry is rm-rf-after-cd-chain, whose why is that a FAILED cd leaves the delete running wherever the shell already was; bare `rm -rf *` is not a registry hazard by design (its successor is an absolute-path delete as its own command). A host-honoured workdir is a different mechanism from a shell cd: whether the failed-cd hazard exists at all depends on what the host does when workdir does not exist — fail the tool call (no hazard) or fall back to the session directory (the hazard) — and the probe evidence does not answer that, so it must be probed on that host before either branch is chosen. (4) No shipped surface carries the field today: hooks/hooks.json wires the Bash matcher only, and the shipped host's Bash tool input has command, timeout, description, run_in_background; the opencode adapter is lab-tier (itd-22 lineage), nothing of it in the tree. (5) The fork, for the maintainer: (A) extend the guard contract — guard.Check in core gains a context carrying the host's workdir/cwd and entries may constrain it; the naive form where the hook prepends `cd <workdir> && ` before Check is WRONG and must not be the implementation, because it fires the cd-chain entry on every workdir'd recursive delete, whose failed-cd hazard the host-managed chdir does not have; versus (B) document the reach — add to the guard brief chapter's 'what an allow still does not see' and to adr-42's scope that the guard reads the command string, a host input that changes where or as whom it runs is outside the read, and a host adapter is responsible for folding such inputs into the string it submits or into a documented field. adr-42 decision 1 (mistake filter; the execution-layer boundary belongs to the host, adr-25) leans toward B, but A is a core API change the ADR does not settle. (6) Severity minor, category observation; stays open until the maintainer picks.
- 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.
- itd-1Every Intent Has Acceptance Criteria; Every Audit Verifies Them
- itd-2In-Session Subagent Dispatch — the Default Host-Delegated Oracle
- itd-7Lifeboats Carry RepoPrompt Workspace Definitions Forward
- itd-111A Stale abcd Never Answers Silently
- itd-113The MCP Front Door Opens — abcd's Core Verbs From Any MCP-Capable Harness
- itd-2609091034175565Before an agent starts on an item, it reserves it on the shared remote, and everyone who works on the project sees the reservation
- itd-2610030814013772abcd's projects keep one conventions file, AGENTS.md, and Claude Code reads it directly
- spc-22a-stale-abcd-never-answers-silently-every-surface-that-runs
- spc-2610031156364295evaluate-whether-claude-md-can-be-removed-safely-now-that