Spec Studio

One adapter module per harness, behind one contract

spec:cc-agents.adapter-contractContract · featureready floor ready

Packs: none

Design: 4 design entries · 4 pinned declarations · no decisions

Findings: no errors, no warnings

specs/runner/adapter-contract.sdp.md

3 open questions and no blocking questions; implementation binding present, verifier binding present, and expected-outcome oracle none.

An adapter is one module, plugin/sidecar/adapters/<harness>.mjs; the harness name is also the agent type's name. The sidecar imports it by that name and knows nothing else about the harness. The prose of the contract lives in plugin/sidecar/adapters/CONTRACT.md; this Spec holds what the code does, and the questions below hold where the two differ.

Intent
actor
The author of an adapter for a new harness, and the sidecar that drives it.
problem
Harnesses differ in how they start, steer, stop, fail and leave work behind, and the sidecar must stay free of every one of them.
outcome
A harness is added by writing one module that exports a few functions and emits a closed set of neutral events.
value
The mod and the sidecar never change when a harness is added or fixed.

Open questions

  • non-blocking · question.capKeyMeaning line 22 · specs/runner/adapter-contract.sdp.md:22 · CONTRACT.md says the cap key names the account a turn spends, while the code keys Codex by one machine-wide bucket named codex and OpenCode by opencode:<provider>, which does not identify distinct accounts.Decider: ownerProvisional: The key is whatever capFor returns; nothing establishes credential identity.
  • non-blocking · question.extraArguments line 25 · specs/runner/adapter-contract.sdp.md:25 · The sidecar also passes options to check for a job and options and log to the turn constructor, which CONTRACT.md does not list; OpenCode ignores them.Decider: delegatedProvisional: The contract grows an options argument described by the Relay-compatible job type.
  • non-blocking · question.olderDescription line 28 · specs/runner/adapter-contract.sdp.md:28 · Early reports describe a four-method adapter without preparation and reaping; the current contract and code add both.Decider: delegatedProvisional: Only the current contract is described here.
Contract
rule line 34 · specs/runner/adapter-contract.sdp.md:34
An adapter module exports capFor, check and one function named create<Harness>Turn, and may export reap and needsModel, and the sidecar finds the constructor by its name.
rule line 35 · specs/runner/adapter-contract.sdp.md:35
The function capFor takes the model and returns a cap key and a default number of concurrent turns, which the user's caps option can replace.
rule line 36 · specs/runner/adapter-contract.sdp.md:36
The function check takes the working directory, model, effort and mode before anything starts, and answers the values the turn will run with, or a refusal that says what would work.
rule line 37 · specs/runner/adapter-contract.sdp.md:37
A mode the harness cannot enforce is refused by check, so no turn runs under a mode that is not enforced.
rule line 38 · specs/runner/adapter-contract.sdp.md:38
The turn constructor returns prepare, run, steer, interrupt and kill, and prepare runs first and names the turn's work as plain JSON before anything executable has been submitted.
rule line 39 · specs/runner/adapter-contract.sdp.md:39
A turn that the harness accepts emits turn once, and before it only status lines about setting up the thread; after it come any of text, tool, status, message and usage. A turn that fails, is refused or is cancelled before the harness accepts it emits no turn. Every turn emits final once, then end once with a status of completed, interrupted or failed, and nothing after end.
rule line 40 · specs/runner/adapter-contract.sdp.md:40
The final event carries the answer exactly as the harness produced it, and usage is the turn's own, not the thread's.
rule line 41 · specs/runner/adapter-contract.sdp.md:41
A steer resolves once the harness accepted the text, rejects when it cannot, and rejects after an interrupt has begun.
rule line 42 · specs/runner/adapter-contract.sdp.md:42
An interrupt resolves once end was emitted, or when the adapter's own confirmation wait runs out with the turn still open, and the end follows only when termination is confirmed. The wait is the adapter's, in phases, and has no single deadline: OpenCode first waits for the turn's startup submission to settle and then confirms for ten seconds, and Codex waits up to three seconds for the stop request, one and a half for the end and eight for its process group, 12.5 seconds in all.
rule line 43 · specs/runner/adapter-contract.sdp.md:43
The end event reports the outcome to the agent and does not free capacity, which comes back only when the turn's work is confirmed ended, by the work's process group or by the adapter's reap.
rule line 44 · specs/runner/adapter-contract.sdp.md:44
An error is null or carries a kind of capacity, rate-limit, quota, auth or other, a message and whether trying later could help, and the sidecar never replays a turn.

11 unkeyed rules.

Design
eventKinds line 48 · specs/runner/adapter-contract.sdp.md:48
turn, text, tool, status, message, usage, final, end.

no citation recorded

errorKinds line 49 · specs/runner/adapter-contract.sdp.md:49
capacity, rate-limit, quota, auth, other.

no citation recorded

workShapes line 50 · specs/runner/adapter-contract.sdp.md:50
{ pgid } for a process group that a guard leads, and any other plain JSON that reap(work) understands.

no citation recorded

turnConstructor line 51 · specs/runner/adapter-contract.sdp.md:51
create<Harness>Turn({ cwd, model, effort, mode, threadId, prompt, emit }) returning { prepare, run, steer, interrupt, kill }.

no citation recorded

Verification

Verifier bindings

  • test:cc-agents.opencode-suite · the OpenCode adapter against a fake OpenCode service · anchored · test anchor binding present · tests/adapters/opencode.test.mjs:4
  • test:cc-agents.sidecar-suite · the sidecar, the Codex adapter, the slots and the guard against a fake Codex app-server · anchored · test anchor binding present · tests/sidecar.test.mjs:4
Design view

Requirement drawing

  • Declared verifying examples: no declared verifying example.
Requirement drawing for spec:cc-agents.adapter-contract
The diagram as text
  • spec:cc-agents.adapter-contract: One adapter module per harness, behind one contract · declared Spec
  • spec:cc-agents.runner: Other model families run as real Claude Code agents · declared Spec
  • impl:cc-agents.codex: the Codex adapter: one guarded app-server per turn, sandbox modes, MCP off · anchored code · plugin/sidecar/adapters/codex.mjs:15
  • impl:cc-agents.opencode: the OpenCode adapter: a session on the user's service, polled by transcript, reaped by lease · anchored code · plugin/sidecar/adapters/opencode.mjs:26
  • impl:cc-agents.sidecar: the per-session sidecar: owns each harness turn, its events and its slot, and drives every adapter through the contract · anchored code · plugin/sidecar/server.mjs:12
  • test:cc-agents.opencode-suite: the OpenCode adapter against a fake OpenCode service · anchored test binding · binding present · tests/adapters/opencode.test.mjs:4
  • test:cc-agents.sidecar-suite: the sidecar, the Codex adapter, the slots and the guard against a fake Codex app-server · anchored test binding · binding present · tests/sidecar.test.mjs:4
  • spec:cc-agents.adapter-contract refines spec:cc-agents.runner · declared
  • impl:cc-agents.codex satisfies spec:cc-agents.adapter-contract · anchored
  • impl:cc-agents.opencode satisfies spec:cc-agents.adapter-contract · anchored
  • impl:cc-agents.sidecar satisfies spec:cc-agents.adapter-contract · anchored
  • test:cc-agents.opencode-suite verifies spec:cc-agents.adapter-contract · anchored
  • test:cc-agents.sidecar-suite verifies spec:cc-agents.adapter-contract · anchored
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred and chosen syntax: requirementDiagram; exact refines, satisfies and verifies, with claim words in nodes and per-edge comments.

Decisions

No decision record named.

Runtime
  • impl:cc-agents.codex · the Codex adapter: one guarded app-server per turn, sandbox modes, MCP off · plugin/sidecar/adapters/codex.mjs:15

    Uses

    None.

  • impl:cc-agents.opencode · the OpenCode adapter: a session on the user's service, polled by transcript, reaped by lease · plugin/sidecar/adapters/opencode.mjs:26

    Uses

    None.

  • impl:cc-agents.sidecar · the per-session sidecar: owns each harness turn, its events and its slot, and drives every adapter through the contract · plugin/sidecar/server.mjs:12

    Uses

    None.

Runtime observation: not tracked.

Evidence

Runtime observation: not tracked.

Build attestations: not tracked.

Deployments: not tracked.

Bills of materials: not tracked.

Impact
Relations of spec:cc-agents.adapter-contract
The diagram as text
  • spec:cc-agents.adapter-contract: One adapter module per harness, behind one contract
  • spec:cc-agents.codex-adapter: GPT runs through the user's Codex, one app-server per turn
  • spec:cc-agents.opencode-adapter: Every other family runs through the user's OpenCode service
  • spec:cc-agents.runner: Other model families run as real Claude Code agents
  • spec:cc-agents.sidecar: One sidecar per Claude session owns the session's harness turns
  • spec:cc-agents.adapter-contract refines spec:cc-agents.runner · declared
  • spec:cc-agents.codex-adapter dependsOn spec:cc-agents.adapter-contract · declared — the Codex adapter is one implementation of the contract
  • spec:cc-agents.opencode-adapter dependsOn spec:cc-agents.adapter-contract · declared — the OpenCode adapter is one implementation of the contract
  • spec:cc-agents.runner dependsOn spec:cc-agents.adapter-contract · declared — the seam that keeps every harness out of the other two layers
  • spec:cc-agents.sidecar dependsOn spec:cc-agents.adapter-contract · declared — the sidecar drives every harness only through the adapter contract
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred syntax: flowchart. Chosen syntax: labelled flowchart, preserving exact relations, claims and folds.

Rests on

Rested on by

Distance 1

  • spec:cc-agents.codex-adapter

    Implementing units

    • impl:cc-agents.codex · plugin/sidecar/adapters/codex.mjs:15

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.opencode-adapter

    Implementing units

    • impl:cc-agents.opencode · plugin/sidecar/adapters/opencode.mjs:26

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.opencode-suite

    Packs: None

  • spec:cc-agents.runner

    Implementing units

    None.

    Referencing units

    None.

    Enabled verifiers: None

    Packs: None

  • spec:cc-agents.sidecar

    Implementing units

    • impl:cc-agents.launch · plugin/sidecar/launch.mjs:7
    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.sidecar-suite

    Packs: None

Distance 2

  • spec:cc-agents.cross-session-slots

    Implementing units

    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12
    • impl:cc-agents.slots · plugin/sidecar/slots.mjs:16

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.exact-answer

    Implementing units

    • impl:cc-agents.mod · plugin/hooks/register.js:9

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite

    Packs: None

  • spec:cc-agents.hq-links

    Implementing units

    None.

    Referencing units

    None.

    Enabled verifiers: None

    Packs: None

  • spec:cc-agents.installation

    Implementing units

    None.

    Referencing units

    • impl:cc-agents.harness-options · plugin/hooks/harnesses.js:5

    Enabled verifiers: None

    Packs: None

  • spec:cc-agents.machine-guard

    Implementing units

    • impl:cc-agents.machine · plugin/sidecar/machine.mjs:20

    Referencing units

    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Enabled verifiers: test:cc-agents.machine-suite, test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.mod-layer

    Implementing units

    • impl:cc-agents.mod · plugin/hooks/register.js:9

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite

    Packs: None

  • spec:cc-agents.model-templates

    Implementing units

    • impl:cc-agents.harness-options · plugin/hooks/harnesses.js:5
    • impl:cc-agents.mod · plugin/hooks/register.js:9

    Referencing units

    • impl:cc-agents.sync-templates · scripts/sync-templates.mjs:10

    Enabled verifiers: test:cc-agents.mod-suite

    Packs: None

  • spec:cc-agents.pane

    Implementing units

    • impl:cc-agents.harness-options · plugin/hooks/harnesses.js:5
    • impl:cc-agents.pane · plugin/hooks/pane.js:13

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite, test:cc-agents.pane-suite

    Packs: None

  • spec:cc-agents.relay-job-type

    Implementing units

    • impl:cc-agents.mod · plugin/hooks/register.js:9
    • impl:cc-agents.relay-header · plugin/sidecar/relay-header.mjs:9
    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite, test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.steering-and-stop

    Implementing units

    • impl:cc-agents.mod · plugin/hooks/register.js:9
    • impl:cc-agents.sidecar · plugin/sidecar/server.mjs:12

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.mod-suite, test:cc-agents.sidecar-suite

    Packs: None

  • spec:cc-agents.work-containment

    Implementing units

    • impl:cc-agents.groups · plugin/sidecar/groups.mjs:13
    • impl:cc-agents.guard · plugin/sidecar/guard.mjs:16

    Referencing units

    None.

    Enabled verifiers: test:cc-agents.sidecar-suite

    Packs: None

Relations

Relation and directionOther endClaimReasonExercised keys
refines · outgoingOther model families run as real Claude Code agents
spec:cc-agents.runner · resolves
declaredNoneNone
dependsOn · incomingGPT runs through the user's Codex, one app-server per turn
spec:cc-agents.codex-adapter · resolves
declaredthe Codex adapter is one implementation of the contractNone
dependsOn · incomingEvery other family runs through the user's OpenCode service
spec:cc-agents.opencode-adapter · resolves
declaredthe OpenCode adapter is one implementation of the contractNone
dependsOn · incomingOther model families run as real Claude Code agents
spec:cc-agents.runner · resolves
declaredthe seam that keeps every harness out of the other two layersNone
dependsOn · incomingOne sidecar per Claude session owns the session's harness turns
spec:cc-agents.sidecar · resolves
declaredthe sidecar drives every harness only through the adapter contractNone

Bindings

Implementation binding: present

  • impl:cc-agents.codex · the Codex adapter: one guarded app-server per turn, sandbox modes, MCP off · plugin/sidecar/adapters/codex.mjs:15
  • impl:cc-agents.opencode · the OpenCode adapter: a session on the user's service, polled by transcript, reaped by lease · plugin/sidecar/adapters/opencode.mjs:26
  • impl:cc-agents.sidecar · the per-session sidecar: owns each harness turn, its events and its slot, and drives every adapter through the contract · plugin/sidecar/server.mjs:12

Design reference: none

Verifier binding: present

  • test:cc-agents.opencode-suite · the OpenCode adapter against a fake OpenCode service · anchored · tests/adapters/opencode.test.mjs:4 · test anchor
  • test:cc-agents.sidecar-suite · the sidecar, the Codex adapter, the slots and the guard against a fake Codex app-server · anchored · tests/sidecar.test.mjs:4 · test anchor

Expected-outcome oracle: none

Runtime observation: not tracked.

Run results: not recorded in the graph.

Findings

None.

Composing (0)

Composing needs the script.