# Paperclip Runner compatibility and rollout - Status: Proposed - Date: 2026-08-24 - Parent decision: [Paperclip Runner architecture](paperclip-runner.md) ## Purpose This document defines compatibility rules for introducing the experimental Paperclip Runner. These rules are acceptance criteria for each implementation change. They are not a migration plan for existing adapters. ## Compatibility invariants 1. Existing adapter selection is authoritative. A direct adapter stays direct. 2. A non-runner run must not start runnerd or open PRP. 3. A non-runner run must not create native result, finalization, status-decision, or runner-transport records. 4. A non-runner run must not invoke native status arbitration. 5. Direct adapters keep their current transcript, interaction, cancellation, result, and finalization behavior. 6. Runner-only UI controls depend on persisted runtime facts and are absent from direct runs. 7. The rollout flag controls fresh runner selection and fresh runner starts. 8. A flag change does not rewrite an agent profile or a persisted run choice. 9. Persisted native data remains readable after the flag is disabled. 10. Recovery may finish an already persisted native run while fresh native starts remain blocked. ## Runtime selection The server resolves and persists the runtime once, before provider launch. | Persisted runtime | Adapter | Flag | Result | | --- | --- | --- | --- | | none | Any direct adapter | off or on | Use the existing direct path. | | none | `paperclip_runner` with any qualified provider | off | Reject the fresh start with a stable rollout-disabled error. | | none | `paperclip_runner` with a qualified provider | on | Use PRP v1 and the provider's persisted runnerd backend. | | none | `paperclip_runner` with an incomplete or unqualified profile | on | Reject the profile before runnerd starts. | | direct | Any | changed later | Keep the persisted direct path. | | native | Any | changed later | Keep the persisted native path for read, cancel, recovery, and finalization. | The server must not fall back from a selected `paperclip_runner` start to `codex_local`. A configuration or rollout error must be visible. Silent fallback would hide the runtime that executed the task. ## Remote Codex version window Remote native Codex runs accept stable CLI versions **`>=0.149.0 <0.161.0`**. The install pin is `0.160.0`. A sandbox image can therefore use an older compatible Codex without failing startup or installing another copy. The minimum is fixed at **`0.149.0`** until maintainers deliberately change it. It is not a rolling one-month support window. The official [`@openai/codex` publication history](https://registry.npmjs.org/@openai/codex) records `0.149.0` on 2026-08-20, `0.153.4` on 2026-09-04, `0.156.0` on 2026-09-22, and `0.160.0` on 2026-10-01. See also the [Codex changelog](https://learn.chatgpt.com/docs/changelog). This is an explicit compatibility window, not a claim that every Codex minor release follows a backward-compatible protocol. - Stable patch releases within the window are accepted. Alpha, dev, custom builds, malformed output, older releases, and the next minor release are rejected. - A compatible difference from the install pin produces one diagnostic per version during backend preparation, rather than stopping the task. - The same check applies to discovered, explicitly staged, and npm-installed remote Codex executables, including the final executable after linking. - For an incompatible image, the existing `PAPERCLIP_RUNNER_REMOTE_CODEX_NPM_SPEC=@openai/codex@0.160.0` configuration allows installation of the pinned runtime. Without that configuration, startup reports the supported range and the remediation. - Runner binary contracts, required runner capabilities, artifact digests, provider-pack manifests, and runtime permission checks are unchanged. This window does not relax the separate ACP provider-pack qualification. When qualifying newer Codex releases, review the upper bound in `server/src/services/native-runtime/codex-runtime-compatibility.ts`. Updating the install pin or upper bound does not raise the minimum. Raising `0.149.0` requires a separate, explicit maintainer decision and compatibility evidence. Do not derive eligibility from the current date or fetch release metadata during startup: an idle installation must not become incompatible merely because time passed. Qualification on 2026-09-22 used the actual macOS ARM64 `0.149.0` and `0.156.0` app-server binaries with a local deterministic Responses API fixture. Both passed initialization, thread creation with a dynamic tool, a tool-call/result round trip, turn completion, and thread resume. This verifies the tested protocol path; it is not a live model, Linux sandbox, or exhaustive feature test. The 2026-10-02 refresh moved the install pin and the upper bound to `0.160.0` after verifying the integrity-checked Linux x64 `0.160.0` executable digest and the installed ACP package contract. Codex `0.157.0` through `0.160.0` add the GPT-6.1 Sol catalog entry and app-server pagination without a documented app-server protocol break; the Linux CI qualification workflow covers the real startup and session-resume probes for the new pin. Version-boundary and remote artifact-preparation regressions are covered by `codex-runtime-compatibility.test.ts` and `native-session-executor.test.ts`. ## Direct adapter boundary This rule applies to every built-in and plugin direct adapter. It includes: - `codex_local`; - `claude_local`; - `opencode_local`; - other local CLI or session adapters; - process and HTTP adapters; - gateway adapters; and - external adapter plugins. Adding Paperclip Runner must not add runner imports or runner branches inside a direct adapter implementation. The heartbeat coordinator may select the explicit runner adapter at one narrow seam. All other adapters continue through their existing code. For a flag-off `codex_local` heartbeat, compatibility proof must show: - one direct invocation; - the same normalized result and finalization bytes as the approved baseline; - zero runner processes; - zero PRP connections; and - zero native rows. ## Configuration behavior When the rollout flag is off: - creation UI does not offer `paperclip_runner`; - edit UI does not offer switching to `paperclip_runner`; - server creation and import reject a new `paperclip_runner` selection; - the server rejects a fresh start for an existing runner-configured agent; - read and export preserve an existing runner configuration; - unrelated edits to an existing runner-configured agent do not erase its configuration; and - switching that agent to a direct adapter remains allowed. When the rollout flag is on: - creation, import, and edit accept `paperclip_runner` only with a qualified Codex, OpenCode, Claude Managed, AWS AgentCore, or Claude/Codex ACPX profile; - switching from a direct adapter affects only future unresolved runs; and - switching away from the runner affects only future unresolved runs. Server validation is the authority in both states. Import files and API clients cannot bypass the flag or provider allowlist. ## Persisted native runs The following data remains readable independent of the current flag: - the persisted runtime selection and reason; - run, runner, session, turn, and provider identity; - ordered runner and provider events; - accepted result and evidence assessment; - finalization coordinator and status decision; - usage, cost, cancellation, and terminal details; and - durable interactions and final task-thread reply. If the flag is disabled during an in-flight native run, the server may reconnect, cancel, reconcile, and finalize that same run. It must not use that recovery as authority to start a new native run. Recovery must remain idempotent. Repeating it cannot add a second final reply, interaction, wake, status decision, or application effect. ## Task-page compatibility The task page uses one provider-neutral thread projection. Runtime facts may add runner event groups, semantic receipts, usage, and structured questions. They must not replace classic direct-adapter content. Direct-adapter coverage must include: - an active run; - a settled run; - an empty transcript; - a pending interaction; - a resolved interaction; and - the classic interface state. The existing composer remains usable for direct adapters. A direct run does not show reconnect, runner cancel, semantic receipt, or other runner-only controls. Final replies continue to use the existing issue-comment behavior. ## Structured input compatibility New structured questions use `paperclip.question_set.v1` and the matching response contract. Provider-specific question objects remain inside their drivers. Legacy unstructured interaction records remain readable and resolvable. A structured form fails closed when its required schema, question mode, question ID, option ID, or response value is invalid. The implementation must not silently convert malformed structured input to a legacy text prompt. ## Protocol version compatibility PRP wire versions, fixture versions, event schema versions, and typed schema discriminators are independently versioned. - Peers negotiate the highest common PRP version. - No common required version fails closed before command or provider execution. - An unknown required fixture or event schema version fails closed. - An unknown required typed discriminator fails closed. - Additive optional properties remain compatible only when an old v1 consumer can ignore them without changing behavior. - Unknown object properties must survive validation when the owning schema permits additive fields. Breaking meaning requires a new required version. A provider error is not a protocol negotiation result. ## Semantic action compatibility Catalog generation and production authorization are separate steps. - A catalog entry does not authorize production use. - An operation without a production binding is undiscoverable. - An operation denied to the run is undiscoverable unless the protocol explicitly defines a safe denied receipt for that discovery mode. - A duplicate call with the same idempotency key and canonical input returns the original safe receipt. - Reusing the key with different input returns a conflict and performs no second effect. - Receipts redact credentials, private provider payloads, and hidden identity. ## Required compatibility matrix Each runner-related pull request updates only rows that it can execute. The complete first-wave matrix must cover: | Area | Required cases | | --- | --- | | Runtime selection | Every built-in direct adapter, explicit runner selection, unsupported provider, flag on, and flag off. | | Direct regression | Flag-off `codex_local` invocation count, byte-stable result/finalization, and zero native rows. | | Configuration | Enabled and disabled create, import, edit, read, export, and adapter switch. | | Recovery | Persisted native run after flag disable, reconnect, duplicate event, duplicate command, cancellation, and server restart. | | Protocol security | Cross-company binding, ticket replay, ticket expiry, malformed frame, unsupported version, and revoked lease. | | Semantic actions | Discovery denial, unbound action, duplicate call, conflicting retry, redaction, and governed action. | | Task page | Active, settled, empty transcript, interaction, and classic direct-adapter states. | | Structured input | Valid response, malformed response, stale response, duplicate response, provider loss, and cancellation. | ## Pull request acceptance Every implementation pull request must: - build and work against its declared base; - keep its changed-file count below 100; - update manifests and exports only for implemented surfaces; - run the smallest relevant tests before the repository handoff gate; - run typecheck, tests, and build for handoff; - run protocol parity, Cargo, clean-consumer, migration, token, and browser gates when those areas change; - contain no unexplained failed, cancelled, or path-skipped verification; and - resolve actionable review and security findings before it is ready. Generated files land with their source and a drift check. `pnpm-lock.yaml` is owned by CI and is not part of these pull requests.