ADR-173: Player Queue as Unattended-Execution Substrate; Relationship to Warp
Status: Proposed Date: 2026-05-07 Tracker: warp#1881 (Phase 0 of EPIC warp#1838) Cross-refs: ADR-170, ADR-171, ADR-172, ADR-166
Status note — Proposed not Accepted
Same gating shape as ADRs 171 + 172. Reclassify to Accepted when warp#1762 Phase 1.1 (type:deploy_request schema) ships, demonstrating the typed-ticket-on-Warp pattern works in production.
Context
ADRs 170-172 commit to the substrate's threat model, execution model, and wire contract. They do not commit to where the queue lives. Two options were considered:
- A separate "factory queue" service — its own Postgres database, its own HTTP API, its own ops surface.
- Ride on Warp — the existing fleet work-item queue, with new typed-ticket discriminators.
Option 1 is the substrate paper's earlier draft framing (the paper used the term player queue and named a target home factory-cli/pkg/playerqueue/). Option 2 emerged as the Fleet Runtime paper (warp#1859) walked through the architectural overlap with ADR-166 and warp#1763 — both of which are already riding on Warp's atomic-claim primitive for type:deploy_request and type:ci_run tickets.
The risk of option 1 is operational sprawl — two queue services with similar lifecycle semantics (claim, heartbeat, lease, complete) but separate storage, separate UIs, separate audit trails, separate maintenance.
The risk of option 2 is conflation — substrate-shaped jobs (large inputs, long traces, structured outputs) sitting in the same table as agent work-items (a few hundred bytes of human-readable prose).
This ADR commits to option 2 with explicit boundaries.
Decision
The substrate's queue rides on Warp's existing atomic-claim primitive. Pipeline jobs become typed Warp items with kind: pipeline_job (or a similar discriminator); the inputs/outputs/scope/trace are stored in fields that the existing item-table accommodates.
What this means concretely
- Storage. Same Postgres backend, same
itemstable, sameSELECT … FOR UPDATE SKIP LOCKEDclaim semantics. New columns or sidecar tables for the substrate-specific fields (inputs_manifest, scope, outputs_manifest, trace_bundle_uri). - API. Substrate jobs expose
/api/v1/jobs/*endpoints (alongside Warp's existing/api/v1/items/*). The endpoints share the same auth and the same identity gates but expose substrate-shaped operations (reservations, contract validation, output manifests). - Identity. Workers authenticate with the same agent-token scheme Warp already uses. Per-claim tokens (substrate paper §6.5 Layer 1) are issued by the vault, not by Warp's auth layer.
- Storage of large artefacts. Folder inputs/outputs go to an object store (per ADR-172), referenced from the Warp item by URI + SHA-256. The Warp items table never carries large blobs directly.
- Audit retention. Trace bundles ride the substrate's own retention policy (≥ 90 days per ADR-170); the Warp item itself retains its lifecycle metadata indefinitely (claim/heartbeat/complete events).
Naming
The substrate's earlier paper drafts used the term player queue to distinguish it from the existing gyrum-labs/factory-queue repo (a JSON-file backlog, unrelated). This ADR clarifies: player queue and Warp are the same store with separate ticket-type semantics. Throughout downstream ADRs and code, prefer:
- "Warp queue" for the existing Postgres backend.
- "
type: pipeline_job" (or sister discriminators liketype:deploy_request,type:ci_run) for substrate-shaped tickets. - Avoid "player queue" as a substantive in new prose — it was scaffold vocabulary while the architectural relationship was unclear; ADR-173 obsoletes it.
Don't conflate with
gyrum-labs/factory-queue. That repo exists and is named confusingly close — it's a JSON-file backlog of project ideas, unrelated to the substrate. The substrate's queue is Warp, not factory-queue.
Relationship to ADR-166 and warp#1763
ADR-166's type:deploy_request and warp#1763's planned type:ci_run are sibling ticket types. They share:
- The same atomic-claim primitive.
- The same heartbeat/lease shape.
- The same audit substrate.
They differ in:
- Schema. Each ticket type has its own structured spec (a deploy carries slug+image-tag+manifest-ref; a CI run carries workflow-path+commit-sha+resolved-inputs).
- Workers. Receivers and executors (ADR-166) handle deploy-shape work; runner agents (warp#1763) handle CI-shape work; players (this ADR's vocabulary) handle the general substrate-shape work.
The types are separate; the queue is one. Future agents should not conflate them but also should not separate the queue.
Consequences
What becomes easier
- Single audit trail across all fleet work. A merge fires a webhook → ADR-166 receiver files a
type:deploy_request→ executor claims → deploy completes → all of that is observable in the same/operateUI as substrate-shaped pipelines and CI runs. - Operator's mental model collapses. "Where's that job?" always answers from
warp-readyor/operate, regardless of work type. - Cross-type orchestration is trivial. A pipeline can file a Warp ticket that triggers a deploy that completes and triggers a CI run. All in one queue, all in one audit log.
What gets harder
- Schema migrations to the items table touch all consumers. Adding a new column for substrate-specific data needs to be backwards-compatible with existing agent work-items. Migrations must be reviewed across consumer lists.
- Throughput sharing. Substrate jobs (potentially high-frequency dropped-baton sweeps) and agent work-items share the same atomic-claim infrastructure. Postgres can comfortably handle ~1000 jobs/min on the existing backend (per substrate paper §5.8 capacity planning); we are two orders of magnitude below that ceiling. If substrate work ever approaches that ceiling, the right move is partitioning by
priorityortype, not horizontal sharding.
What stays out of scope
- A separate factory-cli/pkg/playerqueue/. The earlier draft of warp#1838 named this; this ADR retracts that target. Player-queue-shaped Go code lives inside warp's existing api codebase, not as a separate package.
- Multi-region / multi-tenant queues. Single-region Warp Postgres is the substrate. Multi-region is a future concern when fleet scale demands it.
- Replacing Warp's existing items table with anything else. This ADR assumes Warp's atomic-claim primitive stays; if Warp is ever rebuilt (fleet-internal replatform), the substrate's queue rebuild rides that work, not its own schedule.
Compliance / how reviewers check
A PR adding a new ticket type for substrate-shaped work satisfies this ADR iff:
- The ticket type extends Warp's existing
kind/typediscriminators rather than introducing a parallel storage path. - Large artefacts (inputs, outputs, trace bundles) are stored in the object store, referenced by URI + SHA-256, not embedded in the items table.
- The ticket's auth path uses Warp's existing agent-token scheme; per-job credentials come from the vault, not from extra Warp-level tokens.
- The ticket's lifecycle (claim → heartbeat → complete or fail) maps onto Warp's existing primitives without inventing new ones.
Cross-link
The substrate paper §2.4 walks through this two-surface separation (Warp = coordination; the substrate's queue = runtime); this ADR retracts the separate-store implication of that section in favour of shared-store-with-separate-discriminators. The Fleet Runtime paper (warp#1859, merged) Chapter 3 §3.1 is the up-to-date vocabulary mapping. ADR-166 is the smallest-scope precedent: type:deploy_request is already a Warp ticket type today.