## Thinking Path
> - Paperclip manages AI agents and their work.
> - The runner executes agent turns on local and remote providers.
> - A remote per-turn session must save its state before Paperclip
releases its sandbox.
> - The session runtime returned after 100 milliseconds while the remote
checkpoint still ran.
> - The next turn also checked the local state path instead of the
verified remote backup.
> - This pull request waits for the bounded remote close and accepts
only a verified suspended backup.
> - The benefit is reliable multi-turn execution without weaker identity
checks.
## Linked Issues or Issue Description
**What happened?**
A successful remote agent turn released its sandbox before the runner
saved the verified continuation backup. The next turn failed with
`runner_state_identity_mismatch`.
**Expected behavior**
Paperclip must finish the bounded remote checkpoint before it releases
the sandbox. A later turn must validate and restore the digest-matched
suspended backup.
**Steps to reproduce**
1. Run a native ACPX Claude Plan test in a non-reusable Daytona sandbox.
2. Reject the first plan to start a second turn.
3. Observe that the second turn fails before provider execution.
**Paperclip version or commit**
The failure reproduced at `13775a90b078ff64872f50961ea1b83d575e7bc6`.
**Deployment mode**
GitHub Actions with a Daytona sandbox.
## What Changed
- Wait for the internally bounded remote runner close and checkpoint
before the host returns.
- Preserve the existing short cleanup bound for other providers.
- Validate remote continuation lifecycle from a complete digest-verified
backup when local runner state is absent.
- Keep corrupt, non-suspended, mismatched, and unverified state
fail-closed.
- Make native Plan completion and accepted-Plan wake prompts
deterministic.
## Verification
- A prior 45-cell local campaign passed 44 cells. The only failure was
the OpenCode Plan prompt variance fixed here.
- A focused OpenCode local Plan rerun passed.
- ACPX Claude Daytona message and question cells passed.
- Focused regressions cover delayed checkpoint close and verified remote
backup lifecycle.
- GitHub Build and the focused ACPX Claude Daytona Plan cell will
validate this exact head.
## Risks
Remote runnerd sessions now wait for their internally bounded
close/checkpoint path before returning; generic provider cleanup retains
the existing 100 millisecond bound. Durable run success still cannot be
reversed. The environment release guard still blocks sandbox destruction
when no verified backup stamp exists.
## Model Used
OpenAI Codex, GPT-5.6, extended reasoning, with code execution and
GitHub Actions inspection.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal task
id
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open findings
- [ ] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip Runner owns durable provider sessions and streams their
work to the control plane.
> - Pull request #12845 added native restart recovery for live and dead
local runners.
> - A real browser test found three live-adoption gaps after that pull
request merged.
> - Lazy runner process ownership was not always stored before restart.
> - The old controller did not release its PRP authority without closing
the provider turn.
> - Reconnect events could arrive before the active provider turn was
restored.
> - This pull request closes those gaps and proves the same turn
completes after a UI hot restart.
## Linked Issues or Issue Description
Refs #12845
Related search results: #12646 covers indeterminate command results
after a runner restart. It does not cover controller adoption or
active-turn rebinding. No open duplicate pull request was found.
## What Changed
- Store lazy runnerd process ownership after provider session creation,
read, and resume.
- Detach native PRP controller authority during coordinated hot
shutdown. Keep the live provider turn running.
- Restore the exact checkpointed provider session when bounded PRP
identity events have been compacted.
- Restore the active provider turn before reconnect events are replayed.
This prevents `turn_binding_mismatch`.
- Keep exact live ownership by the current controller out of generic
orphan recovery.
- Add driver, transport, and server regression tests for these paths.
## Verification
- Ran 12 Codex driver lifecycle tests.
- Ran 53 runnerd transport tests.
- Ran 143 recovery and orphan-reaper server tests.
- Ran all 8 real-process restart recovery scenarios.
- Ran all 96 existing runner E2E unit tests.
- Ran runner TypeScript typecheck.
- Ran server TypeScript typecheck.
- Ran the migration replay test and migration safety checks.
- Tested the board UI on an isolated local instance. A real local
Codex-backed turn entered a 120-second terminal wait. The UI `Restart
now` action replaced the server and kept the same runner PID, process
start time, run ID, native session ID, runner ID, provider session ID,
and active turn. The original turn then completed.
- Confirmed one heartbeat run, no retry row, one result, one
proposed-result event, one terminal event, no protocol errors, no active
recovery state, and no surviving runner or provider process.
## Risks
- A live runner can continue provider work while no server owns the
control route. Recovery fails closed when the process fingerprint or
durable identity is ambiguous.
- Provider identity can be restored from the database only for an exact
verified adoption claim. An authenticated live `session.snapshot`
validates that identity before the driver can resume.
- The new detach path applies only to native sessions that expose
restart detachment. Other adapters keep their existing shutdown
behavior.
- This follow-up does not change the database migration or
`package.json`. The migration in #12845 remains replay-safe through `ADD
COLUMN IF NOT EXISTS` and its embedded-Postgres idempotence test. The
dedicated real-process command remains in `doc/DEVELOPING.md`.
## Model Used
- OpenAI Codex based on GPT-5. The exact serving build and
context-window size are not exposed. The run used extended reasoning,
repository tools, shell execution, and in-app browser automation.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip Runner keeps durable run and provider state outside
one server process.
> - A server restart can leave that runner alive or can interrupt it
after a provider checkpoint.
> - The old startup path used handoff intent and PID evidence, but it
did not reconstruct native ownership.
> - That gap could block the issue, create a replacement run, or start
duplicate provider work.
> - This pull request adds durable same-run recovery for coordinated and
uncoordinated restarts.
> - The benefit is exact recovery of the run, runner, session, provider,
steering, and finalization state.
## Linked Issues or Issue Description
Refs #9628. That pull request added earlier local-adapter hot-restart
work. This change adds native PRP authority reconstruction and same-run
provider resume.
Refs #10935. That pull request handles missing hot-restart snapshots.
This change also supports hard restarts with no snapshot.
Refs #11624. That pull request prevents unsafe retry after an adopted
legacy process exits. This change reconciles native terminal evidence
before provider recovery.
Refs #12070. That pull request improves process liveness checks. This
change also binds recovery to a process-start fingerprint and fails
closed on ambiguity.
**What happened?**
The server could record hot-restart intent, but startup did not rebuild
native runner ownership. A live runner could not re-register its PRP
authority. A dead runner could not resume the exact native and provider
session on the same heartbeat run. Generic recovery could then block the
issue or create replacement work.
**Expected behavior**
A live native runner must reconnect with the same PID and logical
identities. A dead runner must resume the same durable session and
heartbeat run with only a new operating-system PID. A proposed or
terminal result must finalize once before any provider turn starts.
Ambiguous process or session evidence must stay blocked without a signal
or duplicate spawn.
**Steps to reproduce**
1. Start a Paperclip Runner heartbeat and wait for an active provider
turn.
2. Restart only the Paperclip server, with or without a hot-restart
marker.
3. Observe that the old startup path does not reconstruct the native
control-plane authority.
4. Kill both the server and runner after a provider checkpoint.
5. Observe that the old path cannot resume the exact native session on
the original heartbeat run.
**Paperclip version or commit**
The defect was reproduced from commit
`1991f31fd53e7f7794d5c2e4b93be384ade2b41d`. This branch is rebased onto
the current `master`.
**Deployment mode**
Local development and self-hosted server deployments that use the local
Paperclip Runner.
## What Changed
- Added correlated hot-restart requests and version-compatible native
handoff fields.
- Added controller boot identity, process-start identity, controller
generation, recovery state, request id, and bounded history to the
native finalization ledger.
- Added transactional recovery claims for live-runner reattach,
dead-runner resume, and incomplete bootstrap.
- Added fail-closed ownership takeover rules and process identity
validation.
- Added live runner adoption to the local runner transport without a
duplicate spawn.
- Added same-run provider checkpoint resume and legacy retry-row
compatibility.
- Reconciled proposed and terminal results before runner or provider
recovery.
- Bound the HTTP and PRP listener before startup recovery and delayed
scheduling and generic reapers until classification completes.
- Added restart-aware health diagnostics, run-log recovery transitions,
durable runner diagnostics, and bounded shutdown finalizer draining.
- Moved restart-survivable diagnostics into runner-owned, pre-redacted
bounded writes; raw stdout and stderr are never persisted.
- Added process-start fencing for controller, runner, and provider PIDs;
startup classifies every candidate without an implicit cap.
- Added crash-recoverable, contention-safe development restart-request
coordination and failed-startup listener cleanup.
- Added a credential-free real-process restart suite for eight restart,
scale, and identity scenarios.
- Documented native restart operation, persistence, diagnostics, and
verification.
## Verification
- The documented native restart commands passed. They ran eight
real-process/database recovery scenarios and the live runner adoption
transport test.
- Native executor tests passed: 111 tests.
- Heartbeat recovery tests passed: 124 tests.
- Hot restart, health, and shutdown tests passed: 52 tests.
- The broader affected server suite passed: 350 tests.
- Focused native recovery and startup tests passed: 49 tests.
- Runner transport and control-plane tests passed: 63 tests.
- Runner-owned diagnostic tests passed for write-time bounding,
credential redaction, private file modes, and raw stream
non-persistence.
- Development restart coordination tests passed: 11 tests.
- Database migration checks and the partial-application/replay
regression test passed.
- Server, database, and Paperclip Runner typechecks passed.
- `git diff --check` passed.
- Full Paperclip PR CI passed, including build, canary, all five general
server shards, all five serialized server shards, all three browser E2E
shards, workspace suites, and release-registry verification.
- Greptile completed at 5/5 with no outstanding findings,
recommendations, follow-ups, or open review threads.
## Risks
- Moderate risk. This changes startup ordering and ownership transfer
for active native runs.
- The migration adds nullable columns and does not rewrite existing
rows.
- Recovery fails closed when process or durable session identity is
incomplete or contradictory.
- The first implementation supports the local Paperclip Runner. Remote
targets keep their existing behavior.
- The real-process suite covers cleanup and asserts that no runner or
provider process survives each test.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5. The runtime did not expose a more specific
model revision or context-window size. Repository editing, shell
execution, database tests, and real-process test execution were enabled.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip manages AI agents that perform work.
> - Paperclip Runner connects durable task runs to local provider
processes.
> - The full-stack paid matrix exposed failures after the runner
integrity repair.
> - Verified JavaScript entrypoints lost their relative module graph
when Linux executed them through descriptor paths.
> - Returned provider startup errors also remained pending and became
indeterminate after recovery.
> - Sparse Codex tool lifecycle events lost the `write_document`
identity before task transcript projection.
> - This pull request repairs those three boundaries and makes the
structured-question fixture deterministic.
> - The benefit is repeatable provider startup, exact failure replay,
and correct inline Plan placement.
## Linked Issues or Issue Description
Refs #12721 and #12700.
**What happened?**
The paid runner matrix failed ACPX and OpenCode startup before provider
session creation. The runner journal then replaced the original startup
error with an indeterminate recovery result. Native Codex saved a Plan
but rendered it only as a fallback card. A legacy Claude waiting reply
could also echo the reserved terminal marker before the answer arrived.
**Expected behavior**
Verified JavaScript providers must start from immutable
descriptor-backed artifacts. Returned startup failures must persist as
terminal failed command results. Native tool lifecycle updates must
preserve the `write_document` boundary. Pre-answer fixture output must
not contain the reserved terminal marker.
**Steps to reproduce**
1. Run the local provider cells in the Runner Full-Stack E2E workflow.
2. Observe ACPX and OpenCode fail during `session.open` before provider
execution.
3. Observe recovery report `execution_indeterminate` instead of the
original startup error.
4. Run the native Codex Plan cell and observe the fallback Plan card
after the tool activity row.
5. Run the legacy Claude structured-question resume cell and observe an
early marker echo in waiting prose.
**Paperclip version or commit**
`0f9452101740835ce0b1488a204bf48acd5bafc3`
**Deployment mode**
Local development with the paid GitHub Actions acceptance workflow.
## What Changed
- Bundle the ACPX sidecar and OpenCode proxy as self-contained Node ESM
entrypoints before hashing and verified descriptor launch.
- Anchor ACPX dynamic provider package resolution at a
controller-derived provider-pack root and keep that root out of the
provider child environment.
- Persist executor-returned startup errors as redacted durable failed
command results while retaining indeterminate recovery for true process
death.
- Coalesce sparse native tool items by stable ID so a late
`write_document` name, input, and result reach the transcript boundary
once.
- Forbid the structured-question fixture from spelling or announcing its
reserved terminal marker before the user answers.
## Verification
- Rust and TypeScript regression tests cover durable failed replay, true
crash ambiguity, bundle closure, package-root derivation, environment
filtering, exact Codex tool lifecycle coalescing, and prompt
determinism.
- Local execution is intentionally limited to formatters and static diff
checks. GitHub Actions will run tests, type checks, builds, and security
checks.
- After ordinary CI is green, scoped paid cells will validate one ACPX
launch, one OpenCode launch, native Codex Plan projection, and legacy
Claude structured resume before a complete matrix rerun.
- Prior failing matrix:
https://github.com/paperclipai/paperclip/actions/runs/33682434315
## Risks
- Bundling changes the bytes covered by provider launch hashes.
Provider-pack generation already hashes the final built files.
- ACPX still loads qualified provider packages dynamically. The
controller supplies a normalized package root, while existing version,
digest, path, and descriptor checks remain active.
- Durable `failed` is terminal. Replays return the same redacted result
and do not execute the provider effect twice.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex based on GPT-5 with agentic reasoning, repository
inspection, code editing, Git, parallel subagents, and GitHub Actions
coordination. The exact deployed snapshot and context-window size are
not exposed to this task.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked related public work or described the bug in
this PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
ticket id
- [ ] I have run tests locally and they pass (intentionally deferred to
GitHub Actions)
- [x] I have added or updated tests where applicable
- [x] No documentation change is required for this runtime repair
- [x] I have considered and documented the risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the control plane for agents that perform work.
> - Paperclip Runner connects durable provider sessions to individual
task runs through PRP.
> - Provider continuity and per-run authority are different lifetimes.
> - The existing implementation mixed those lifetimes and lost event
metadata between provider frames, runnerd, persistence, API
sanitization, and the task thread.
> - That caused failed continuation, missing progress and Plans,
duplicate replies, hidden failures, and unsafe recovery.
> - This repair gives every heartbeat fresh authority, preserves
qualified provider-session continuity, and restores one lossless
presentation path without changing direct adapters.
## Linked Issues or Issue Description
**What happened?**
A second native heartbeat could reuse tickets, leases, command receipts,
sequence state, and run identity from the first heartbeat. Provider
phase and item identity could be lost before the UI read them. Redaction
could corrupt protocol discriminators while still missing malformed
credential tails. The task thread could fold progress into the final
response, hide failures, or show more than one final answer. Native
Codex also exposed approval modes that do not yet have a durable
approval bridge.
**Expected behavior**
Each heartbeat uses a new PRP authority epoch. Codex and OpenCode
preserve exact qualified provider sessions; ACPX emits an explicit
continuity event when its qualified process-replacement policy is used.
Every accepted provider event is presented, classified as internal, or
surfaced as unsupported. The task page shows chronological progress,
reasoning summaries, activity, Plans, interactions, terminal failures,
and exactly one final reply. Direct adapters retain their existing path.
**Steps to reproduce**
1. Enable the unified experimental Paperclip Runner setting.
2. Create a local native Codex, OpenCode, ACPX Claude, or ACPX Codex
agent.
3. Run response, Plan, structured-question/resume, restart,
cancellation, and failure scenarios.
4. Reload the task while active, waiting, failed, and settled.
5. On the old implementation, observe stale run authority, missing
classifications, incomplete output, or duplicated/folded replies.
**Paperclip version or commit**
The repair is based directly on `master` at
`87d05e194b643810d16d20612115acd01d735d43`.
**Deployment mode**
Local development with the embedded database.
Related work: Refs #12616, #12646, #12666, #12685, and #12700.
## What Changed
- Rotates PRP control-plane, outbox, ticket, lease, command, receipt,
and sequence authority for each heartbeat while carrying forward only a
validated provider-session identity.
- Reads `control-plane-state.json`, validates both durable schemas and
lifecycle values, resumes coherent current runs, archives qualified
settled authority, and quarantines malformed or mismatched scoped state
without moving ambiguous live legacy state.
- Preserves Codex provider phase and stable item identities so
commentary remains progress and only `final_answer` becomes final.
- Adds raw OpenCode HTTP/SSE boundary coverage and canonical reasoning
lifecycle mapping.
- Makes ACPX normalization lossless for visible reasoning, tool
lifecycle metadata, stable bounded identities, Plan revisions,
structured requests, failures, and qualified process replacement. Only
the compatible terminal assistant message is promoted as final.
- Applies schema-aware redaction before generic JWT-shaped detection and
scans every diagnostic string leaf. Malformed raw/escaped quoted
credential tails are redacted in both server and durable Rust state.
- Restores snapshot-style chronological task presentation, expandable
tool activity, inline Plan cards, visible waiting/resume/cancel/failure
states, and exactly one final answer.
- Makes `never` the only qualified native Codex permission mode and
rejects unsupported persisted native modes with remediation. OpenCode
and ACPX policies remain intact.
- Keeps the unified experimental Runner setting as the only enablement
flag. Onboarding and direct Codex, Claude, and OpenCode stay on their
legacy execution/finalization paths.
- Adds cross-language goldens, authority/recovery/fault coverage, exact
response/count assertions, and native plus legacy acceptance scenarios.
## Verification
- Pull-request GitHub Actions run Rust formatting/tests, TypeScript
checks, server/UI tests, builds, protocol drift checks, browser E2E, and
security scans.
- A separate workflow-only validation ref is pinned directly on this PR
head and runs the 35-cell paid local matrix: three core scenarios plus
structured-question resume and restart/resume for native Codex, native
OpenCode, ACPX Claude, ACPX Codex, and direct Codex/Claude/OpenCode.
Run: https://github.com/paperclipai/paperclip/actions/runs/33682434315
- Acceptance requires exact single visible replies, monotonic sequences,
matching envelope discriminators, one semantic terminal, one run
terminal, no unresolved interaction, no duplicate mutation, no secret
leakage, provider continuity, and zero native rows for direct adapters.
- Per maintainer direction, tests are running in GitHub Actions rather
than on the slower local host. Only formatters and static diff checks
were run locally.
## Risks
- Recovery from old or partial filesystem state is sensitive. The repair
fails closed, preserves active or unverifiable authority, and
quarantines only state whose scoped ownership is safe to move.
- Provider event formats can change. Closed validators and boundary
goldens turn new or malformed events into visible diagnostics instead of
silent drops.
- Shared task presentation could affect direct adapters. Runtime-fact
gating plus the direct-adapter matrix protect the existing path.
- Managed and remote providers are not qualified here. Shared code
continues to compile and fail safely, but live qualification is
deferred.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex based on GPT-5. The exact deployed snapshot and
context-window size are not exposed to this task. It used agentic
reasoning, repository inspection, code editing, Git, parallel subagents,
and GitHub Actions.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass (intentionally deferred to
GitHub Actions)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented risks above
- [ ] All Paperclip CI gates are green
- [ ] The paid local-provider matrix is green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner subsystem executes agent work across local and managed
provider backends.
> - The lower pull requests restore the task runtime, provider backends,
and managed-provider control plane.
> - The restored system needs repeatable full-stack checks before it can
ship safely.
> - Paid live checks also need clear access, cost, and secret controls.
> - This pull request adds acceptance, live evaluation, chaos, and
release gates for the restored runner stack.
> - The benefit is measurable runner parity with safer release
decisions.
## Linked Issues or Issue Description
**Subsystem affected**
Cross-cutting. This change covers runner tests, release workflows,
server contracts, and evaluation tools.
**Problem or motivation**
The runner stack did not have one complete acceptance surface for native
Codex, ACPX, Claude Managed, and AWS AgentCore. Release checks could
miss provider drift, task-view regressions, cost-policy errors, and
destructive cleanup errors.
**Proposed solution**
Add a 57-cell full-stack catalog, a Daytona image, and opt-in paid
workflows. Add live evaluation, chaos, cost-limit, redaction, and
release contract checks. Add AWS AgentCore infrastructure and guarded
provisioning tools. Keep the native runner experimental flag off by
default.
**Alternatives considered**
We considered manual smoke tests only. They do not give repeatable
evidence and they do not protect release branches. We also considered
one large pull request. The stacked pull requests keep each review below
the Greptile file limit.
**Roadmap alignment**
This work supports the shipped Cloud / Sandbox agents milestone and the
shipped Agent evals & feedback milestone in `ROADMAP.md`.
Related stack:
- #12699 adds managed provider backends and lifecycle support.
- #12691 adds qualified OpenCode and ACPX provider backends.
- #12685 restores task runtime rendering and steering.
## What Changed
- Add the runner full-stack harness with 57 catalog cells and 60 unit
tests.
- Add a Daytona runner image with digest-pinned base images and
base-aware image-content checks.
- Add guarded live evaluation and chaos workflows with a fixed
40-execution matrix; live and full-stack paid schedules now run only on
Sundays or by manual dispatch.
- Add in-flight reported-usage cost stops, post-turn cost caps,
exact-threshold failure classification, secret redaction, retry
classification, and actor authorization.
- Reattach stream and hard-budget listeners before restart-recovery
continuations so restored paid sessions cannot bypass in-flight
interruption.
- Preserve OpenCode usage and cost across tool-loop messages and turns
while exposing an explicit current-run delta to durable accounting.
- Keep PNG/WebM evidence in access-controlled artifacts only, reject
SVG, and publish only pruned inert structured per-attempt evidence.
- Add AWS AgentCore infrastructure, provisioning checks, and smoke
tools; reject unsafe model identifiers, require exact stack ownership
markers, and make failed-stack replacement explicit.
- Add evaluation-session contracts and capability reports.
- Add release workflow checks for immutable action pins, frozen
dependency installs, exact weekly cron shape, paid-run guards,
provider-secret isolation, and chaos test paths.
- Reauthorize the original and triggering numeric actor IDs as the first
step of every provider-secret job, including partial reruns, before
checkout or provider access.
- Give each full-stack matrix cell only its matching provider
credential, expose Daytona only to Daytona cells, and disable shared
dependency caches anywhere paid credentials or OIDC write access are
present.
- Protect the legacy manual E2E workflow with the same default-branch,
allowlist, environment, and per-job authorization boundary.
- Rotate live-eval candidates by week and retain 120 days of compatible
history so the seven-week trend window remains viable.
- Restore the root runner-acceptance commands and reconcile reported
snapshots,
raw receipts, and terminal usage without double counting or losing late
usage.
- Mark ACPX token deltas exact only when every budget field is present,
keep
cumulative cost/request authority separate, reject non-USD cost
labeling,
and include thought tokens in output-token budgets.
- Keep `enableNativeRunner` off by default. The acceptance harness
enables it only in its isolated test instance.
## Verification
Passed locally:
- `pnpm --filter @paperclipai/paperclip-runner typecheck`
- `pnpm test:runner-acceptance:typecheck`
- `pnpm test:runner-acceptance` (19 tests)
- focused OpenCode proxy, driver, runnerd transport, live-session, and
turn-stream tests (106 tests)
- `pnpm --filter @paperclipai/paperclip-runner exec vitest run
src/live/clean-room-server.test.ts` (22 tests)
- `pnpm test:e2e:runner:typecheck`
- `pnpm test:e2e:runner:unit` (62 tests)
- `node --test scripts/__tests__/release-verify-workflow.test.mjs`
- `pnpm --filter @paperclipai/paperclip-runner
test:runner-workflow-evals` (22 tests)
- `pnpm -r typecheck`
- `pnpm build`
- `node --test
packages/paperclip-runner/scripts/aws-agentcore-provisioning.test.mjs`
(6 tests)
- `git diff --check`
- `cargo test --manifest-path
packages/paperclip-runner/runner/Cargo.toml -p paperclip-runner-core
--lib --locked` (161 tests)
- focused ACPX provider-event tests (10 tests)
- The rebased PR changes 92 files. `pnpm-lock.yaml` is unchanged.
I did not run paid live provider jobs or provision AWS resources. Those
checks need credentials and can create cost.
## Risks
The paid workflows can create provider cost. They require an allowlisted
original and triggering actor, the protected `runner-e2e-paid`
environment, explicit opt-in variables, and cost limits. The four
provider credentials exist only in that master-only environment, which
requires allowlisted reviewer approval and disables administrator
bypass; repository and organization Actions scopes contain no copies.
Provider usage arrives after a billable request, so the live guard
cannot prevent one request from crossing a threshold. It interrupts
immediately on the first reported threshold hit and permits no
continuation.
Visual evidence can contain secrets rendered as pixels. PNG/WebM remain
only in access-controlled workflow artifacts; SVG and per-attempt XML
are excluded, and S3/Pages receive a pruned structured dashboard.
The AWS scripts can create cloud resources. They use explicit commands,
least-privilege roles, KMS encryption, saved nonsecret metadata, and
explicit teardown.
This pull request does not enable the experimental native runner for
existing instances.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex with GPT-5. The model used extended reasoning, tool use,
code execution, and parallel subagents.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip Runner provides durable, provider-neutral agent
execution.
> - The current stack supports qualified local providers but omits the
managed provider paths from the integration branch.
> - Claude Managed Agents and AWS AgentCore need explicit profile
qualification, durable recovery, usage accounting, and cleanup controls.
> - This pull request adds those managed backends as the third part of
the Runner parity stack.
> - The benefit is managed execution without weakening the default-off
Runner rollout gate.
## Linked Issues or Issue Description
**Subsystem affected**
Cross-cutting: Runner, server orchestration, database profiles, CLI, and
adapter configuration UI.
**Problem or motivation**
The current Runner stack cannot select or execute the managed Claude
Agents API or AWS Bedrock AgentCore Harness backends. It also lacks
qualified profile storage and recovery checks for those remote
resources.
**Proposed solution**
Add qualified managed and remote profiles, API and CLI management, exact
provider selection, durable lifecycle handling, cumulative usage
accounting, bounded cleanup, and retention acknowledgement. Keep
`enableNativeRunner` default-off.
**Alternatives considered**
A direct copy of the old integration branch was rejected because its
provider contracts, model values, credential flow, and migration history
no longer match the current base. A single large parity pull request was
also rejected because stacked review keeps each subsystem bounded.
**Roadmap alignment**
This continues the existing Runner architecture and rollout work. It
does not introduce a separate execution system.
**Additional context**
This pull request is based on the merged #12691 and #12685 stack. It
also closes the delayed security-review findings reported on #12691 by
binding qualified ACPX and OpenCode launch artifacts to the bytes
actually executed. A GitHub search for managed agent, AgentCore, and
Claude managed work found no duplicate public issue or pull request.
## What Changed
- Add Claude Managed Agents and AWS AgentCore provider executors to
runnerd.
- Add qualified managed and remote profile storage, routes, OpenAPI
contracts, CLI commands, and migration 0237.
- Validate profile ownership, enabled state, exact qualified revision,
model, agent version, and secret binding before persistence and
recovery.
- Persist durable provider session and owned skill state for
restart-safe cleanup.
- Reconcile uncertain create responses and delete remote sessions before
owned skills.
- Track cumulative provider usage and enforce positive session spend
caps.
- Recover interrupted AgentCore usage at the next turn boundary by
charging the prior invocation ceiling exactly once; keep the session
gated until an explicit monotonic budget raise.
- Isolate AgentCore AWS configuration from host profiles and
credential-process/SSO configuration while preserving workload identity.
- Require OpenCode 1.18.17 and fixed build-owned provider-pack artifact
paths; remove the ambient executable override.
- Snapshot and content-verify ACPX and OpenCode commands, scripts, and
provider executables before launch. Linux executes sealed inherited
descriptors; macOS uses authenticated private snapshots with retry-safe
rematerialization at the spawn boundary.
- Persist canonical ACPX and OpenCode launch-profile digests, reject
drift across fresh recovery, and make recovery failures sticky.
- Close and journal unsafe ACPX active-turn recovery before any provider
bootstrap or reconnect.
- Add managed provider fields to the Runner configuration UI and
permission projection.
- Preserve the default-off `enableNativeRunner` experimental flag.
## Verification
- `pnpm -r typecheck`
- `pnpm build`
- Focused managed server, database, CLI, Runner TypeScript, Rust,
Claude, AgentCore, ACPX, OpenCode, process-supervisor, and
durable-recovery tests passed.
- `cargo test -p paperclip-runner-core --lib --locked` (160 tests)
- `cargo check --workspace --all-targets --locked`
- Native Codex integration tests passed (60 tests); native provider
tests passed (7 tests); server native-runtime tests passed (87 tests).
- Verified-launch replacement, nested-spawn retry, exact-version,
profile-drift, sticky-failure, and no-bootstrap active-recovery tests
passed.
- `git diff --check`
- The PR changes 91 files. `pnpm-lock.yaml` is unchanged. The Rust
workspace lockfile adds the approved `rustix` dependency used for safe
descriptor handling while `#![forbid(unsafe_code)]` remains enabled.
## Risks
- The provider APIs can change while they are in beta. Exact
qualification and fail-closed recovery checks limit drift.
- Remote cleanup can fail after a partial create. Durable ownership
inventories and retry-safe deletion preserve recovery state.
- Migration 0237 adds profile tables. The generated migration and
snapshot pass the repository migration checks.
- Managed execution can incur provider cost. Positive default spend caps
and explicit retention acknowledgement limit accidental use.
- An interrupted AgentCore invocation without final metadata is
conservatively charged to its active session ceiling. This can overstate
cost, but cannot undercount it; later work requires an explicit budget
increase.
- Linux qualified launches use sealed memory descriptors. macOS lacks
executable-descriptor APIs, so the runner uses owner-only private
snapshots and minimizes linked-path lifetime; hostile same-UID processes
remain outside the documented local-host trust boundary.
- The global Runner feature remains default-off.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5, with tool use, code execution, and subagent review.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip Runner is the experimental native runtime for governed
agent work.
> - The runtime contracts already describe Codex, OpenCode, and ACPX
providers.
> - The merged control plane still rejected OpenCode and ACPX for new
runner agents.
> - Runnerd also selected only the Codex provider implementation.
> - This pull request activates the qualified OpenCode and ACPX paths
from the form to runnerd.
> - The benefit is one durable runner path with provider-specific
permissions and recovery.
## Linked Issues or Issue Description
Refs #12685
**Subsystem affected**
This change affects the runner package, server orchestration, adapter
configuration, and UI configuration.
**Problem or motivation**
Paperclip Runner stores provider contracts for OpenCode and ACPX. New
agents cannot select those providers. Runnerd cannot execute those
stored provider descriptors. The UI also shows only Codex.
**Proposed solution**
Accept the qualified OpenCode 1.18.17 profile and the fixed ACPX Claude
and Codex profiles. Route them through runnerd. Keep provider selection,
model selection, permissions, credentials, events, and recovery inside
closed provider-specific boundaries.
**Alternatives considered**
One option was to keep the contracts dormant. That option leaves stored
configuration and runtime behavior out of sync. Another option was to
enable every ACPX agent. That option is not safe because Pi does not yet
have the same verified launch path.
**Roadmap alignment**
This change supports the completed cloud and sandbox agent milestone. It
also supports self-healing runs and governed agent execution. It does
not add a new roadmap surface.
## What Changed
- Add one server profile resolver for Codex, OpenCode, and qualified
ACPX descriptors.
- Keep `adapterConfig` as the provider and permission authority for
fresh runs.
- Add Paperclip Runner provider, ACPX agent, and provider-specific
permission controls to the UI.
- Reset the model to a compatible qualified value when the provider
changes.
- Route Codex, OpenCode, and ACPX through the durable runnerd provider
selector.
- Add a durable ACPX executor with bounded state, recovery, events, tool
receipts, and identity checks.
- Remove Codex labels from OpenCode events, results, evidence, and
recovery diagnostics.
- Pass only provider-specific credential names to child processes.
- Keep ACPX Pi unavailable and reject it before process launch.
- Keep the existing Paperclip Runner experimental flag unchanged.
## Verification
- `pnpm exec vitest run
packages/paperclip-runner/src/backends/native-backend-factory.test.ts
packages/paperclip-runner/src/live/runnerd-codex-transport.test.ts
packages/adapters/codex-local/src/ui/build-config.test.ts
ui/src/adapters/codex-local/config-fields.test.tsx
server/src/__tests__/adapter-registry.test.ts
server/src/__tests__/adapter-routes.test.ts
server/src/__tests__/agent-adapter-validation-routes.test.ts
server/src/__tests__/company-portability.test.ts
server/src/services/native-runtime/runtime-mode.test.ts
server/src/services/native-runtime/native-session-executor.test.ts
server/src/services/heartbeat-runner-provider-config.test.ts`
- The focused TypeScript, server, and UI suites passed 274 tests.
- `cargo test -p paperclip-runner-core --test native_provider_backend`
- The executable native provider integration suite passed 4 tests.
- `cargo test -p paperclip-runner-core --lib`
- The Rust unit suite passed 91 tests.
- `pnpm -r typecheck`
- `pnpm check:token-gates`
- `pnpm build`
- `git diff --check codex/runner-parity-task-runtime...HEAD`
## Risks
- This changes provider process selection and durable recovery. The
experimental flag still gates every fresh Paperclip Runner run.
- OpenCode requires a model in `provider/model` form and stays pinned to
version 1.18.17.
- ACPX accepts only exact Claude and Codex profile versions and models.
Pi stays unavailable.
- ACPX steering stays unavailable and reports that limit through the
driver capabilities.
- Child processes receive explicit environment allowlists. They do not
inherit the full server environment.
- This pull request has no database migration.
## Model Used
OpenAI Codex, GPT-5, with tool use, code execution, and subagent review.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The runner subsystem drives a Codex provider process and reads its
events with `CodexProvider::poll`
> - The Codex provider integration tests wait for those events in poll
loops
> - Two of those loops count iterations instead of measuring time, so
they stop waiting too early
> - This makes `cargo test` fail at random on branches that change no
Rust code
> - This pull request bounds the two loops by wall clock, like every
other wait in the same file
> - The benefit is that a red CI job now means a real defect
## Linked Issues or Issue Description
**What happened?**
`packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs`
fails
`cargo test` at random. The failure appears in the `ci / Build` job with
exit code 101.
It appears on branches that change no Rust code. Two tests fail:
-
`ambiguous_or_dead_replacement_start_preserves_result_not_exit_authority`
at line 1274
- `ambiguous_replacement_turn_adopts_one_later_completion_identity` at
line 1443
Both assertions report `left: None`. The value is not wrong. The loop
never saw the
`CodexProviderEvent::Exited` event at all.
**Expected behavior**
The tests must wait for the provider process to exit. A test must fail
only when the
provider gives a wrong result.
**Steps to reproduce**
1. Build the integration test: `cargo test --test codex_provider
--no-run`.
2. Run one of the two named tests 25 times in a row.
3. About 8 of the 25 runs fail with `left: None`.
**Paperclip version or commit**
Reproduced on `master` at `2e5a24e17`.
**Related pull requests**
Refs #12241. That pull request also edits
`packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs`.
It does not
fix these two loops. The two changes may need a merge if both land.
**Root cause**
`CodexProvider::poll` (`crates/runner-core/src/codex_provider.rs:824`)
reads with a 1 ms
timeout. That timeout does not apply on every path.
`ProcessSupervisor::receive_stdout_line`
(`crates/runner-core/src/process_supervisor.rs:293`)
returns at once, and uses none of the 1 ms budget, in two cases:
`StdoutClosed` at line 309
and `RecvTimeoutError::Disconnected` at line 315.
A child process closes its pipes before its exit status is ready to
reap. In that window
every `poll()` call returns `Ok(None)` in nanoseconds. A loop of 64 or
128 iterations then
ends in microseconds, before the exit status is available. The failing
run above ends in
0.06 s.
## What Changed
- `tests/codex_provider.rs`: bound the exit wait at line 1256 by a 5
second deadline instead
of 64 iterations.
- `tests/codex_provider.rs`: bound the exit wait at line 1397 by a 5
second deadline instead
of 128 iterations.
- Both loops now sleep 1 ms when `poll()` returns no event. This copies
the pattern that the
same file already uses at line 1511 and in every `wait_for_*` helper.
- No production code changes. The change is test-only.
## Verification
Measured before and after the change. Each test ran 25 times in
sequence, on an idle
machine, with `--test-threads=1`.
| test | before | after |
|---|---|---|
|
`ambiguous_or_dead_replacement_start_preserves_result_not_exit_authority`
| 8 / 25 failed | 0 / 25 failed |
| `ambiguous_replacement_turn_adopts_one_later_completion_identity` | 9
/ 25 failed | 0 / 25 failed |
The full `codex_provider` suite also ran 12 times with
`--test-threads=4` after the change.
Every run passed.
Commands:
```
cargo test --test codex_provider --no-run
cargo test --test codex_provider
```
## Risks
Low risk. The change touches test code only. It makes two waits longer
in the failure case:
a genuinely broken provider now takes up to 5 seconds to fail these two
tests instead of
microseconds. Every other wait in this file already uses the same 5
second deadline.
## Model Used
Claude Opus 5 (`claude-opus-5`), extended thinking, with tool use and
code execution.
Depends-on: none — this is a self-contained test-only change with no
prerequisite pull request.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes — none
apply. This change is test-only and alters no public interface, so no
docs page and no end-to-end test change is needed.
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: zannis <1011451+zannis@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip Runner gives native runs a durable and governed execution
path.
> - The lower stack PR adds authenticated remote execution targets and
provider ingress.
> - The Rust daemon currently accepts only loopback plaintext WebSocket
connections.
> - Remote Codex needs authenticated WSS dialing and provider-ingress
listener mode.
> - This pull request adds the bounded Rust transport contract.
> - The benefit is a secure transport layer for the Codex remote
vertical slice.
## Linked Issues or Issue Description
Refs #12638.
Refs #12616.
Refs #12352.
**Subsystem affected**
Paperclip Runner Rust transport and remote runner networking.
**Problem or motivation**
The runner daemon cannot connect to a public control plane with TLS. It
also cannot accept a provider preview connection on the run-bound
ingress path.
**Proposed solution**
Add WSS with native trust roots and an optional private CA bundle. Add a
fixed authenticated listener mode for provider ingress. Advertise the
exact transport contract through build metadata.
**Alternatives considered**
Plaintext public WebSocket connections would weaken the transport
boundary. A general listener would expose more network surface than the
run-bound provider ingress requires.
**Roadmap alignment**
This work supports the Cloud and Sandbox agents milestone. It also
supports self-healing native runs.
## Stack
- Lower merged PR: #12638.
- This PR contains only its 13-file delta against `master`.
- Later stack PRs add the task workspace and administrator UI.
## What Changed
- Added WSS dialing with rustls and native certificate roots.
- Added an optional bounded private CA bundle that augments native
roots.
- Kept plaintext WebSocket dialing restricted to loopback addresses.
- Pinned resolved dial addresses for the process lifetime.
- Added a fixed `0.0.0.0:43127` listener with an exact run-bound path.
- Rejected listener queries, ambiguous paths, and WebSocket extensions.
- Kept frame and message size bounds.
- Added bounded reconnect grace and exponential jitter.
- Retried bootstrap failures only before authentication proof
transmission begins.
- Kept post-proof failures fail-closed and bounded the welcome exchange
at two seconds.
- Added runnerd build metadata for the versioned transport contract.
- Updated Rust dependencies and `Cargo.lock` only for TLS and
certificate handling.
- Did not add provider dispatch, Pi, AWS, `pnpm-lock.yaml`, migrations,
or workflows.
## Verification
- GitHub Actions will run Cargo formatting, Rust tests, repository
tests, typecheck, build, security, and policy gates.
- Rust tests cover URL validation, listener path validation, build
metadata, durable recovery, and the existing Codex provider path.
- Local tests were not run. The requested verification policy uses
GitHub Actions for this series.
- `git diff --check master...HEAD` passes.
- The delta contains 13 files.
## Risks
- TLS and listener changes affect the runner trust boundary.
- Public plaintext transport remains rejected.
- The listener uses one fixed port and one exact run-bound path.
- PRP authentication remains required after the WebSocket upgrade.
- The optional CA file uses the existing private-file checks and a 4 MiB
limit.
- This PR does not enable another provider or change direct adapters.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex with GPT-5.6. The work used high-reasoning agent mode,
repository tools, GitHub tools, and parallel code-audit agents.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with Fixes: / Closes /
Refs OR (b) described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner package already provides the production protocol and
execution spine.
> - Contributors still need stable SDK surfaces, deterministic test
tools, and local inspection tools.
> - Those surfaces share generated contracts and must change as one
package boundary.
> - This pull request adds the package-local SDK, labs, examples, and
drift checks.
> - The benefit is a reviewable developer platform that does not change
application execution selection.
## Linked Issues or Issue Description
**Subsystem affected**
`packages/paperclip-runner` — runner SDK, conformance tools, and
developer tooling.
**Problem or motivation**
The production runner spine is present, but package consumers cannot
build deterministic integrations, inspect sessions, or verify
provider-neutral behavior through supported surfaces.
**Proposed solution**
Add browser, React, standalone, live-session, scenario, conformance, and
evaluation surfaces. Add generated contract inventories and
package-local verification scripts. Keep production application routing
unchanged.
**Alternatives considered**
We considered splitting each generated catalog, SDK surface, and demo
into separate pull requests. Those changes share exports, fixtures, and
drift gates. Splitting them would create intermediate package states
that do not build.
**Roadmap alignment**
No overlapping item appears in `ROADMAP.md`. This work extends the
runner package that is already on `master`.
## What Changed
- Add browser, React, standalone, live-session, and issue-thread SDK
surfaces.
- Add deterministic mock control-plane, scenario, conformance, replay,
and evaluation tools.
- Add bounded Codex, OpenCode, and ACPX development transports and
fixtures.
- Keep deferred managed-provider execution fail-closed. Persisted
compatibility data remains readable.
- Add generated capability inventories with their source files and drift
checks.
- Add examples, package documentation, browser checks, and
clean-consumer checks.
- Preserve the reviewed protocol bounds, replay compatibility aliases,
process environment isolation, and semantic redaction limits.
- Update the ACPX package patch that the existing workspace patch
registry already tracks.
- Do not change `pnpm-lock.yaml`, repository workflows, server runtime
selection, or the application UI.
## Verification
GitHub Actions is the verification authority for this pull request. The
repository CI, package TypeScript and Rust checks, package tests,
generated-output drift checks, browser checks, security scans, and
Greptile review must pass on the exact head.
Local test suites were not run because this series uses parallel GitHub
Actions for verification.
## Risks
This is a large greenfield package change. The main risks are public
export drift, generated-output drift, and optional React consumer
compatibility. Package boundary checks, clean-consumer checks, and
browser tests cover those risks. Production adapter selection and server
execution are outside this pull request.
## Stack
1. **This PR:** runner SDK and developer tooling.
2. [Codex production server
integration](https://github.com/paperclipai/paperclip/pull/12616).
3. [Provider-neutral task-thread
UI](https://github.com/paperclipai/paperclip/pull/12617).
## Model Used
OpenAI Codex, GPT-5, high-reasoning mode, with tool use and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in-PR following the feature request
template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip Runner already has a hardened ACPX path for Codex.
> - Claude can reuse that protocol only with an exact package/model
profile and provider-lifetime fencing.
> - Pi needs a separately spawned runtime whose executable does not yet
have the descriptor-confined verified launch used by the ACP server.
> - This pull request therefore activates Claude only and keeps Pi
unavailable before installation or process launch.
## Linked Issues or Issue Description
**Subsystem affected**
Paperclip Runner ACPX driver, runtime host, sidecar, backend factory,
package dependency, and provider conformance tests.
**Problem or motivation**
The production ACPX backend was Codex-only. Claude needs the same
fail-closed model, authorization, cancellation, cleanup, and recovery
boundaries without exposing an unsafe secondary runtime path.
**Proposed solution**
Generalize the hardened ACPX runtime for the exact qualified `claude`
profile, add the pinned Claude ACP package and reviewed isolation patch,
and reject Pi before installation, backend construction, sidecar
initialization, Rust session admission, or process creation.
**Alternatives considered**
Activating Pi in this PR was rejected after security review because its
secondary runtime executable was pathname-based and lacked the verified
descriptor/snapshot boundary. Pi is deferred to a dedicated follow-up.
Replaying the older generic ACPX implementation was rejected because it
predates current hardening.
**Roadmap alignment**
ROADMAP.md does not list a conflicting ACPX-provider project. This
extends the existing Runner provider architecture.
## What Changed
- Generalized the ACPX backend, driver, runtime adapter, host, and
sidecar for the qualified Claude profile.
- Added Claude ACPX activation through its exact pinned package/model
pair and isolated-settings patch.
- Added provider-lifetime fencing for non-Codex qualified ACPX sessions.
- Kept Pi dependencies and its patch out of the package and build
configuration.
- Added fail-closed Pi rejection at driver validation, backend
construction, runtime-host admission, sidecar initialization, and Rust
session validation.
- Added focused tests for Claude selection, model enforcement, lifecycle
fencing, cancellation, recovery, and Pi rejection.
- Did not change or commit `pnpm-lock.yaml`; CI regenerates the PR
lockfile under the existing repository policy.
## Verification
- GitHub Actions is the authoritative verification environment for this
PR.
- CI runs dependency policy, runner package checks, protocol parity,
typecheck, build, security, and stack policy.
- Local tests were not run because this checkout is resource
constrained, per the requested workflow.
## Risks
- Claude package behavior can drift from the qualified protocol; the
package and patch are pinned and admission verifies the exact profile.
- Unsupported providers and models fail closed.
- Pi remains unavailable until descriptor-confined verified launch
exists for its separate runtime.
- Existing Codex ACPX behavior remains covered by shared conformance
tests.
> For core feature work, check [ROADMAP.md](ROADMAP.md) first and
discuss it in #dev before opening the PR. Feature PRs that overlap with
planned core work may need to be redirected.
## Model Used
OpenAI Codex, GPT-5.6, with repository tools, code execution, and
parallel agent review.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs
- [x] I have described the issue in the PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name contains no internal task identifier
- [ ] I have run tests locally and they pass — GitHub Actions is
authoritative for this resource-constrained checkout
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented risks
- [ ] All applicable Paperclip CI gates are green
- [ ] Greptile is 5/5 with no actionable findings
## Stack
- Position: lowest unmerged PR
- Base: `master`
- Previous:
[#12588](https://github.com/paperclipai/paperclip/pull/12588), merged
qualified OpenCode runtime
- Next: [#12591](https://github.com/paperclipai/paperclip/pull/12591),
native application integration
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust ACPX session can now suspend only at a safe idle boundary
and its events have a durable projection
> - A later runner process cannot safely resume from an unbound or
partially written identity record
> - The recovery anchor must bind the exact provider identity, run,
normalized session, catalog revision, and catalog digest
> - The record must be bounded, private, strict about schema drift, and
atomically replaceable
> - Recovery must re-admit the entire prospective session configuration
before releasing the stored identity
> - This pull request adds only that package-local checkpoint store
without selecting ACPX in runnerd
## Linked Issues or Issue Description
Refs #12424
Refs #12422
## What Changed
- Add a versioned ACPX safe-suspension checkpoint contract with unknown
fields rejected at every persisted level.
- Bind each checkpoint to the run, normalized session, catalog revision,
catalog digest, and exact provider identity.
- Persist a checkpoint-specific strict identity that requires the pinned
permission mode without narrowing the additive live sidecar identity
wire shape.
- Construct checkpoints only from a session configuration whose model,
permission policy, tool catalog, and expected identity validate.
- Admit recovery only when reconstructing the checkpoint from the
prospective configuration produces an exact match.
- Reject run, session, catalog revision, catalog digest, model,
permission, expected-identity, profile, and workspace drift fail closed.
- Require persisted run/session IDs to satisfy the same stable-ID
boundary as fresh session admission.
- Store the checkpoint under a dedicated private runner-state
subdirectory.
- Bound checkpoint files to 1 MiB before reading or decoding.
- Refuse symlinked state directories and non-private or non-regular
checkpoint files.
- Replace checkpoints atomically through a private temporary file and
directory sync.
- Make repeated saves of the same checkpoint idempotent.
- Add integration coverage for private round trips, complete recovery
admission, malformed/oversized files, nested schema drift, missing
permission binding, invalid stable IDs, and symlink denial.
- Document the package-local suspension recovery boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `1ee738cf487defe88043b241c4e2dc34a1a8a7bc` (`master`
after #12424 merged).
- Exact replay head: `89cfea5495428be890810d2b8a27673943234ca3`.
- Stable patch ID: `dbaeb0bbe773f1ca5ef1f9bdc0fa61f4a08ca451`.
- The exact delta is 4 files and 591 additions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, dependency, or migration change.
- `git diff --check` and the Cargo formatting check pass on the replayed
delta.
- Exact-head GitHub Actions run `33374006661` (attempt 2): **PASSED**
with 23/23 jobs passed.
- Greptile reviewed exact head
`89cfea5495428be890810d2b8a27673943234ca3`: **5/5**, with zero
unresolved review threads.
- Superagent, contributor trust, Socket, and Snyk security checks:
**PASSED**.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- A checkpoint is valid only after the sidecar has confirmed safe
suspension. The constructor therefore accepts the exact identity
returned by that operation and revalidates it against local authority.
- Loading proves only that the file is structurally valid;
`admit_recovery` is the boundary that proves the file belongs to the
prospective run, catalog, model, permission policy, and expected
provider identity.
- The checkpoint intentionally contains no credentials, bootstrap
ticket, provider output, or pending request payload.
- Strict checkpoint schema and identity validation rejects incompatible
or tampered recovery records rather than attempting partial migration.
This strictness is checkpoint-local and does not narrow existing PRP or
sidecar wire compatibility.
- Atomic replacement uses the platform `rename` primitive; Unix
additionally syncs the private parent directory before reporting
success.
- No production path loads this checkpoint in this pull request. Runnerd
execution and durable recovery wiring remain a later slice.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with version and capability
details
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have linked the preceding public PRs or described the issue
in-PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust runner now owns validated, scoped ACPX reducer events and
safe session suspension
> - Durable PRP transport must receive provider-neutral events rather
than sidecar-native envelopes
> - Semantic calls and questions must retain the exact run, session,
turn, item, and provider-request authority used by the durable command
stream
> - Terminal, result, assistant, process, and diagnostic events also
need one reviewed projection boundary
> - Permission requests remain impossible under the pinned Codex policy
and must fail closed if they reach projection
> - This pull request adds only that package-local projection without
selecting ACPX in runnerd
## Linked Issues or Issue Description
Refs #12422
## What Changed
- Add a validated durable ACPX event projection context bound to one
run, normalized session, turn, and item.
- Pass already normalized activity events through without reintroducing
provider-native envelopes.
- Project authorized tool calls into canonical semantic input receipts
with exact correlation and content digests.
- Project structured questions into provider-neutral
`paperclip.runtime_request.v2` events.
- Preserve both the public projected request identity and the original
provider request identity so responses resolve the exact sidecar
request.
- Project dynamic semantic operation results as `semantic_tool.result`;
only reserved finish/block operations may propose the run result.
- Project semantic completion results into `run.result.proposed`.
- Project terminal-flushed assistant messages on the final channel and
turn terminal states into existing provider-neutral event families.
- Project sanitized process metadata and diagnostics into bounded
harness diagnostics.
- Validate runtime-request origins against their strict durable shape
and fall back from empty optional titles to a valid question prompt.
- Reject invalid identities, projected-identity collisions, unstable
semantic receipt identities, permission requests, and cross-turn
projection fail closed.
- Add integration coverage across reducer event families, correlation,
identity validation, projected question resolution, and pinned-policy
denial.
- Document the durable projection boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `80639f4f69c8938eb74bdc0833df93e0ed91dab3` (`master`
after #12422 merged).
- Exact replay head: `3cb29581d2bcbc4b47f8069baffd721c6ce4e444`.
- Stable patch ID: `92910b56575e67ae83960177d467a565019ba282`.
- The exact delta is 22 files, 1,206 additions, and 59 deletions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, dependency, or migration change.
- `git diff --check` and the Cargo formatting check pass on the replayed
delta.
- Exact-head GitHub Actions run `33372209037` (attempt 2): **PASSED**
with 23/23 jobs passed.
- Greptile reviewed exact head
`3cb29581d2bcbc4b47f8069baffd721c6ce4e444`: **5/5**, with zero
unresolved review threads.
- Superagent, contributor trust, Socket, and Snyk security checks:
**PASSED**.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- This function accepts reducer output, not raw sidecar frames. Callers
must preserve the existing scope-first decode and reduction order.
- Semantic input includes the already sanitized provider input while its
content receipt uses the same canonical digest.
- Structured input preserves the validated provider-neutral question set
and sanitized origin.
- Noncanonical provider request identities are deterministically
projected for PRP while the original identity remains authoritative for
the sidecar resolution command.
- Existing PRP v1 identifiers remain schema-compatible; the only public
ID-schema change widens turn/item limits from 160 to 240 characters. The
internal ACPX sidecar wire schema now mirrors the stable IDs its Rust
transport already enforced.
- The projector verifies event-carried terminal and assistant turn
identifiers against the durable context.
- No production path invokes this projector in this pull request.
Durable command execution remains the next slice.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with version and capability
details
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have linked the preceding public PR or described the issue in-PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust runner now owns exact ACPX request resolution and
fail-closed session state
> - A recoverable provider session needs an explicit suspension boundary
before runnerd can safely hand work across process lifetimes
> - Suspension is unsafe while a turn or provider request is active
because remote effects may still be in flight
> - A successful acknowledgement must preserve the exact immutable
session identity
> - This pull request adds only that guarded package-local lifecycle
operation without selecting ACPX in runnerd
> - The benefit is a small recovery primitive whose safety rules are
independently reviewable
## Linked Issues or Issue Description
Refs #12421
## What Changed
- Add a provider-state query for active pending tool, input, or
permission requests.
- Permit session suspension only when no turn or provider request is
active.
- Send a bounded `session.suspend` command with an operator-safe reason.
- Require an affirmative suspension acknowledgement and the exact
existing session identity.
- Treat transport failure, rejection, omitted or malformed identity, and
identity drift as fail-closed terminal errors.
- Mark a successfully suspended session closed and terminate the local
sidecar process while retaining cleanup ownership if termination must be
retried.
- Preserve a valid session after purely local unsafe-state rejection so
it can settle before retry.
- Extend the fake sidecar with deterministic suspension success,
acknowledgement mismatch, identity mismatch, and missing-identity modes.
- Add integration coverage for safe suspension, active-turn rejection,
fail-closed remote mismatches, and retained cleanup.
- Document the package-local suspension boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `91d861ff69d415a3b105ae2eaad9cc56c66a9231` (`master`
after #12421 merged).
- Exact replay head: `085667e10c51c6c0360732f63c8fef83e806dd88`.
- Stable patch ID: `b18d6b7efd1de569d3068b0a782f8aac2fbc9322`; this is
the prepared suspension delta plus the focused fake-sidecar fix that
consolidates mismatch modes into the existing command arm.
- The exact delta is 5 files, 136 additions, and 5 deletions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- Exact-head GitHub Actions run `33369571343` (attempt 1): **PASSED**
with 23 jobs passed and zero failures.
- Greptile reviewed exact head
`085667e10c51c6c0360732f63c8fef83e806dd88`: **5/5**, with zero
unresolved review threads.
- Superagent, contributor trust, Socket, and Snyk security checks:
**PASSED**.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- The sidecar may apply suspension before a transport failure is
observed. The local session closes rather than retrying an ambiguous
effect.
- Local active-work rejection happens before transport and leaves the
valid session open so the caller can settle it safely.
- Identity equality is checked across provider, driver, session, thread,
run, and company fields before accepting suspension.
- No production path invokes suspension in this pull request. Runnerd
execution and durable recovery wiring remain later slices.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked existing issues with `Refs #` or described
the issue in-PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust runner now owns a scoped ACPX turn and validates provider
requests before exposing them
> - A live turn can pause for semantic tool results or structured input
> - Local state and the sidecar can diverge if the runner clears a
request before the sidecar accepts its resolution
> - A mismatched or ambiguous remote acknowledgement must close the
session, while a local validation error must preserve the request for a
safe retry
> - This pull request adds those two-phase resolution paths and rejects
Codex permission events that bypass the pinned policy without selecting
ACPX in runnerd
> - The benefit is an atomic request boundary that can be wired into
durable execution in a later slice
## Linked Issues or Issue Description
Refs #12420
Refs #12419
## What Changed
- Resolve authorized semantic tool calls only for the active turn and
exact pending operation.
- Validate semantic results against the authorized response schema
before transport.
- Send a bounded generic provider error when a semantic operation fails
without exposing internal error text or payloads.
- Resolve structured input only after validating the exact persisted
question set.
- Support explicit submit, decline, and cancel input outcomes.
- Reject any Codex permission event that bypasses the pinned sidecar
policy.
- Build candidate provider and authorization state before each request.
- Commit candidate state only after the sidecar returns an affirmative
resolution acknowledgement.
- Preserve pending work after local validation errors so the caller can
retry safely.
- Terminate the session after transport failure or an invalid remote
acknowledgement because the remote effect is ambiguous.
- Bind every resolution to the exact active turn and request or call
identity.
- Extend the fake sidecar and add integration coverage for successful
commits, safe local retries, pinned-policy enforcement, redaction, and
fail-closed acknowledgement mismatch.
- Document the package-local resolution boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `3aa2065d084d6a29492aaa15e822b5d17c3a4266` (`master`
after #12420 merged).
- Exact replay head: `9507024f70c6f434c2c322385d3a9e240250b03c`.
- Stable patch ID: `a4f27d2fae606596f70b5b1c2b29dd7f250541d8`, identical
to the prepared two-commit delta.
- The exact delta is 5 files, 463 additions, and 7 deletions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- GitHub Actions run `33368135190`, attempt 2: **PASSED** on the exact
replay head (23/23 jobs passed; a failed-job-only retry cleared one
unrelated server test environment failure where `npm` was unavailable).
- Greptile: **5/5** on the exact replay head with zero unresolved review
threads; Superagent, Socket, Snyk, and contributor-trust checks also
passed.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- A transport failure can happen after the sidecar applied a resolution.
The session closes instead of retrying an ambiguous effect.
- Local validation happens before transport and preserves pending state,
so a corrected answer or result can be retried.
- The sidecar transport already correlates each command response to its
request identifier. This slice also requires `resolved: true` before
local commit.
- The initial Codex sidecar owns its pinned permission policy and does
not delegate permission resolution. Any permission event therefore
terminates the session fail closed.
- No production path invokes these methods in this pull request. Durable
ACPX execution wiring remains a later slice.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust runner now owns a bounded ACPX session and a fail-closed
turn lifecycle
> - Provider questions pause a turn and must return structured answers
to the same persisted question set
> - JSON Schema validates the wire shape, but it cannot validate
identifiers and constraints across two documents
> - Unknown questions, invalid choices, and malformed custom answers
must fail before any provider receives them
> - This pull request adds only the package-local response validator and
tests
> - The benefit is a small trust boundary that later request-resolution
code can use without changing production selection
## Linked Issues or Issue Description
Refs #12419
## What Changed
- Validate `paperclip.question_response.v1` against its versioned JSON
Schema.
- Bound serialized responses to 768 KiB before validation.
- Require answer identifiers to match the exact persisted question set.
- Require answers for required questions and reject unknown question
identifiers.
- Enforce text, single-select, and multi-select answer modes.
- Match the existing TypeScript numeric syntax, including decimal,
exponent, hexadecimal, octal, and binary input.
- Match ECMAScript trimming exactly, including BOM whitespace while
rejecting Unicode NEL rather than inheriting Rust-specific whitespace
behavior.
- Enforce known options, custom-answer policy, text length, pattern, and
numeric constraints.
- Validate duplicate option IDs, inverted bounds, and dynamic patterns
before answer lookup so malformed optional questions fail closed even
when unanswered.
- Match JavaScript UTF-16 code-unit length semantics for text
constraints and the 100,000-unit response-field bound.
- Preserve the public optional `recommended` question-option field in
the versioned schema, generated schema bundle, and Rust validation path.
- Return typed validation errors for malformed inputs without panics.
- Export the validator from the Rust runner core.
- Add table-driven tests for valid, mismatched, malformed, oversized,
and numeric-boundary responses.
- Document the package-local structured-response boundary.
- Add `num-bigint` 0.4 and `num-traits` 0.2 as direct runner-core
dependencies for exact arbitrary-length radix parsing and one-step
JavaScript Number rounding; update only the package-local runner Cargo
lockfile.
- Do not change the repository PNPM lockfile, workflows, runnerd
selection, server behavior, UI, or migrations.
## Verification
- Replay base: `9a9fdf06ee4142f77427db30efccc4c43056f64b` (`master`
after #12419 merged).
- Exact replay head: `fad92b3fb348b66ddb10dde44b7b060e55c4fe96`.
- Stable patch ID: `4d6ffbd519dd081f7ea530977cd965bd4569fc75`; this is
the prepared two-commit delta plus the focused cross-language parity fix
found during replay review.
- The exact delta is 10 files, 712 additions, and 2 deletions, all in
`packages/paperclip-runner`.
- The package-local `packages/paperclip-runner/runner/Cargo.lock`
records the two direct runner-core dependencies; their already-resolved
versions and checksums are unchanged.
- The question-set schema source, generated TypeScript schema bundle,
and protocol manifest hash are updated together; the schema SHA-256 is
`42b5441a3d388851dacb6e4500dfd4a17d878eded2e724228078b647e7440d3f`.
- GitHub Actions run `33366812025`, attempt 2: **PASSED** on the exact
replay head (23/23 jobs passed; a failed-job-only retry cleared one
unrelated ACPX runtime-host timeout).
- Greptile: **5/5** on the exact replay head with zero unresolved review
threads; Superagent, Socket, Snyk, and contributor-trust checks also
passed.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- The validator compiles the embedded response schema for each
submission. Responses are user-paced and bounded, so this keeps the
slice simple without affecting a hot event path.
- The persisted question set is the source of truth for identifiers and
constraints. A malformed persisted set fails closed.
- Numeric input follows the existing structured-question contract,
including JavaScript-prefixed syntax. Optional whitespace-only answers
are rejected instead of being treated as an omitted value.
- Error messages identify the invalid field but do not include answer
text.
- No production path invokes this validator in this pull request.
Request resolution remains the next slice.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust runner now validates and opens an exact Codex ACPX session
and attaches one run/catalog identity
> - A live session must start one turn, preserve immutable workspace
authority, accept interruption, and consume provider events
> - Command acknowledgements and event scope are trust boundaries;
disagreement must terminate the session rather than permit divergent
state
> - The existing reducer already owns bounded state, ordering,
correlation, and terminal cleanup
> - This pull request connects turn commands and polling only to that
reviewed reducer without selecting ACPX in runnerd
> - The benefit is a fail-closed execution loop before semantic request
resolution and production selection are added
## Linked Issues or Issue Description
Refs #12418
Refs #12417
## What Changed
- Start one ACPX turn only after validating a bounded turn identifier,
bounded message, and the session's immutable working directory.
- Require `turn.start` to acknowledge the exact requested turn before
mutating provider state.
- Request interruption only for the active turn and require an
affirmative cancellation acknowledgement.
- Keep the turn active until the authoritative terminal event is polled
and reduced.
- Poll bounded sidecar events and route every event through the existing
scope-first payload/state boundary.
- Terminate the sidecar fail closed after a rejected or mismatched turn
command, transport failure, or invalid scoped event.
- Preserve ordinary local validation failures without mutating or
closing an otherwise valid session.
- Admit semantic tool calls through the run-scoped authorized tool
bridge before returning them to runnerd.
- Reduce events and authorize tool calls on candidate clones, then
commit both together only after every check passes.
- Rotate and scope tool receipts by run and turn; reserve pending,
completed, and settled call IDs across both tool bridges so cross-bridge
duplicates cannot be re-admitted.
- Preserve exact turn correlation through payload, scope, provider
state, fake sidecar, and integration fixtures.
- Extend the package-local fake sidecar with deterministic turn
activity, terminal, acknowledgement-mismatch, and scope-violation modes.
- Add integration coverage for happy-path
activity/interruption/settlement, local validation, turn mismatch,
cancellation mismatch, cross-run denial, receipt rotation, and
cross-bridge call-ID reuse.
- Document the turn lifecycle boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `10cf68295d5457ccf1010db20a06e2951e47162a` (`master`
after #12418 merged).
- Exact replay head: `1ec18368bdacafafa58720b9e2b982e1b0d11163`.
- Stable patch ID: `4b43a5ef2a428850a05b0741d06e7cf4f48ec334`, identical
to the prepared four-commit delta plus the focused cross-bridge call-ID
reservation fix.
- The exact delta is 13 files, 1,855 additions, and 37 deletions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- GitHub Actions run `33364033038`, attempt 2: **PASSED** on the exact
replay head (23/23 jobs passed; the failed-job-only retry cleared one
unrelated flaky server heartbeat test).
- Greptile: **5/5** on the exact replay head with zero unresolved review
threads; Superagent, Socket, and Snyk checks also passed.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- A sidecar may emit events while a command response is in flight. The
transport buffers them, then the session binds the acknowledged turn
before polling and reducing those events.
- A turn-start rejection can leave provider-native state ambiguous, so
command rejection or acknowledgement mismatch terminates the session
instead of attempting unsafe reuse.
- Interruption does not settle local state; only the validated terminal
event clears the active turn.
- Any transport or event validation failure terminates the child process
so a compromised stream cannot continue.
- Call identifiers are authorization identities and remain reserved
across pending, completed, and settled states in both bridges.
- The package exposes new Rust methods, but no production path invokes
them in this pull request.
- Tool authorization is atomic with reducer state, but tool results,
structured input, and permission resolution remain later slices.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust runner now has bounded ACPX transport, scope, payload,
normalization, and state-reduction layers
> - A live provider needs a lifecycle owner that starts the sidecar and
proves it opened the exact requested session
> - The initial production slice is Codex-only and must fail closed on
capability, model, identity, policy, or catalog drift
> - Failed bootstraps must not leak a child process, and ordinary
shutdown must preserve resumable provider state
> - This pull request adds that package-local lifecycle without
selecting ACPX in runnerd
> - The benefit is a reviewable bootstrap/recovery boundary before turn
commands and production selection are connected
## Linked Issues or Issue Description
Refs #12417
Refs #12416
## What Changed
- Add a package-local ACPX provider session configuration and lifecycle
owner.
- Reject non-UTF-8 runtime and working directories before spawning so
JSON path serialization cannot panic.
- Validate the sidecar launch contract, Codex-only agent, model, run and
session identifiers, absolute directories, positive JSON-safe catalog
revision, pinned permission mode, bounded instructions, and canonical
authorized tool catalog before spawning.
- Verify the initialization protocol version, child PID,
persistent-session support, exact-model support, runner-owned permission
policy, semantic-tool bridge, and structured-input contract.
- Open an identity-bound session and require the requested and effective
models, permission mode, session identifier, digests, and optional
recovery identity to match exactly.
- Attach the run and require the sidecar to confirm the exact run
identifier and catalog revision.
- Retry failed transport cleanup while retaining lifecycle ownership;
terminate the sidecar after every failed bootstrap and on an unclosed
session drop.
- Close sessions without discarding persistent state and make explicit
shutdown idempotent.
- Extend the package-local fake sidecar with deterministic bootstrap,
wrong-model, and wrong-run responses.
- Add five integration tests covering successful bootstrap/shutdown,
pre-spawn policy validation, model mismatch, run mismatch, and recovery
identity matching.
- Document the session bootstrap boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `f038633bf5b04163ff985ef0542876bd9f455379` (`master`
after #12417 merged).
- Exact replay head: `a6d9ad62f20fdb47a1dbc76aa4baa9d8fa6dae53`.
- Stable patch ID: `82b6f2551749598a688c3f44a1a3714516030429`, identical
to the reviewed `e6e550f9..d51a8855` delta.
- The exact delta is 5 files and 609 additions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- Focused Rust lifecycle, package, repository, security, and Greptile
checks: **PASSED** on the replayed exact head. Full CI run `33362799786`
is green; its failed-job retry passed one unrelated flaky server shard
without a patch change. Greptile is exact-head 5/5, all security checks
pass, and no review threads remain unresolved.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- This lifecycle owns a child process and session identity.
Configuration is fully validated before spawning, every bootstrap
response is checked against the child PID and requested identity, and
failed bootstrap always terminates the process.
- Recovery identity matching is exact so a persisted native record
cannot silently attach to another session, model, workspace, profile, or
permission policy.
- Explicit shutdown preserves persistent provider state; a dropped
unclosed session still terminates its process group as a safety
fallback.
- The package exports a new Rust module, but no production path
constructs it in this pull request.
- Turn commands, event polling, request resolution, and runnerd
selection remain later slices.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust runner now has bounded ACPX transport, scope, payload, and
provider-neutral normalization layers
> - A live provider still needs state across events to correlate
requests and preserve turn ordering
> - That state must not mutate before scope and payload validation
succeeds
> - It must bound retained text and pending values, suppress repeated
reasoning boundaries, and make one semantic result authoritative
> - This pull request adds that reducer without issuing process commands
or selecting ACPX in runnerd
> - The benefit is a separately reviewable state machine before
transport commands and production selection are connected
## Linked Issues or Issue Description
Refs #12416
Refs #12415
## What Changed
- Add a package-local ACPX provider state reducer with one run binding
and one active turn.
- Decode every sidecar event through the existing scope-first payload
boundary before state mutation.
- Bound retained assistant text, pending semantic tool inputs, and
pending runtime request values.
- Correlate semantic tool calls, structured input requests, and
permission requests by stable IDs.
- Keep pending tool resolution two-phase so callers remove state only
after a later sidecar command succeeds.
- Carry authoritative tool classification from validated payloads into
retained state.
- Suppress repeated reasoning-start activity within one turn.
- Accept one semantic result idempotently and fail closed on a
conflicting result.
- Flush the final assistant message before the authoritative terminal
event.
- Clear unresolved turn-scoped requests at terminal state and reject
late events for the settled turn.
- Admit redacted global process and diagnostic events without requiring
an active turn.
- Add seven integration tests for turn ordering, correlation, conflicts,
scope-before-mutation, redaction, and terminal cleanup.
- Document the state boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `7bb6cebeae727a16c205bb80b5c2b9e92ea6b5fa` (`master`
after #12416 merged).
- Exact replay head: `da82e7f67ecd6f0f2184f303b1b703721099cd86`.
- Stable patch ID: `3027df409450d08b2c32383585597a39e06c6f53`, identical
to the reviewed `f9cb4e54..e6e550f9` delta.
- The exact delta is 4 files and 720 additions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- Focused Rust state, package, repository, security, and Greptile
checks: **PASSED** on the replayed exact head. Full CI run `33362166929`
completed successfully, Greptile is exact-head 5/5, all security checks
pass, and no review threads remain unresolved.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- This reducer owns security-sensitive correlation and terminal
ordering, so its only raw-event entry point always invokes scope-first
decoding.
- Pending resolution methods must be called only after the corresponding
sidecar transport command succeeds; the later process adapter owns that
sequencing.
- Terminal events intentionally clear unresolved turn-scoped requests so
late tool or input results fail closed.
- One semantic result remains readable after terminal state for later
durable finalization and is cleared only when a new turn begins.
- The package exports a new Rust module, but no production path
constructs it in this pull request.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Rust runner now admits ACPX sidecar frames only after transport,
scope, and payload validation
> - Valid payloads still contain provider-native runtime event shapes
> - Provider-native shapes must not cross the PRP boundary or diverge
from direct Codex task activity
> - This pull request maps the display-safe runtime subset into existing
provider-neutral event families
> - Stateful semantic-result, terminal, and reasoning-deduplication
behavior remains reserved for the later provider adapter
> - The benefit is a reviewable normalization boundary without selecting
ACPX in production
## Linked Issues or Issue Description
Refs #12415
Refs #12414
## What Changed
- Normalize validated ACPX text, reasoning, plan, status, tool, notice,
and error updates into existing PRP activity families.
- Keep reasoning contents private while preserving a reasoning activity
boundary.
- Map plan entries, usage, review-mode status, and tool lifecycle into
bounded canonical payloads.
- Generate one shared ACPX sidecar event/classification contract for
TypeScript and Rust, with ASCII-only classification parity and bounded
kind/title fields.
- Preserve authoritative tool-call identity and classification even when
the aggregate native event exceeds the generic frame budget.
- Resolve display-only tool targets within the workspace under the
provider host's path semantics; reject raw, unmarked, absolute,
parent-traversing, URL-shaped, and unsafe drive-shaped values.
- Redact and digest retained tool output with the existing durable
policy.
- Ignore provider inventory status updates that have no user-facing
activity.
- Leave semantic results and `done` updates to the stateful adapter so
durable receipts and terminal events are not duplicated.
- Add cross-language and Rust coverage for every mapping family,
classifier parity, privacy, unsafe paths, redaction, bounded
titles/kinds, and oversized tool-call preservation.
- Document the normalization and display-path authorization boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `fe2ddfad2b5cb604b3244492257db0e6aec11d47` (`master`
after #12415 merged).
- Exact replay head: `b7f5588bf6e8e0f946ffa8869a3204c344808418`.
- Stable patch ID: `fda62c7c20afc5ef9c75d07f163a466db82efabd`, identical
to the prepared six-commit delta plus the focused oversized-tool-call
review fix.
- The exact delta is 14 files, 1,714 additions, and 60 deletions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- Focused protocol-generation, TypeScript sidecar, Rust normalization,
package, repository, security, and Greptile checks: **PASSED** on the
replayed exact head. Full CI run `33361437835` completed 23/23 jobs
successfully, Greptile is exact-head 5/5, all security checks pass, and
no review threads remain unresolved.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- This code controls what provider activity is retained and displayed,
so malformed native values must not bypass the earlier decoder.
- The function contract requires an already scope-checked and
payload-validated runtime event; the future adapter must preserve that
order.
- Tool classification and identity are security-relevant authorization
inputs and remain explicit even when optional aggregate display data is
dropped for bounds.
- Repeated reasoning chunks require stateful suppression. This mapper
exposes a privacy-safe start boundary and the later adapter owns
per-turn deduplication.
- Semantic results and terminal authority intentionally produce no
activity here; the later adapter must commit them through the durable
operational paths.
- The package exports new generated and Rust normalization surfaces, but
no production path invokes them in this pull request.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The runner needs a bounded process boundary for each qualified
provider runtime
> - The ACPX transport now verifies frame shape, sequence, run scope,
and turn scope
> - The sidecar schema keeps event payloads open so each event family
needs a second validation boundary
> - A later provider adapter must not retain or act on malformed payload
fields
> - This pull request validates and redacts package-local payloads and
keeps the provider unselected
> - The benefit is a typed fail-closed boundary before provider state or
semantic tools can consume an event
## Linked Issues or Issue Description
Refs #12414
Refs #12412
## What Changed
- Decode sidecar payloads only after run and turn scope validation
passes.
- Limit each decoded payload to 256 KiB.
- Add typed payload variants for runtime events, permission requests,
input requests, semantic tool calls, terminal events, process events,
and diagnostics.
- Admit only the nine runtime event shapes emitted by the reviewed Codex
ACPX sidecar.
- Validate runtime text, plan entries, tool locations, semantic result
identities, notices, errors, and terminal status values.
- Validate input requests against `paperclip.question_set.v1`.
- Reject duplicate question IDs and duplicate option IDs within one
question.
- Require bounded control identities and object-shaped operational
values.
- Redact diagnostic, error, process, permission, tool, and retained
runtime values before they can enter provider state.
- Add six integration tests for every admitted shape, malformed values,
scope-before-decode ordering, size limits, question ambiguity, and
secret redaction.
- Document the payload boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `3db24d9366831559b1219782475e760ec041b639` (`master`
after #12414 merged).
- Exact replay head: `de045b42b5b52cca6c3021380747c56693ccd179`.
- Stable patch ID: `90a6f0ed68b2ca7fa5397a8bf93e5e95df5bb58c`, identical
to the reviewed `972a3b38..9bb85e93` delta.
- The exact delta is 4 files and 806 additions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- Focused Rust payload, package, repository, security, and Greptile
checks: **PASSED** on the replayed exact head. Full CI run `33360832792`
completed 23/23 jobs successfully, Greptile is exact-head 5/5, all
security checks pass, and no review threads remain unresolved.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- This decoder is a security boundary because later code can act on
decoded semantic tool calls and input requests.
- It validates event authority before it inspects a payload.
- It fails closed on unknown runtime event types, malformed fields,
ambiguous question identifiers, unsupported terminal states, and
oversized payloads.
- It applies the existing durable redaction policy to retained values.
- The package exports a new Rust module, but no production path
constructs it in this pull request.
- A later provider adapter must preserve this validation order and must
not consume raw sidecar payloads directly.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The runner needs a bounded process boundary for each qualified
provider runtime
> - The package now has a fail-closed transport for the Codex ACPX
sidecar
> - A valid sidecar frame can still name the wrong run or turn
> - A later provider adapter must reject those events before it maps or
persists them
> - This pull request adds package-local run and turn scope validation
and keeps it unselected
> - The benefit is an explicit authorization boundary without a
production behavior change
## Linked Issues or Issue Description
Refs #12412
Refs #12410
## What Changed
- Add an `AcpxEventScope` for one run and at most one active turn.
- Validate run and turn identifiers before they enter scope state.
- Make repeated binding of the same turn safe.
- Reject a second active turn and a stale turn clear.
- Require exact run and active turn bindings for operational, tool,
input, permission, and terminal events.
- Permit process and diagnostic events without a scope because they can
describe the sidecar process itself.
- Validate every optional run or turn binding on process and diagnostic
events.
- Add an integration-test file for all seven event families, missing
scope, cross-run scope, cross-turn scope, inactive turns, turn
lifecycle, and invalid identifiers.
- Document the event authorization boundary.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `75708fec6d421a247ba2fc832997de24ed10a085` (`master`
after #12412 merged).
- Exact replay head: `88bb248442b8f628c20e977d2c2dbc21d85fb6dd`.
- Stable patch ID: `0b6443b1bc32ed244f650936026367dd84bcfd65`, identical
to the reviewed `c5654218..972a3b38` delta.
- The exact delta is 4 files and 280 additions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- Focused Rust scope, package, repository, security, and Greptile
checks: **PASSED** on the replayed exact head. Full CI run `33360199404`
completed successfully, Greptile is exact-head 5/5, all security checks
pass, and no review threads remain unresolved.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- Event scope is a security boundary because it rejects data from
another run or turn.
- The validator fails closed on missing, malformed, stale, or
cross-scope identifiers.
- Process and diagnostic events can remain global, but any scope they
provide must be valid.
- The package exports a new Rust module, but no production path
constructs it in this pull request.
- A later provider adapter must bind and clear the exact turn around
each sidecar turn.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The runner needs a bounded process boundary for each qualified
provider runtime
> - The package now provides a reviewed Codex-only ACPX sidecar
executable
> - Runnerd does not yet have a strict client for that sidecar protocol
> - A later provider adapter must not mix process transport defects with
provider mapping
> - This pull request adds the package-local Rust transport and keeps it
unselected
> - The benefit is a tested fail-closed boundary for the later Codex
provider adapter without a production behavior change
## Linked Issues or Issue Description
Refs #12410
Refs #12386
## What Changed
- Add a Rust client for the generated ACPX sidecar v2 contract.
- Validate the executable path, launch arguments, request timeout, and
shutdown grace before process start.
- Require exact request identities and contiguous event sequence
numbers.
- Reject replayed events, sequence gaps, wrong response identities,
malformed frames, unknown fields, and unsupported protocol versions.
- Bound stdout frames to 1 MiB and buffered events to 512 entries.
- Bound event poll timeouts to 120 seconds before any `Instant`
arithmetic.
- Buffer valid events that arrive while a command waits for its
response.
- Treat an empty event poll as a normal timeout without poisoning the
transport.
- Discard retained events and reject all polling after a terminal
transport failure.
- Terminate the process group after a timeout, transport failure, or
protocol failure.
- Keep a valid sidecar command rejection separate from a transport
failure so the next command can run.
- Redact and bound stderr diagnostics before they enter an error.
- Add a deterministic fake sidecar and twelve integration tests for
success, polling, timeout bounds, poisoned queues, replay, gaps,
identity mismatch, rejection, event floods, oversized frames, and secret
redaction.
- Document that the transport remains package-local and does not change
runnerd provider selection.
- Do not change dependencies, lockfiles, workflows, runnerd selection,
server behavior, UI, or migrations.
## Verification
- Replay base: `9ad8dbffa0a4759dcda2769042d6e8f02adcdf8d` (`master`
after #12410 merged).
- Exact replay head: `5c41111c4f9564405a6e87a02b6cf253a4424e5f`.
- Stable patch ID: `60aca2620fdbb73fbbc203e928d39dcd450e085b`; this
preserves the reviewed `1334a7f5..c5654218` six-file delta and keeps
trusted bounded-reader failures distinct from fully redacted child
stderr.
- The exact delta is 6 files and 896 additions, all in
`packages/paperclip-runner`; it contains no lockfile, workflow, server,
UI, or migration change.
- Focused Rust transport, package, repository, security, and Greptile
checks: **PASSED** on the replayed exact head. Full CI run `33359202433`
is green; its failed-job retry passed the two unrelated flaky jobs
without a patch change. Greptile is exact-head 5/5, all security checks
pass, and no review threads remain unresolved.
- No local test result is claimed. GitHub Actions is the authoritative
verification environment for this replayed revision.
## Risks
- The transport crosses an untrusted process boundary and is
security-sensitive.
- It fails closed on malformed frames, identity drift, event replay,
sequence gaps, queue overflow, frame overflow, timeout, process exit,
and channel failure.
- A terminal failure clears retained events before it marks the
transport unavailable.
- It redacts and bounds retained diagnostics before it returns them to a
caller.
- A valid remote command rejection does not corrupt the transport state.
- The package exports a new Rust module, but no production path
constructs it in this pull request.
- The later provider adapter must validate run, turn, session, model,
and tool bindings before it selects this transport.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5.6, agentic reasoning, tool use, and code
execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip runner needs a safe boundary before it can launch
ACP-compatible agents.
> - A caller-controlled command, model, environment, or frame could
bypass that boundary.
> - The ACPX transport contract in #12386 defines the allowed messages
but does not bind a runtime profile.
> - This pull request defines closed, versioned profiles and validates
the launch inputs around that contract.
> - The benefit is a small and reviewable trust boundary before any ACPX
process can become available.
## Linked Issues or Issue Description
**Agent or provider**
ACPX sidecar support for the qualified Pi, Claude, and Codex ACP
servers.
**Why this adapter is useful**
The runner needs one bounded process boundary for ACP-compatible
providers. A closed profile prevents an untrusted run from selecting an
arbitrary executable, package version, or model.
**How the agent is invoked**
A later pull request will launch an internal sidecar from an exact
profile. This pull request only validates profiles, environment values,
and protocol frames. It does not add an executable dependency or enable
an adapter.
**Additional context**
This pull request is stacked on #12386. It keeps the existing direct
adapters and the Codex runner path unchanged.
## What Changed
- Add a closed profile table for the qualified Pi, Claude, and Codex ACP
servers.
- Require the exact qualified model and return an isolated profile value
to callers.
- Add an agent-specific environment allowlist with entry and aggregate
size limits.
- Add strict parsing for bounded sidecar requests and structured plan
values.
- Reject unknown fields, unsupported protocol versions, invalid
identifiers, null bytes, cyclic values, and oversized input.
## Verification
- Runner TypeScript typecheck — passed.
- Runner TypeScript tests — 40 files and 362 Vitest tests passed; 11
Node contract tests passed.
- `pnpm -r typecheck` — passed for all applicable workspaces.
- `pnpm build` — passed, including runner binary, server, UI, and
workspace packages.
- Prettier and `git diff --check` — passed.
- The diff contains 6 files and does not change `pnpm-lock.yaml`, a
workflow, a package dependency, or a public export.
## Risks
The main risk is accepting more launch state than the sidecar needs. The
implementation uses an agent-specific allowlist, rejects null bytes, and
enforces per-entry and aggregate bounds. This pull request does not
launch a process or expose a new adapter, so production and
direct-adapter behavior remain unchanged.
## Model Used
OpenAI Codex with GPT-5 and repository tool use.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked an existing public item or described the
issue in this PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal task
identifier
- [x] I have run the affected tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have documented the compatibility and security boundary
- [ ] All applicable GitHub Actions are green
- [ ] Greptile is 5/5 with every actionable comment resolved
- [x] I will address all review findings before requesting merge
## Thinking Path
> - Paperclip Runner now has a complete guarded Codex vertical slice.
> - The next provider series must not start by importing a provider
implementation or dependency bundle.
> - ACPX needs one bounded, versioned process boundary shared by
TypeScript and Rust.
> - A schema is the authority; checked-in generated inventories keep
both languages in lockstep.
> - Unknown versions, commands, event types, and properties must fail
closed.
> - This pull request therefore lands only the sidecar wire contract and
its drift gate.
> - No ACPX runtime, dependency, executable, package export, or
production selection is added.
## Linked Issues or Issue Description
This is the first package-local unit in the post-Codex provider series.
**What happened?**
The integration branch contains an ACPX provider, but its TypeScript
sidecar and Rust client need a small shared authority before either
implementation can be reviewed safely. Importing the final integration
implementation directly would mix the protocol, runtime, third-party
dependencies, and production wiring.
**Expected behavior**
The schema defines every ACPX sidecar request, response, event, command,
event type, and protocol version. Generated TypeScript and Rust
inventories must drift-check against that schema. No runtime can select
or execute ACPX yet.
**Steps to reproduce**
1. Change the protocol version, command inventory, or event inventory in
the schema.
2. Run the runner protocol type check without regenerating the language
inventories.
3. Observe the drift gate fail.
**Paperclip version or commit**
Stacked on `runner-server-semantic-codex` at `ebd7f9df7`.
## What Changed
- Add the internal ACPX sidecar v2 JSON Schema outside the public PRP v1
schema catalog.
- Generate one TypeScript inventory and one Rust inventory from that
schema.
- Add generate and check hooks to the existing runner protocol-type
workflow.
- Add fail-closed AJV tests for all three message families, version
drift, unknown commands, and extra properties.
- Keep the generated Rust module unregistered until the Rust ACPX
transport exists.
## Compatibility Boundary
- Codex remains the only production runner provider.
- `paperclip_runner` selection and the default-off rollout flag are
unchanged.
- No ACPX package, patch, lockfile, binary entry point, root export,
server file, UI file, workflow, or dependency is added.
- The schema is shipped with the existing `protocol` directory but is
not added to the public PRP manifest.
- Existing direct adapters continue through their current paths.
- Diff against the actual stacked base: 6 files.
## Verification
- Runner TypeScript typecheck and both generated-contract drift gates —
passed.
- Runner TypeScript tests — 37 files and 355 Vitest tests passed; 11
Node contract tests passed.
- Rust provider-bridge regression suite after restacking — 14/14 passed.
- `pnpm -r typecheck` — passed for all applicable workspaces.
- `pnpm build` — passed, including runner binary, server, UI, and
workspace packages.
- `pnpm test:run` — attempted; the local host reproduced unrelated
workspace/Postgres and port-exposure failures in unchanged server
suites. The changed runner contract suites pass, and the repository's
serialized/sharded GitHub checks remain authoritative for those
host-sensitive suites.
- Prettier, rustfmt, generated-source drift checks, and `git diff
--check` — passed.
- `pnpm-lock.yaml` is unchanged.
## Risks
The main risk is allowing schema and generated language inventories to
diverge. Build and typecheck now fail on any drift. The sidecar
implementation and third-party ACPX packages are deliberately absent, so
this PR cannot alter runtime behavior or expand the production attack
surface.
## Model Used
OpenAI Codex with GPT-5 and repository tool use.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs
- [x] I have described the issue and expected behavior in this PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal task
identifier
- [x] I have run the affected tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have documented the compatibility and security boundary
- [ ] All applicable GitHub Actions are green
- [ ] Greptile is 5/5 with every actionable comment resolved
- [x] I will address all review findings before requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The hidden native coordinator already computes a run-scoped semantic
tool projection.
> - The durable Codex backend now accepts and enforces that projection.
> - The server did not include the projection in its `run.prepare`
command.
> - Codex therefore received no production semantic tools even when the
server authorized them.
> - This pull request adds the deterministic wire projection and sends
it to runnerd.
> - The benefit is one fail-closed authorization catalog from the server
through Codex.
## Linked Issues or Issue Description
Refs #12384
**What existing behavior does this improve?**
This improves the existing flagged Paperclip Runner Codex path.
**Current behavior**
The server creates a run-scoped list of authorized read tools. It does
not pass that list to runnerd, so the production Codex session starts
with no tools.
**Proposed behavior**
The server maps the authorized definitions to the versioned runner
contract. It computes a cross-language catalog digest. It includes that
immutable contract in `run.prepare`.
**Reason and benefit**
Runnerd and the server now enforce the same catalog identity. Unknown,
duplicate, changed, or malformed tool contracts fail before Codex can
use them.
**Breaking changes**
None. Direct adapters are unchanged. A native run with an empty server
projection still starts with no dynamic tools.
## What Changed
- Add a deterministic semantic-definition to runner-authorization
projection.
- Match the Rust canonical digest with a shared test vector.
- Include the server coordinator projection in the native Codex
`run.prepare` command.
- Extend the native Codex vertical slice to require and execute a
semantic tool.
- Verify the production prepare payload in a host-independent server
test.
## Verification
- `pnpm --filter @paperclipai/paperclip-runner test:typescript` (354
tests pass)
- `pnpm --filter @paperclipai/server exec vitest run
src/services/native-runtime/native-codex-runner.test.ts`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm -r typecheck`
- `pnpm build`
- The embedded-Postgres vertical slice is present for CI. This local
host reports that embedded Postgres is unavailable, so Vitest skips that
host-dependent test locally.
- Confirmed that the PR changes 7 files against
`runner-codex-durable-tools`.
- Confirmed that `pnpm-lock.yaml` is unchanged.
## Risks
The main risk is a catalog digest mismatch between TypeScript and Rust.
Both implementations use canonical JSON. They share the same fixed
digest vector. Runnerd also recomputes the digest and rejects a
mismatch.
The rollout flag and the existing native runtime selection rules remain
unchanged.
## Model Used
OpenAI Codex with GPT-5 and repository tool use.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip Runner gives an agent a durable execution boundary.
> - The Codex transport can now advertise a run-scoped semantic tool
catalog.
> - The durable backend did not yet persist tool calls or correlate
their results.
> - A restart could therefore lose the boundary between a provider call
and a Paperclip action.
> - This pull request binds authorized calls, durable events, results,
replay, and cancellation.
> - The benefit is safe semantic tool recovery without duplicate
Paperclip actions.
## Linked Issues or Issue Description
Refs #12382
**What existing behavior does this improve?**
This improves the durable Codex provider backend in
`@paperclipai/paperclip-runner`.
**Current behavior**
The Codex transport can project authorized dynamic tools. The durable
backend rejects their calls because it cannot persist and recover their
identities.
**Proposed behavior**
The durable backend records each authorized call before it emits the
semantic input event. It records each result before it sends the result
to Codex. It reconciles exact provider replays without another Paperclip
action.
**Reason and benefit**
This order prevents duplicate semantic actions after a process restart.
It also keeps unauthorized, changed, oversized, and late calls closed.
**Breaking changes**
None. A run without an authorized tool catalog still starts Codex with
no dynamic tools.
## What Changed
- Persist the authorized tool catalog with the Codex provider state.
- Emit correlated and redacted semantic input, reconciliation, and
result events.
- Reconcile exact pending and completed calls after a provider restart.
- Reject catalog drift, changed replay input, malformed results, and
unauthorized operations.
- Complete pending tool calls with a durable failure when a turn stops.
- Bound retained tool values and validate recovered state before
provider startup.
- Bind production runner events to the active run, session, turn, and
item identities.
## Verification
- `cargo fmt --all -- --check`
- `cargo test --workspace`
- `pnpm -r typecheck`
- `pnpm build`
- Confirmed that the PR changes 9 files against
`runner-codex-dynamic-tools`.
- Confirmed that dependency installation did not change
`pnpm-lock.yaml`.
## Risks
The main risk is a mismatch between recovered provider state and the
controller tool catalog. Recovery validates the complete catalog and its
digest before Codex starts. The backend persists a call before it emits
work and persists a result before it returns the result to Codex.
This PR does not enable the server adapter or change any direct adapter
path.
## Model Used
OpenAI Codex with GPT-5 and repository tool use.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The native runner keeps provider execution separate from Paperclip
tool authority.
> - The Rust authorization bridge can now validate a run-scoped semantic
tool catalog.
> - Codex still needs a bounded protocol adapter for that validated
catalog.
> - The adapter must advertise the same catalog after provider recovery.
> - The adapter must reject unadvertised calls and mismatched results.
> - This pull request adds that Codex-only transport boundary.
> - The benefit is a fail-closed provider bridge that remains inactive
until durable wiring supplies tools.
## Linked Issues or Issue Description
**Subsystem affected**
`packages/paperclip-runner` and its Rust Codex app-server provider.
**Problem or motivation**
The native runner has a validated semantic tool catalog, but the Rust
Codex provider cannot project that catalog or correlate Codex tool calls
with Paperclip results. A resumed Codex thread also needs the same
run-scoped catalog.
**Proposed solution**
Add an explicit Codex start path that accepts validated tools. Send
those tools through `dynamicTools` for both thread start and thread
resume. Validate thread, turn, call, operation, result identity, and
size before data crosses the provider boundary.
**Roadmap alignment**
This work supports the shipped governed MCP Tool Gateway and
self-healing run milestones. It does not add a user-facing adapter or
enable the experimental runner.
## What Changed
- Add a Codex dynamic-tool projection for explicit authorized tool sets.
- Advertise the same tool set on new and resumed provider threads.
- Correlate bounded Codex tool calls and Paperclip semantic results.
- Reject calls outside the active thread or turn, unadvertised tools,
reused request identities, mismatched results, and oversized values.
- Keep the current durable backend on the zero-tool path until the next
wiring change.
- Extend the fake Codex app server and integration tests for success,
recovery, denial, and correlation behavior.
## Verification
- `cargo test --manifest-path
packages/paperclip-runner/runner/Cargo.toml --workspace`
- `cargo clippy --manifest-path
packages/paperclip-runner/runner/Cargo.toml --workspace --all-targets --
-D warnings -A clippy::manual_is_multiple_of -A
clippy::filter_map_bool_then`
- `pnpm -r typecheck`
- `pnpm build`
- The PR changes 4 files relative to `runner-rust-semantic-tool-bridge`.
## Risks
Low activation risk. The existing `CodexProvider::start` path still
supplies an empty tool set. The durable backend treats a tool event as
an error until a later PR attaches the durable authorization bridge. The
new transport rejects unknown tools and invalid provider bindings.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex with GPT-5 and repository tool use.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The runner package has a reviewed semantic action catalog and
dispatcher
> - The Rust runner process needs the same fail-closed authorization
boundary
> - Provider calls must remain correlated and idempotent across durable
recovery
> - Input and result values must satisfy the authorized operation
schemas
> - This pull request adds a package-local durable semantic tool bridge
> - It does not advertise tools to Codex or enable the Paperclip Runner
adapter
## Linked Issues or Issue Description
**Subsystem affected**
`packages/paperclip-runner/runner` semantic tool authorization and
correlation.
**Problem or motivation**
The Rust runner needs a durable representation of the run-scoped tools
that the control plane authorizes. It must reject unknown operations,
catalog drift, invalid values, and conflicting duplicate calls or
results before a provider integration can use those tools.
**Proposed solution**
Add a serialized provider tool bridge. Validate the authorized catalog
and its JSON Schemas. Validate each call and result. Keep pending and
completed identities so retries are idempotent and conflicts fail
closed.
**Alternatives considered**
Trusting provider arguments would bypass the run-scoped catalog.
Validating only in TypeScript would leave the Rust process without a
recovery-safe authorization boundary. Adding provider behavior in this
pull request would make the review unit too broad.
**Roadmap alignment**
This adds a package-local safety boundary for the Codex-first runner
path. It does not enable a new adapter or change an existing direct
adapter path.
## What Changed
- Added the versioned authorized-tool, pending-call, and result
contracts.
- Added canonical SHA-256 catalog binding and drift rejection.
- Added JSON Schema compilation and input and response validation.
- Added duplicate-call and duplicate-result idempotency with conflict
rejection.
- Added bounds for catalogs, schemas, values, and retained call
identities.
- Added the Rust `jsonschema` dependency and its Cargo lock entries.
- Added focused tests for authorization, recovery, envelopes, bounds,
and conflicts.
## Verification
- `cargo fmt --manifest-path packages/paperclip-runner/runner/Cargo.toml
--all -- --check`
- `cargo test --manifest-path
packages/paperclip-runner/runner/Cargo.toml -p paperclip-runner-core`
(64 tests)
- `cargo clippy --manifest-path
packages/paperclip-runner/runner/Cargo.toml -p paperclip-runner-core
--all-targets -- -D warnings -A clippy::manual_is_multiple_of -A
clippy::filter_map_bool_then`
- `pnpm -r typecheck`
- `pnpm build`
- The repository test runner also reached unrelated server worktree
suites. Those suites fail on the current macOS worktree with database
deadlocks and filesystem fixture assumptions. This pull request does not
change those files. The applicable GitHub checks remain the handoff
authority.
## Risks
The main risks are accepting a tool that the run did not authorize and
replaying a conflicting provider result. The bridge validates the
catalog, operation identity, JSON Schema, call identity, and result
identity before it changes durable state. The new Cargo dependency is
package-local. This pull request changes no GitHub workflow and no pnpm
lockfile.
## Model Used
OpenAI Codex with GPT-5 and repository tool use.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The experimental Paperclip Runner can execute a guarded Codex run
and persist provider-neutral events.
> - The task page still reads direct-adapter transcripts and cannot
present those native events.
> - Structured runner questions must also use the existing task
interaction experience.
> - Runtime selection must use the persisted run mode, not an adapter
name or a current feature flag.
> - This pull request projects native events and questions into the
existing task thread.
> - Direct adapters keep their existing transcript, composer,
interaction, and finalization paths.
> - The benefit is a complete native Codex task thread without a
behavior change for existing adapters.
## Linked Issues or Issue Description
Refs #12202. This pull request replaces that stale implementation on
current `master`.
**What happened?**
The server persists native runner events and structured input requests.
The task page only consumes direct-adapter transcripts. A native run
therefore cannot present a complete transcript, usage, or question flow
through the normal task experience.
**Expected behavior**
Native runs project persisted provider-neutral events into the existing
task thread. Native structured questions use the existing interaction
card. Direct adapters retain their current behavior.
**Steps to reproduce**
1. Enable the experimental runner.
2. Start a native Codex run that emits progress, usage, a structured
question, and a final reply.
3. Open the task page.
4. Observe that the direct-adapter transcript path cannot project the
native event records.
**Paperclip version or commit**
`master` at `67f9867bc`.
## What Changed
- Add the canonical structured-question validator and shared contract
exports.
- Materialize native input requests as existing task interactions.
- Validate native answers and deliver them through the durable
question-response receipt.
- Resume the original PRP request with an idempotent `request.resolve`
command.
- Project native messages, tool activity, cumulative usage, and final
replies into the existing transcript model.
- Propagate persisted `runtimeMode` to the task page and select native
handling only for `runtimeMode: "native"`.
- Expire pending interactions through the shared issue service on every
terminal transition, including decisions, stalled reviews, tree control,
and pipeline retry cleanup.
- Queue native run cancellation while a transaction is open and execute
it only after the owning transaction commits.
- Keep nonterminal and non-runner issue paths on their existing service
call shapes and behavior.
## Verification
- `pnpm --filter @paperclipai/server typecheck` — passed, including the
Rust runner release build and protocol/catalog drift gates.
- Focused native-thread and lifecycle suites — 18 files and 481 tests
passed during review.
- `issue-execution-policy-routes.test.ts` — 19/19 passed after the final
transactional-queue expectation update.
- `issue-agent-mutation-ownership-routes.test.ts` — 87/87 passed in the
final isolated compatibility rerun.
- GitHub Actions — policy, build, canary, typecheck/release registry, 5
serialized server shards, 8 general-test shards, 3 browser shards, and
both aggregate gates passed on `7793f3193`.
- Security — Snyk, Socket Project Report, Socket PR Alerts, and
Superagent passed.
- Greptile — 5/5 on `7793f3193`; all actionable review threads resolved.
- `git diff --check` — passed.
- Diff against `master`: 44 files.
## Compatibility Boundary
- Native transcript polling only runs when the persisted run reports
`runtimeMode: "native"`.
- Missing or legacy runtime modes continue through
`useLiveRunTranscripts`.
- Legacy questions keep the existing optional free-text choice.
- Native closed select sets can suppress that legacy fallback.
- Terminal cleanup uses the same issue service for native and legacy
interactions; only a bound native question schedules a native run
cancellation.
- Native cancellation happens after transaction commit, so failed or
rolled-back writes do not cancel a still-valid run.
- The durable delivery service checks the original native request before
it considers a continuation run.
- This pull request adds no migration, dependency, workflow, manifest,
or lockfile change.
## Risks
The main risk is routing a direct-adapter task through native handling
or changing terminal issue behavior. The implementation selects the
native path only from persisted runtime facts, retains the existing
nonterminal call shape, and schedules native cancellation only for a
validated bound native question after commit. Focused and
repository-wide tests cover both paths. Native requests remain bound to
the company, issue, run, and agent; answers are validated, durable, and
idempotent across reconnects.
## Model Used
OpenAI Codex, GPT-5 family. The client does not expose the exact
deployment ID or context window. Agentic reasoning, tool use, and code
execution were enabled.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run the affected local tests and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated the compatibility notes for this change
- [x] I have considered and documented risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I addressed all Greptile and reviewer comments before requesting
merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Paperclip Runner now has protocol, provider, tool, package,
persistence, and hidden server boundaries.
> - The server still cannot select that path for a real agent heartbeat.
> - A new runtime must not change any existing direct adapter.
> - An experimental runtime must fail closed when its rollout flag is
off.
> - This pull request adds one guarded Codex vertical slice through
runnerd.
> - The benefit is a production-built runner path that users cannot
start by default.
## Linked Issues or Issue Description
Refs #11962
Refs #12111
Refs #12169
Refs #12176
**Subsystem affected**
Cross-cutting. The change affects the runner package, server
orchestration, shared settings, and adapter configuration UI.
**Problem or motivation**
The hidden PRP coordinator cannot execute a real heartbeat. The
application also needs an explicit rollout boundary before it can expose
the experimental runner. Existing direct adapters must keep their
current execution and finalization behavior.
**Proposed solution**
Add `paperclip_runner` as a Codex-only adapter behind the default-off
`enableNativeRunner` instance flag. Select the native runtime only for
that adapter. Persist the run binding before runnerd starts. Wait for
the durable PRP result and terminal event. Resume the real Codex
provider thread on later heartbeats. Keep persisted native runs readable
and recoverable after the flag changes.
**Alternatives considered**
The server could route `codex_local` through runnerd. That option would
change an existing adapter and weaken rollback safety. The server could
expose all providers now. That option would add unreviewed provider
behavior. The build could depend on a prebuilt runner binary. That
option would make source builds architecture-dependent and difficult to
verify.
**Roadmap alignment**
This work supports the shipped enforced-outcomes, governed-tool, and
self-healing-run milestones. It does not add a new roadmap surface. It
is the guarded execution step after the merged hidden runner boundaries.
**Additional context**
This is the next replacement for the closed large runner pull request.
Task-thread presentation remains a separate follow-up so this change can
preserve the current direct-adapter UI.
## What Changed
- Add `paperclip_runner` as an explicit Codex-only adapter.
- Add the default-off `enableNativeRunner` instance flag.
- Reject fresh create, hire, import, switch, and execution requests
while the flag is off.
- Allow edits to persisted runner agents while the flag is off.
- Recover an already persisted native run even after the flag is
disabled.
- Keep every built-in direct adapter on its existing runtime path.
- Persist an immutable native run binding and revisioned completion
contract before runnerd starts.
- Execute server to PRP to runnerd to Codex to server through the hidden
coordinator.
- Validate the durable result against the terminal event and exact
completion criteria before finalization.
- Preserve the Codex provider thread ID and use `thread/resume` on the
next heartbeat.
- Strip unsupported Codex configuration fields from the experimental
adapter.
- Build a target-native release runner binary from source and vendor it
into the server distribution.
- Install Rust only in the Docker build stage. Do not add a workflow or
lockfile change.
- Stop the runner process group on completion, cancellation, and forced
shutdown.
## Verification
- Run `pnpm --filter @paperclipai/paperclip-runner check:all`. All 69
TypeScript tests and 58 Rust tests pass. Protocol, conformance, replay,
formatting, and generated-file checks pass.
- Run the 12 focused adapter, settings, runtime-selection, coordinator,
direct-isolation, and real Codex integration test files. All 186 tests
pass.
- The real integration test uses PostgreSQL, HTTP, WebSocket, runnerd,
and a fake Codex app server. It proves one `thread/start` followed by
one `thread/resume`.
- Run `pnpm -r typecheck`.
- Run `pnpm build`.
- Run `pnpm check:token-gates`.
- Build the Docker `build` target from a clean context. Confirm that the
server distribution contains an executable `paperclip-runnerd` built
with Debian Rust 1.85.
- Start the server through the source-mode tsx entry point with the
package `dist` directory absent. Confirm the vendor shim resolves source
exports and the server boots.
- Run `pnpm test:run` twice. On this macOS host, 405 files pass and 1
file skips. Eight untouched workspace and loopback tests fail because
macOS resolves `/tmp` and `/var` through `/private` and because
PID-derived test ports exceed 65535. Linux CI must pass the full suite.
- Confirm that the diff contains 52 files. Confirm that it contains no
`.github` or `pnpm-lock.yaml` change.
## Risks
- The feature flag is off by default. A fresh native start fails with a
stable error while the flag is off.
- A persisted native run remains recoverable after the flag changes.
This prevents rollout changes from corrupting recorded work.
- Only local Codex execution is accepted. Other providers and remote
work modes fail closed.
- Existing direct adapters do not start runnerd, create native rows, use
native status arbitration, or enter native finalization.
- The runner receives its one-use bootstrap ticket through the child
environment. The server does not put the ticket in command arguments or
logs.
- The server validates the company, task, agent, run, runner, session,
completion contract, result, and terminal binding before it accepts
completion.
- The build compiles a target-native Rust binary. Cross-platform release
packaging remains a later concern. Source builds and Docker builds
compile for their current target.
- Docker needs enough build memory for the existing server TypeScript
compile. The Docker build stage sets a 4 GB V8 heap limit.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex with GPT-5. The exact deployment ID and context-window
size are not exposed. The model used agentic reasoning, repository
tools, code execution, and test execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and applicable tests pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The package-local runner now has a durable PRP transport, but it
cannot execute a real provider.
> - The first provider must preserve PRP identities while using Codex
native thread and turn identities.
> - Recovery must resume the same Codex thread without starting a
duplicate turn.
> - Provider output must become bounded and provider-neutral before it
crosses PRP.
> - Semantic tools must remain unavailable until the catalog and
authorization layers exist.
> - This pull request adds the Codex provider bridge inside the runner
package only.
> - The benefit is a reviewable provider slice with no server or
user-facing behavior change.
## Linked Issues or Issue Description
**Subsystem affected**
Cross-cutting. This pull request extends private provider infrastructure
in `packages/paperclip-runner`.
**Problem or motivation**
The durable runner from #12100 has no production provider. It cannot
start Codex app-server, map its events, cancel or steer a turn, deliver
a structured question, or recover a native thread after process restart.
**Proposed solution**
Add a supervised Codex app-server transport and a normalized runner
backend. Persist the Codex thread and active turn identities. Resume and
inspect the exact thread after restart. Convert supported notifications
into bounded PRP events. Keep the dynamic tool inventory empty.
**Alternatives considered**
The combined runner branch implements several providers, semantic tools,
server coordination, and UI integration together. That change is too
large for one review unit. Reusing the direct `codex_local` adapter
would also couple this package layer to the existing server execution
path.
**Roadmap alignment**
This work supports the governed tools and self-healing run direction in
`ROADMAP.md`. It does not add a server endpoint, runtime adapter,
rollout flag, or user-facing behavior.
**Additional context**
Refs #12100 and #11962. Pull request #12100 was squash-merged first.
This branch starts at the resulting `master` commit. Its current delta
is 16 files.
## What Changed
- Added a Codex-only app-server process transport with bounded JSONL
frames and buffered notifications.
- Added strict provider descriptor validation for the Codex driver,
working directory, launch arguments, model, instructions, and
non-interactive approval policy.
- Started new Codex threads with an empty dynamic tool inventory and the
named workspace-only permission profile.
- Added native turn start, steering, interruption, cancellation, thread
reads, and structured question responses.
- Added thread and active-turn binding checks for provider requests and
notifications.
- Added provider-neutral normalization for session, turn, item, plan,
usage, tool execution, notice, and structured input events.
- Bounded and redacted provider text and process output before durable
persistence.
- Added private atomic provider state for the descriptor, thread ID,
account session ID, active turn ID, and unacknowledged normalized
events.
- Added exact-thread recovery through `thread/resume` and `thread/read`.
Recovery does not issue another `turn/start` for an active turn.
- Preserved active native turn identity across unexpected provider exit
and reconciled it before later start, interrupt, or snapshot commands.
- Added stable provider-event identities, per-event durable commit and
acknowledgement, and a bounded fingerprint receipt journal that prevents
duplicate delivery across outbox and provider-ack crash windows.
- Extended the durable command executor with provider event polling and
explicit process shutdown on stop, suspend, revocation, lease expiry,
and runtime expiry.
- Preserved completed shutdown behavior when the command result is
replayed after a disconnect.
- Added a fake Codex app-server and integration tests for response
buffering, structured questions, interruption, provider exit,
unacknowledged-event recovery, durable resume, and duplicate-turn
prevention.
- Added a focused `test:codex` package command for the provider
integration suite.
- Kept server code, UI code, other providers, semantic catalogs, tool
authorization, and production runtime selection out of this pull
request.
## Verification
- `pnpm --filter @paperclipai/paperclip-runner check:all` passes.
- TypeScript contract tests pass: 8 Node tests and 44 Vitest tests.
- Rust tests pass: 43 unit tests, 5 Codex integration tests, 3 public
durable-recovery tests, 2 local-runner tests, and 3 process-supervisor
tests.
- Rust conformance and replay parity checks pass against the shared PRP
fixtures.
- `cargo clippy --workspace --all-targets -- -A
clippy::filter-map-bool-then -D warnings` passes. The narrow allow
covers an unchanged replay implementation from the preceding contract
pull request.
- `pnpm -r typecheck` passes.
- `pnpm build` passes.
- `pnpm check:token-gates` passes.
- `git diff --check` passes.
- The delta against `master` is 16 files. The package lockfile is
unchanged. The PR workflow generates its temporary lockfile artifact
from the changed package manifest.
- `pnpm test:run` completed locally with 4,690 passing tests, 19 skipped
tests, and 26 failures in 8 unchanged server test files. The failures
reproduce the established local macOS path-alias, listener, port-range,
and workspace-runtime baseline. No changed-file test failed. Linux CI
remains the repository handoff authority.
- Browser and migration tests are not applicable because this pull
request changes no server, UI, database, or migration file.
- The full Linux PR workflow passes. One unchanged heartbeat recovery
test timed out on the first pass and passed on the failed-only rerun;
the aggregate `verify` gate is green.
- Greptile is 5/5 on the final commit. All four review threads are
resolved.
## Risks
Production behavior is unchanged because no server code starts this
provider. The main risks are a provider process escape, cross-thread
event confusion, secret leakage, duplicated turns, duplicated or lost
provider events, lost questions, and unsafe recovery. Process-group
supervision, identity binding, private bounded state, redaction, durable
command replay, retained event acknowledgements, bounded durable
receipts, exact-thread reconciliation, and integration tests cover these
risks. Semantic tools remain undiscoverable in this layer.
I checked `ROADMAP.md`. This change is private provider infrastructure
for planned control-plane work. It does not duplicate a shipped or
public product surface.
## Model Used
OpenAI Codex with GPT-5 was used. The exact serving model ID and context
size were not exposed. The model used high reasoning, repository tools,
GitHub tools, and local code execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The package-local runner can supervise a local process, but it
cannot yet survive a broken controller connection.
> - A production transport must authenticate both peers without putting
the bootstrap secret on the wire.
> - Commands and events must remain bounded, ordered, and recoverable
across reconnects and crashes.
> - Retrying an uncertain side effect is unsafe, so indeterminate
outcomes must fail closed instead of running twice.
> - This pull request adds those transport and recovery guarantees
inside the runner package only.
> - The benefit is a durable PRP boundary that can be reviewed before
any provider or server integration exists.
## Linked Issues or Issue Description
**Subsystem affected**
Cross-cutting. This pull request extends private transport
infrastructure in `packages/paperclip-runner`.
**Problem or motivation**
The local runner introduced by #12095 has no authenticated network
handshake, durable outbox, reconnect lease, cumulative acknowledgement,
or crash-safe command journal. A dropped connection could otherwise lose
an event or tempt a controller to repeat a side effect whose outcome is
unknown.
**Proposed solution**
Add an authenticated PRP v1 WebSocket transport, encrypted frames,
lease-based reconnects, a bounded durable event outbox, cumulative
acknowledgements, and an idempotent command journal. Preserve pending
commands before execution and classify the crash window as indeterminate
so an uncertain side effect is never repeated automatically.
**Alternatives considered**
The combined runner branch implements transport together with Codex,
semantic tools, and server coordination. That change is too large for
one review unit. Keeping transport in memory would make reconnect and
crash recovery unverifiable. Re-running a pending command after restart
would weaken the at-most-once side-effect boundary.
**Roadmap alignment**
This work supports the governed tools and self-healing run direction in
`ROADMAP.md`. It does not add a production provider, server endpoint,
adapter, feature flag, or user-facing behavior.
**Additional context**
Refs #12095 and #11962. Pull request #12095 was squash-merged first.
This branch has been rebased onto the resulting `master` commit, and its
current delta is 13 files.
## What Changed
- Added a loopback-only WebSocket connection policy with one-time DNS
resolution and pinned reconnect addresses.
- Added an HMAC mutual-authentication handshake that never sends the
bootstrap ticket over the socket.
- Added AES-256-GCM secure frames with per-direction keys, monotonic
counters, and session-bound authenticated data.
- Added one-use bootstrap-ticket handling and lease-based reconnect
validation with expiry, revocation, and epoch checks.
- Added a private, symlink-resistant state directory with atomic,
synchronized state replacement.
- Added a bounded durable event outbox, priority-zero reserve,
cumulative acknowledgements, and reconnect replay of only the
unacknowledged suffix.
- Added a bounded command journal with contiguous sequence enforcement,
persistent results, and deterministic duplicate responses. Duplicate
replay requires a SHA-256 match over the complete canonical command.
- Persisted commands before their effects. A crash after persistence but
before result storage returns an indeterminate terminal result and does
not execute the command again.
- Migrated pre-fingerprint command journals by compacting through their
persisted controller cursor. Legacy redelivery fails closed instead of
reconstructing an incomplete identity or repeating an uncertain effect.
- Added strict limits and validation for frames, state, results, outbox
entries, command history, and redacted diagnostics.
- Added a transport-only `paperclip-runnerd --connect-url` mode. It
handles lifecycle commands and rejects provider commands because no
provider is present in this pull request.
- Added a full disconnect-before-ack fault test that reconnects with the
lease, replays identical command and event state, and proves the effect
ran once.
- Kept provider transports, semantic tools, server integration, and
production runtime selection out of this pull request.
## Verification
- `pnpm --filter @paperclipai/paperclip-runner check:all` passes.
- TypeScript contract tests pass: 8 Node tests and 44 Vitest tests.
- Rust tests pass: 33 unit tests, 3 public durable-recovery integration
tests, plus the existing 2 local-runner and 3 process-supervisor tests.
- The disconnect-before-ack, lease reconnect, duplicate command,
malformed state, unknown command, bounds, and crash-window tests pass.
- Rust conformance and replay parity checks pass against the shared PRP
fixtures.
- `cargo clippy --workspace --all-targets -- -A
clippy::filter-map-bool-then -D warnings` passes. The narrow allow
covers an unchanged replay implementation from the preceding contract
pull request.
- `pnpm -r typecheck` passes.
- `pnpm build` passes.
- `pnpm check:token-gates` passes.
- `git diff --check` passes.
- The delta against `master` is 13 files. The package lockfile is
unchanged.
- `pnpm test:run` completed locally with 4,686 passing tests, 19 skipped
tests, and 30 failures in 8 unchanged server test files. The failures
reproduce the established local macOS path-alias, listener, port-range,
and workspace-runtime baseline. No changed-file test failed; Linux CI
remains the repository handoff authority.
- Storybook visual regression is not applicable because this pull
request changes no UI or story files.
## Risks
Production behavior is unchanged because no server code starts or
connects to this transport. The main risks are secret disclosure, forged
or replayed frames, state corruption, unbounded disk growth, duplicated
side effects, and incorrect recovery. Mutual authentication, encrypted
counter-bound frames, private atomic state, explicit bounds, cumulative
acknowledgements, a durable command journal, fail-closed indeterminate
recovery, and fault-injection tests cover these risks.
I checked `ROADMAP.md`. This change is private transport infrastructure
for planned control-plane work. It does not duplicate a shipped or
public product surface.
## Model Used
OpenAI Codex with GPT-5 was used. The exact serving model ID and context
size were not exposed. The model used high reasoning, repository tools,
GitHub tools, and local code execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip Runner needs a small local process model before it can
connect to a production provider or server.
> - The TypeScript PRP contracts now define the expected replay
behavior.
> - A second language implementation must produce the same result from
the same fixtures.
> - Local child processes also need bounded input, bounded output, and
complete descendant cleanup.
> - This pull request adds a package-local Rust runner, a scripted fake
harness, and deterministic parity checks.
> - The benefit is a testable process boundary with no production
Paperclip behavior change.
## Linked Issues or Issue Description
**Subsystem affected**
Cross-cutting. This pull request adds private test infrastructure to
`packages/paperclip-runner`.
**Problem or motivation**
The PRP contracts have no second implementation on `master`. There is
also no small harness that can prove process cleanup, command
idempotency, terminal reconciliation, or bounded JSONL handling without
a production provider.
**Proposed solution**
Add a minimal Rust workspace. Add a local runner process, a scripted
fake harness, a bounded process supervisor, and Rust conformance and
replay checks. Keep all binaries package-local. Do not connect them to
the Paperclip server.
**Alternatives considered**
The combined runner branch includes provider transports, durable
networking, SDKs, labs, and server behavior. That change is too large
for this review unit. A TypeScript-only harness would not test
cross-language contract parity.
**Roadmap alignment**
This work supports the governed tools and self-healing run direction in
`ROADMAP.md`. It does not add a user-facing runtime, adapter, endpoint,
or rollout flag.
**Additional context**
Refs #12091 and #11962. Pull request #12091 was merged before this
branch opened. This branch is based on the current `master`. Its delta
is 25 files.
## What Changed
- Added a minimal locked Rust workspace with only `serde` and
`serde_json` dependencies.
- Added a package-local `paperclip-runnerd` local mode and a scripted
fake harness.
- Added bounded controller input, harness input, subprocess output
queues, line sizes, log retention, script sizes, script steps, and
command history.
- Added contiguous controller and harness sequence checks and
equivalent-command replay handling.
- Added process-group supervision that cleans up child processes and
remaining descendants after forced or natural harness exit.
- Added runner-owned terminal reconciliation for success, failure,
interruption, cancellation, controller closure, and protocol failure.
- Added Rust conformance output and deterministic replay summaries for
the shared PRP fixtures.
- Added fake scripts for success, failure, interruption, interaction,
duplicate terminal output, process cleanup, and oversized output.
- Added package scripts and documentation for the Rust and
cross-language checks.
- Kept provider transport, server integration, semantic tools, and
production runtime selection out of this pull request.
## Verification
- `pnpm --filter @paperclipai/paperclip-runner check:all` passes.
- TypeScript contract tests pass: 8 Node tests and 44 Vitest tests.
- Rust tests pass: 20 unit tests, 2 local-runner tests, and 3
process-supervisor tests.
- The Rust conformance and replay parity checks pass against the shared
fixtures.
- The natural-exit and forced-exit tests confirm that the harness and
its worker process are stopped.
- The oversized-frame test confirms that a harness frame above the
configured limit is rejected.
- `pnpm -r typecheck` passes after the final rebase to `master`.
- `pnpm build` passes after the final rebase to `master`.
- `pnpm check:token-gates` passes.
- `git diff --check` passes.
- The delta against `master` is 25 files. The package lockfile is
unchanged.
- `pnpm test:run` completed locally with 4,686 passing tests, 19 skipped
tests, and 30 failures in 8 unchanged server test files. The failures
are local macOS path-alias, listener, port-range, and workspace-runtime
baseline failures. No changed-file test failed, and every applicable
Linux CI shard passes.
- Storybook visual regression skipped intentionally because this pull
request changes no UI or story files.
## Risks
Low production risk. No server code invokes the new binaries. The
package remains private. The main risks are process leaks, unbounded
local input, and cross-language drift. Bounded queues and sizes,
process-group cleanup tests, fixture manifests, and parity checks cover
these risks.
I checked `ROADMAP.md`. This change is private test infrastructure for
planned control-plane work. It does not duplicate a shipped or public
product surface.
## Model Used
OpenAI Codex with GPT-5 was used. The exact serving model ID and context
size were not exposed. The model used high reasoning, repository tools,
GitHub tools, and local code execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>