ADR-171: Player + Pipeline + Blocks as the Unattended Execution Model
Status: Proposed Date: 2026-05-07 Tracker: warp#1881 (Phase 0 of EPIC warp#1838) Cross-refs: ADR-118, ADR-124, ADR-166, ADR-170 (sibling — the threat model this design satisfies)
Status note — Proposed not Accepted
This ADR locks the design now while the substrate paper is fresh, but the architecture it commits to is gated on prior phases shipping evidence:
- Stage B (warp#1762, ADR-166) must prove the receiver/executor pattern on deploys.
- Stage C (warp#1763) must prove the queue-driven CI pattern on one workflow.
- Stage D (warp#1838) generalises both.
Reclassify to Accepted when Stage C4 (the prototype's "what breaks" doc) lands and the design survives. See docs/features/fleet-runtime-activation-arc.md for the full activation arc.
Context
The fleet's unattended work runs as Claude Code sessions on the operator's laptop. The naive shape for moving that work onto a server — "put claude -p in a container with the team's credentials" — is foreclosed by ADR-170's threat model. So what is the right shape?
Three primitives can build it.
The substrate paper (docs/proposals/unattended-pipeline-execution-substrate.md, shipping in this PR) names the design: player + pipelines + blocks. ADR-118 (per-block validation) and ADR-124 (pipeline tooling is block-composition) are the prior principles this ADR operationalises. ADR-166 is its smaller-scope precedent: receivers and executors are players; deploys are pipelines.
Decision
The substrate's unattended-execution model has three primitives. Workers run a player; players execute pipelines; pipelines compose blocks.
Player
A small runtime — a few hundred lines of Go — whose only job is to execute a pipeline. The player:
- Pulls jobs from the queue (substrate-paper §3, ADR-172 for the contract).
- Validates the contract pre-flight (fail-closed on schema skew or scope-unobtainable).
- Requests scoped credentials from the vault per pipeline declaration.
- Spawns each block in a sandboxed subprocess.
- Streams structured trace events to the audit log.
- Reports completion with the output manifest.
- Releases credentials and dies.
The player has no compiler, no shell, no git, no gh, no claude binary baked in. It is the supervisor; capability comes from the blocks it runs.
The player is what ADR-166 calls an executor and what warp#1763 calls a runner agent. Same primitive, different vocabularies. See docs/features/fleet-runtime-activation-arc.md and the Fleet Runtime paper §3.1 for the vocabulary mapping.
Pipeline
A declarative YAML composition of blocks. Versioned by id + integer (e2e-run@v3). Content-addressed by SHA-256; the runtime rejects mismatches. Each pipeline declares its inputs (with JSON Schemas), its outputs (with JSON Schemas + retention), its scope (credentials + network egress + filesystem + privacy classification — per ADR-170), its contract (timeout, heartbeat cadence, cancellation semantics), and its steps (block invocations with input/output mappings).
A pipeline is what ADR-166 calls a playbook and what warp#1763 calls a workflow. The same vocabulary mapping.
Block
A bounded primitive. Each block is a small program (a binary, a script, a container, a Go module) with declared input schema, output schema, and scope. Blocks are independently security-reviewed. Blocks ship with regression suites (per ADR-118). The substrate paper §6.4 specifies the v0 set: gh-query, warp-add, file-template, model-call, clone-repo.
Blocks are a primitive the deploy and CI vantages don't have today (their playbooks/workflows are monolithic units). When those vantages graduate from monolithic to block-composition, they inherit the substrate's primitives directly.
The trust boundary line
This ADR draws an explicit line:
| Context | Full-agent claude -p (Bash, Read, Write, Edit, network) |
Bounded model-call block (string in, string out, broker-routed) |
|---|---|---|
| Operator's laptop (interactive, human present) | First-class. | Yes, when called from a script. Not the dominant pattern on the laptop. |
| CI runner / Actions cron | Only via the player + bounded block model. Tools available to the agent must be those declared by a pipeline's block composition. | First-class. The dominant pattern for off-laptop scheduled work. |
| Server-hosted worker container (unattended) | No. Structurally incompatible with ADR-170's threat model. | First-class. The dominant pattern for server-hosted work. |
The line is trust-boundary-shaped, not autonomy-shaped. The substrate is comfortable with autonomous unattended work; what it forecloses is placing high-value credentials and full tool access in a container an attacker can reach.
Consequences
What becomes easier
- New use cases ship as new pipelines (declarative YAML). The block library grows by accretion of small, reviewed primitives.
- Audit + replay are first-class. Every block invocation is
(block-id, input-hash, output-hash, exit-code, duration). Aclaude -psession in a container is a non-deterministic transcript; a pipeline run is structured data. - Composability. The same
model-callblock serves observers, e2e summarisers, feature-impl review steps. The sameclone-repoblock serves all of them.
What becomes harder
- Improvisational flexibility goes away. A
claude -psession can decide on the fly to read another file, run another command, branch the plan. A pipeline can only do what its block composition declares. For unattended execution against real codebases and real credentials, this rigidity is a feature. - Author cost. Writing a pipeline costs more than running a script. The tradeoff is that pipelines are reviewable, auditable, retryable, schedulable.
What stays out of scope
- Full agent flexibility on the operator's laptop is preserved. The substrate doesn't constrain interactive work.
- The existing
gyrum-pipelineslibrary (private alpha v0.0.x, four packages:pkg/block,pkg/pipeline,pkg/runner,pkg/retro) is the v0 substrate the player composes against. The substrate doesn't replace that library; it adds the runtime + queue + worker hardening + scope contract on top.
Compliance / how reviewers check
A PR that adds a pipeline or block satisfies this ADR iff:
- The pipeline declares the full structure (inputs, outputs, scope, contract, steps) per ADR-172's contract.
- Blocks are bounded — declared input/output schemas, declared scope (credentials + network + filesystem), regression corpus.
- No
claude -pwith full tool surface in any unattended worker. Full agents only on the operator's laptop.
Cross-link
The substrate paper Part II §2.2-2.5 walks through the trust-boundary line with the per-line reasoning. ADR-172 (this PR) specifies the contract that connects pipelines to players. ADR-166 is the smaller-scope precedent. ADR-118 + ADR-124 are the prior principles.