Skip to main content

Crate tapes_harnesses

Crate tapes_harnesses 

Source
Expand description

§tapes-harnesses

Client-side knowledge about coding-agent harnesses, for Tapes capture.

This is the half of capture that changes because a harness was added. Its membership test is one question: would adding one more harness change this? If yes it belongs here; if no it belongs in tapes-capture, which this crate depends on and which may never depend back.

Consumed by tapesctl and by closed-source daemon clients alike, so every client launches and attributes sessions with the same code rather than with separate implementations kept in agreement by review.

§Public seams

  • harness — the registry, and the vocabulary the rest of the crate speaks. One declaration per harness bundles its id, User-Agent rule, launch support, attribution strategy, transcript location, and plugin needs. Every other module takes its harness ids from here, and consumers derive their supported-agent lists from it rather than hard-coding one.
  • launch — per-harness environment and config injection for running a harness under a capture proxy. Recipes are pure: they plan an argv prefix, an environment overlay, and any config documents a harness reads from disk. Spawning, materialisation, and cleanup stay with the consumer.
  • attribution — session-file reads, fork-parent recovery, peer-PID lookup, and the session watchers, grouped per harness (attribution/claude/, attribution/codex/, …) with the harness-agnostic pieces shared. The composition itself lives in attribution/pipeline.rs.
  • plugin — the plugin and extension artifacts a harness needs on disk, with the vendor-neutrality bar enforced by the module’s own tests.
  • transcript — discovering and packaging harness transcripts for the transcript ingest lane. Delivery, auth, and retry are the consumer’s.
  • config — the config-document patch grammars the launch recipes plan against.

No feature flags: everything above is always compiled.

§Adding a harness

Start at src/harness.rs, then follow docs/adding-a-harness.md at the repository root — it covers the registry declaration, when a launch recipe is needed, which attribution strategy applies, and what the tapes deriver needs on its side.

§Stability

This crate is supported public API, meant to be depended on directly. So are its two siblings — tapes-capture (the capture protocol) and tapes-client (the read client) — and all three version independently on crates.io.

Pre-1.0, 0.x versions carry the usual Cargo meaning: a breaking change bumps the minor (0.2.0), anything compatible bumps the patch (0.1.1). What counts as breaking is the boundary in the repository README, not just the signatures: knowledge that stops being harness-specific belongs in tapes-capture, and moving it is a break here even when nothing stops compiling.

Adding a harness to the registry is additive, and it is the change most worth reading about — consumers derive their supported-agent lists from that registry rather than hard-coding one, so a new entry appears in their surface without their doing anything. This crate also requires tapes-capture at a version, so an upgrade may carry one; the changelog says when it does.

Changes are recorded in CHANGELOG.md.

§License

Dual-licensed under MIT OR Apache-2.0; see the repository root.

§Module map

Exactly three places hold harness knowledge; this crate is one of them (the deriver and the envelope spec/fixtures are the other two). It owns these responsibilities:

  • harness — the registry: one declaration per harness, bundling its id, User-Agent rule, launch support, attribution strategy, transcript location, and plugin needs. The other modules take their harness ids from it and consumers derive their supported-agent lists from it, so adding a harness starts in exactly one place.
  • launch — per-harness env/config injection to run a harness under a capture proxy.
  • config — persistent harness-config patch grammars: how an installer patches a capture provider into a harness’s own config file, idempotently and preserving the user’s content. Where launch plans per-process config that dies with the process, this module owns the durable install a desktop app or long-lived integration needs.
  • plugin — the artifacts a harness with no base-URL knob needs installed into it before capture is possible at all, and the environment contract those artifacts read. Consumers are installers; the bytes live here so every client installs the same ones.
  • attribution — session-file reads, fork-parent recovery, peer-PID lookup, the peer-trust ancestry walk, and the codex session watcher, grouped per harness.
  • transcript — discovering and packaging harness transcripts for the POST /v1/ingest/transcript lane.

§The three ways a harness gets captured

This is the distinction to hold on to, because it decides which modules apply to a given harness. It correlates with harness::AttributionStrategy but is not the same axis: that enum says how a request acquires an identity, this says how the traffic is reached at all.

mechanismwhen it appliesplan it with
Launch redirect — point the harness’s base-URL knob at a proxythe harness has such a knob (claude, codex, opencode)launch
Installed plugin — code runs inside the harness and stamps its own envelopethe harness has no such knob (pi)plugin
Lifecycle hooks — a hook plugin reports allowlisted evidence at session boundariesthe harness is configured rather than launched (codex-app)plugin::codex_app and config

A harness declares which of these it needs through harness::LaunchSupport and harness::PluginDelivery; nothing here infers it.

§What is not here

Everything above changes when a harness is added. The parts of capture that do not live in tapes_capture, which this crate depends on: the X-Tapes-* envelope producer and its harness-id vocabulary, the capture-gateway environment contract and launch-nonce protocol, peer-PID lookup, and the peer-trust ancestry check. The edge runs one way by construction: a harness module may reach for a capture primitive, and nothing over there can reach back, because the moment a capture primitive knows a harness’s name it stops being the thing every harness shares.

The envelope is the sharpest case, because the arrow points the way that first looks backwards. Harness ids are envelope vocabulary — they are what goes on the wire — so harness takes its ids from tapes_capture rather than declaring them and having the envelope import them back. Reading them the other way is what used to make the two mutually dependent, and it is why the producer now asks for a tapes_capture::HarnessSession instead of naming any harness’s session type.

§Provenance

attribution is extracted from a daemon client’s proxy session layer — the code that validated peer-PID attribution and fork-parent discovery against real Claude and Codex traffic.

launch is extracted from the same client’s per-agent env/config injection, with the Go tapes start opencode/codex knowledge folded in — including opencode, which that client never supported. Its recipes are pure: they plan argv, environment, and config documents, and the consumer owns process spawning and cleanup.

transcript is extracted from that client’s transcript uploader — its discovery/packaging half, the push trigger, and the ingest payload shape — and adds a startup sweep of the transcript tree, which closes a gap every daemon client has: a session that began and ended while the daemon was down is never re-registered by live traffic, so its fork skeleton was previously lost. Delivery, auth, and retry stay in each client.

§Names

The repository is tapes-crates; this crate is one of its four members. There is no tapes-harness crate — the singular spelling is reserved as a stub redirect so the near-miss cannot be claimed by someone else. tapes is a different repository entirely: the server a capture client ships to.

Modules§

attribution
Session attribution.
config
Persistent harness-config patch grammars.
harness
The harness registry — the one place a harness is declared.
launch
Launch recipes.
plugin
Plugin artifacts — the files that must be installed into a harness before its traffic can be captured at all.
transcript
Transcript tailer.