Development record
A role runs through the command-line harness the operator chose, and every fall back to the host is recorded
Press Release
Any role can run through a command-line harness the operator names, with the same brief, contract and transcript store as the host's own sub-agent; where it cannot, the host takes it and the record says so.
"My reviews were the scarcest thing in a run, and a second harness was sitting on the machine doing nothing," said a technical facilitator. "Now a role names its runner, the review goes out through it, and the receipt reads the same as one the host ran. When the runner is not there the host picks it up, and the run tells me how often that happened, which is how I learned which roles to move."
Why This Matters
The loop's process driver names a command-line rung that nothing reads. Every harness checked on 2026-09-22 can be driven headlessly with a prompt in, structured events out, an exit code and a non-interactive permission switch: Claude Code's print mode, opencode's run mode and its server, Codex's exec and app-server, Gemini's headless mode, Cursor's CLI, Aider's message mode. What is missing is abcd's side: one runner interface, a route per role, and the discipline that a fallback is evidence rather than a silent recovery. Two hazards are recorded: a print-mode run without the bare flag executes the target repository's hooks and configured servers with no trust dialog, and a runner's model route must obey the provider allowlist.
Mechanism
We expect a role routed through another harness's command line to land in the record indistinguishably from a host-run one, because the runner is handed the same brief and held to the same output contract; shown wrong if a reader cannot tell which route ran from the receipt alone, or if no role is routed off the host in a release of it shipping.
Scope Conditions
- Holds for a harness with a documented non-interactive mode that takes a prompt, returns structured output and an exit code, and grants permissions without a prompt.
- Holds where the runner's binary is on the machine and its own credential is already configured; abcd never logs a harness in.
- Holds under adr-2609221009491186: a runner's model route is one its provider's allowlist admits.
What's In Scope
- The route per role:
roles.<role>.runnernameshost(the default) or a configured runner; nothing changes for a role left unset. - The runner interface: the same brief, inputs and output contract the host sub-agent gets; the answer validated the same way; the transcript captured into abcd's own store, whatever the harness keeps of its own.
- The shipped runners: the claude CLI in print mode with the bare flag (so a target repository's hooks and servers do not run untrusted) and opencode, through its run mode or its server; a third is configuration of the same interface where the harness's shape allows.
- The fallback, and its record: an unavailable or failing runner hands the role to the host, which is the default route, and where abcd runs as the binary with no host session the fallback is a host the operator configured (their main connector, the claude CLI for example) so there is always a landing. Every fallback writes a receipt naming the role, the runner asked for, the reason, and the route that ran.
- The intel: the run's summary reports the fallback count per runner and per role, so a route that never works is visible without reading transcripts.
- The allowlist: a runner's model route is resolved against its provider's list before the lane starts.
- The first proof: a ruthless review of a real lane sent through the opencode runner from a lane the host drives, its receipt indistinguishable from a host-run review but for the route it names.
- Security review before it ships: it starts processes with a prompt and a repository path.
What's Out of Scope
- Choosing the runner for a role (the model tier's, itd-2609170822093401).
- Installing or authenticating a harness (itd-63 explains; the person installs).
- A served console over the loop (reframed as the operator console, research note of 2026-09-22).
Decisions
Ruled by the product thinker on 2026-09-21, in the interview that filed and planned this intent (adr-2609221009491186 records the vocabulary rulings it rests on):
- The host is the default route; a runner is opt-in per role (adr-25).
- Where abcd runs with no host session, the fallback is a host the operator configured, never nothing (ruled 2026-09-22).
- Every fallback is recorded as intel: a receipt per event and a count per runner and per role in the run record (ruled 2026-09-22).
- The claude CLI runner passes the bare flag, so a target repository's hooks and configured servers do not run untrusted.
Open Questions
None open.
Acceptance Criteria
- Given a role whose runner is set to a configured command-line harness, when the lane reaches that role, then the runner gets the same brief, inputs and output contract the host sub-agent would get, its answer is validated the same way, and its transcript lands in abcd's own store.
- Given a role left unset, when the lane reaches it, then the host runs it and nothing differs from today.
- Given a runner that is absent, refuses or fails, when the lane reaches that role, then the host runs it instead; with no host session the configured fallback host runs it; and a receipt names the role, the runner asked for, the reason and the route that ran.
- Given a completed run, when its summary is read, then it reports the fallback count per runner and per role.
- Given a runner whose model route is not on its provider's allowlist, when the lane is about to start, then it is refused before the runner is launched.
- Given the claude CLI runner, when it launches, then it passes the bare flag, so the target repository's hooks and configured servers do not run untrusted.
- Given a review run through the opencode runner, when its receipt is read beside a host-run review's, then the two differ only in the route named.
- Given the lane, when it ships, then a security review of the process launch, the repository path and the permission flags is on its record.
Typed Links
- built on by
itd-2610161733316518(one feature built across a model matrix): Routes OpenRouter and local models into the matrix's tool-using builder and reviewer roles through these runners.
Audit Notes
- 2026-09-30 — Closed on the fake-harness tests under the person's ruling RN1 (2026-09-30: "(a) SAME RULE, FINISH ON FAKES: the runner may close on the fake-harness tests with the three live checks (iss-2609301519558538) recorded as owed."). The acceptance criteria are proven against fake harnesses on a pinned PATH (the opencode review-route criterion structurally, and the no-host fallback path in the core only, since its surface waits on the ADR decision 6 owes); no real claude or opencode binary has run a role. The three live checks are owed and not yet run: iss-2609301519558538 tracks them and stays open.
- 2026-10-02 — Amended by the facilitator's rulings RN2, OC1 and OC2 (2026-10-02), below the settled Decisions, which stand as written. RN2 ("SEALED, PAID KEY ONLY"): the claude runner keeps print mode with the bare flag on an API key, and only a personal route (the machine's file, or the person's own invocation) may hand a role to a runner; a repository route to a runner is skipped with a diagnostic and the role runs on the host as if unrouted, so a person whose account is a subscription runs the role in their own session (grounds: code.claude.com/docs/en/cli-reference and code.claude.com/docs/en/legal-and-compliance, read 2026-10-02). OC2 ("SAME RULE, PERSONAL ONLY"): the layer rule covers every runner, claude and opencode alike. OC1 ("SEAL IT, AND TEST IT"): the opencode runner launches with its project-configuration, CLAUDE.md-compatibility and external-skills switches set beside its no-plugins flag, and a hostile-fixture test asserts the exact launch. Criterion 1 ("a role whose runner is set") is read from here on as a role the person routes.
- 2026-10-02 — Declared mutual pair with itd-2609201916151817 (the technical facilitator's ruling CY1 of 2026-10-02, verbatim: "KEEP BOTH, DECLARED (a declared mutual pair)"). itd-2609201916151817's builds_on itd-2609201916056194 needs the runner a lane goes through: the command-line adapter the loop's process driver calls for each lane (the loop's decision 5). itd-2609201916056194's builds_on itd-2609201916151817 needs the loop's lane, where a role is reached, and its run record, where each fallback receipt and the per-runner and per-role fallback counts are written (the runner's criteria 1 to 4). Both edges stand. The record has no field record-lint's edge_cycle rule reads as a declared pair, so the linter still reports the cycle at warn until it does (iss-2610020836255174). Each piece is read from the two records' own scope, criteria and decisions; the ruling names none. Recorded by lane rd2 of autonomous run A.
Fidelity review OWED (receipt rcp-e5eee57c3e2a).
Grounds
- pursued: the goal is abcd in the preferred harness with the others reachable, and the run's reviews are its scarcest step; we expect a review sent through a second harness to be indistinguishable in the record; shown wrong if a reader cannot tell the route from the receipt alone, or if no role is routed off the host in a release
Frontmatter
| id | itd-2609201916056194 |
|---|---|
| type | intent |
| lifecycle | shipped |
| kind | standalone |
| severity | minor |
.abcd/development/intents/shipped/itd-2609201916056194-abcd-runs-a-delegated-agent-through-a.md
open on GitHub ↗ · commit history ↗
References7
- adr-2609221009491186A provider adapter serves only the models it lists, under a vendor denylist no listing overrides; everything else runs on the hostrelated
- itd-2In-Session Subagent Dispatch — the Default Host-Delegated Oraclesupersedes
- itd-6RepoPrompt over MCP is one opt-in reviewer route, beside the host's own agentsrelated
- itd-22abcd Reaches Any Harness Through an Adaptorrelated
- itd-51abcd Knows What "Safe Enough to Adopt" Means Before a New Harness Arrivesrelated
- itd-2609170822093401abcd proposes the model each agent deserves, and facilitates it where it canbuilds on
- itd-2609201916151817One verb takes a READY intent to delivered, and refuses while a decision is openbuilds on
Referenced by14
- adr-2610222222326104abcd drives agents with no host session only on the person's own invocation, on the person's own account behind a wall, and hands back what it cannot finishrelated
- iss-2610262037492393The shipped runner intent has a no-host fallback to an operator-configured host in scope; the build chapter says a host session always drives and the no-host path waits on an ADR.related
- iss-2610262044255432The worker-transcript draft says autonomous passes leave only an ephemeral log, but the shipped runner captures every role's transcript into abcd's storerelated
- iss-2610262045365573itd-6 says abcd never spawns claude -p; itd-2609201916056194 ships a claude CLI print-mode runnerrelated
- itd-6RepoPrompt over MCP is one opt-in reviewer route, beside the host's own agentsbuilt on by
- itd-2609081951381895abcd ships an OpenAI-compatible API adapter, and a provider serves only the models it listsrelated
- itd-2609201916151817One verb takes a READY intent to delivered, and refuses while a decision is openbuilt on by
- itd-2610030810370060abcd's interviews run in a plain Terminal, with the same question layoutrelated
- itd-2610100627454580An agent's text comes back from abcd's rewriter written for its reader and within its limitsrelated
- itd-2610161733316518One task done across a model matrix, re-runnablybuilt on by
- itd-2610240922461281The review set is a setting, in model classesbuilt on by
- itd-2610240933025076abcd plans unattended and asks only what a person ownsbuilt on by
- itd-2611031342011261abcd keeps a per-session ledger of every agent it launchesbuilt on by
- spc-2609221533057881abcd-runs-a-delegated-agent-through-a-command-line-model-runimplemented by
mentions19
- adr-25The LLM is host-delegated by default; oracles are opt-in adapters
- 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).
- dcn-26092500000000052026-09-25 — The layered configuration resolver has one home, `internal/core/layered`, and two file families; every later consumer reads through it rather than opening a file of its own (lane implementer, autonomous run A, on spc-2609180535002478 part 1). Precedence is flag, then repository, then machine, then bundled, and every value comes back with the layer and the origin that supplied it. The families are `layered.Config`, which is `.abcd/config.json` in the checkout the session resolved and `~/.abcd/config.json` on the machine, and `layered.OracleRouting`, which is `.abcd/config/oracle-routing.json` and `~/.abcd/oracle-routing.json`, the files itd-2609170822093401 names. Scalar keys go in `Config` under a namespace: `pace.*` (itd-2609201925079472), `oracle.review` (itd-6), `roles.<role>.runner` (itd-2609201916056194) and `match.threshold` (itd-2609212137116617). The per-agent routing table keeps its own file, as its intent names, and no new file family is added without an entry here. Loudness: an absent file is an absent layer; a present file that is malformed, symlinked, carries a key twice, trails content, declares the wrong `schema_version`, or (machine layer) is not the caller's own owner-only-writable file is an error naming it; a consumer claims its namespace and every key in it (`Stack.Claim`, with `*` for an open segment such as the role name), and an unknown key under a claimed namespace is refused. Because ahoy writes `oracle.backend` into the shared file, whoever claims `oracle` lists `backend` too. A winning value that does not decode or fails its check is refused, and nothing falls through to a lower layer or to the default.
- dcn-26093000000000262026-09-30 — The implement loop's state file is schema version 9 where a run works in parallel (ruling DR6, spc-2609202134341288), not the version 8 that spec's amendment names: the runner's record (itd-2609201916056194) reached the default branch first, on the v0.12.0 cut, and took version 8, so the two are numbered in the order they landed (integration integ24d of autonomous run A, `internal/core/implement/loop/state.go`). A version 7 or 8 file migrates its lane's one `awaiting` into `awaits` and is written back at version 9; a version 7 or 8 file carrying what only version 9 writes, and a version-9 file carrying `awaiting`, are refused naming the version and the key. The closed spec keeps its text; brief 34-build.md names the three versions.
- dcn-26100200000000112026-10-02 — Ruling CY1 of the technical facilitator (2026-10-02, asked in plain words by the interviewer abcd-a2 [75597f]; same local-tier file as the H3-0 entry above; recorded by lane rd2), verbatim: "runner <-> loop mutual builds_on: KEEP BOTH, DECLARED (a declared mutual pair)." The person's note, verbatim: "in future, I think we need an intent to ensure this is checked first, so that the loop -- while acceptable -- is precise about what is needed each way." Applied: itd-2609201916056194 (the command-line runner) and itd-2609201916151817 (the build loop) keep both builds_on edges, and each carries a dated Audit Notes line naming the pair and each direction's piece. The record has no field record-lint's edge_cycle rule reads as a declared pair, so none is invented, edge_cycle still warns on the pair, and the gap is captured as iss-2610020836255174. This answers the ruling iss-2609300848016421's remedy waited on. Not filed: the intent the note asks for, whose routing waits on the person's decomposition confirm.
- iss-2609240227236354itd-6 passes its readiness gate (intent ready itd-6: READY, all seven checks ok) but cannot be built from today's tree: its spec spc-2609211950427074 stands on four things that do not exist. (1) The build loop's validator stage the adapter implements, itd-2609201916151817 (planned, spec spc-2609202134338445 open; there is no build verb). (2) The layered oracle.review resolver the model-tier and pacing intents share, itd-2609170822093401 and itd-2609201925079472 (both planned, specs spc-2609180535002478 and spc-2609202134341288 open). (3) The command-line runner the adapters chapter entry sits beside, itd-2609201916056194 (planned, spec spc-2609221533057881 open). (4) An MCP client, which the spec's Approach names as a new dependency subject to the new-dependency sign-off; go.mod has none. itd-6 declares builds_on [itd-2] only, so the gate has no edge to refuse on and READY reads as buildable to a run that picks it.
- iss-2609260932372448A spec step that waits on another intent has no marker, so the build loop opens a lane on it: spc-2609202134338445's step 3 (the process driver) waits on itd-2609201916056194, and `build` takes the first unlanded step and briefs a lane there, which can only report the dependency. A blocked:/after: marker would change the first-unlanded-step rule and the ready row's count, which is a product design point, not an implementer's; routed to the product thinker.
- iss-2609300848016421itd-2609201916056194 (the CLI runner) and itd-2609201916151817 (the implement loop) each declare builds_on the other, so the dependency graph that sequences work (adr-2609212115255771) holds a two-node cycle and neither can be ordered first. The loop's record states its edge (it calls the runner for each lane, decision 5 and its builds_on note); the runner's record gives no reason for its edge on the loop. Found by a read-only sweep of planned and draft edges; record-lint has no cycle rule, so the gate passes it.
- iss-2609301519558538Deferred, owed to a person: the live proof of the command-line runner (itd-2609201916056194). Phase 1 and 2 prove every criterion on fake harness binaries only; no ruling admits a live check of the runner (ruling L names the site setup, the API adapter and the decision adapter; H8 lists the three paid-service checks and itd-6's RepoPrompt run), so the intent cannot close. What a person must run: (1) the intent's first proof, criterion 7 live: a ruthless review of a real lane routed roles.ruthless-reviewer.runner=opencode, from a lane the host drives, its return recorded beside a host-run review's and differing only in the route; (2) the claude runner under --bare with --permission-mode dontAsk and --allowedTools, confirming a nested claude does not refuse under an inherited CLAUDECODE, and noting that the headless page (read 2026-09-30) says bare mode never reads OAuth or the keychain, so the claude runner needs ANTHROPIC_API_KEY, a paid API key rather than the person's subscription; (3) opencode run --format json --pure: the event shape (the CLI page does not document it; the adapter assumes step_start/text/step_finish with a sessionID and a part) and whether run mode prompts for a permission, which the timeout bounds and records as a fallback.
- iss-2610020836255174The intent record has no way to declare a mutual builds_on pair that record-lint's edge_cycle rule honours. The technical facilitator ruled on 2026-10-02 that three two-intent cycles stay as declared mutual pairs, each direction naming the piece it needs (CY1: itd-2609201916056194 and itd-2609201916151817; CY2: itd-14 and itd-15, itd-2609081951381895 and itd-2609170822093401), but the frontmatter offers only builds_on and blocked_by, and edge_cycle reports every strongly connected set whatever the records say, so the three pairs are declared only in each intent's Audit Notes and still read as cycles to the linter, and to anything that orders work by these edges. Until a declared pair is something the linter can read, edge_cycle cannot be promoted from warn to blocker without refusing the ruled pairs.
- iss-2610161709506207The opencode runner stamps the model it was asked for as the model that answered: internal/core/runner/opencode.go:85 sets ans.Model = o.model after parsing, so the route receipt's model field can never differ from the request, and a run routed through opencode to OpenRouter or a local server cannot show that a different model, provider or fallback served it.
- itd-33Multiple Agents Coordinate On Intent And Spec Work Without Duplicating Effort Or Producing Competing Artefacts
- itd-63The Amateur Coder Is Told What Is Being Installed And Why, Not Just Asked To Run A Command
- itd-2610100704270254An abcd run sets how many sub-agents it keeps alive by the models they run on
- spc-2609202134338445one-verb-takes-a-single-intent-from-ready-to-delivered-witho
- spc-2609202134341288an-autonomous-implementation-run-paces-itself-by-default-and
- spc-2609211950427074rp-mcp-only-integration
- spc-2609301921521360the-budget-check-and-the-rate-limit-checkpoint
- spc-2610030911534855a-person-can-run-abcd-s-interviews-in-a-plain-terminal