## Thinking Path
Paid cells now reuse the AWS image's system Chrome, but Playwright video
recording still resolves its revision-pinned FFmpeg helper from the
Playwright cache. Run 33875618534 proved Chrome qualification succeeds
and then failed before provider startup because that helper was absent.
The same run also exposed that generic lock repair can churn unrelated
package platform metadata, so the automated repair paths need
resolution-only regeneration rather than lockfile-only metadata refresh.
## What Changed
- install Playwright FFmpeg only on the AWS/system-Chrome path
- retry the small helper installation up to three times before provider
secrets are exposed
- keep the GitHub-hosted Chromium fallback unchanged
- bind static coverage to the exact FFmpeg step block and its pre-secret
ordering
- add pnpm `--resolution-only` to all four automated lock-repair paths
while retaining full transitive resolution
- require resolution-only repair in the shared workflow regression
The actual generated lockfile correction remains bot-owned by PR #12828
and is intentionally not committed here.
## Verification
- `node --test
.github/scripts/tests/lockfile-refresh-workflows.test.mjs`
- `actionlint -ignore SC2012` on all modified workflows
- focused Prettier checks
- `git diff --check`
- prior run 33875618534: system Chrome 151 qualified; missing Playwright
FFmpeg was the sole cell startup failure
## Risks
Low. The new network operation is limited to Playwright's pinned FFmpeg
payload, happens before paid credentials are exposed, and leaves the
hosted-runner path unchanged. Resolution-only is still a full
dependency-resolution pass, unlike lockfile-only, while avoiding
unrelated current-platform metadata churn.
## Model Used
GPT-5
## 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 validates every change through an immutable reusable PR
workflow.
> - That caller still pinned a revision that ran pnpm setup before Node
setup.
> - The implementation fix in #12808 is therefore present on master but
inactive for ordinary PR CI.
> - Advancing the immutable caller pin activates the already tested
Node-first workflow.
> - A focused contract prevents the caller from silently returning to
the old revision.
> - The benefit is a faster PR feedback loop without changing product
code or secret boundaries.
## Linked Issues or Issue Description
Refs #12808
## What Changed
- Pin ordinary PR CI to trusted workflow revision
`a0a78ee60946a5f79f85b2bd0584fc766fae43bb`.
- Assert that the reusable workflow call is canonical, unique, and
SHA-pinned to that audited revision.
## Verification
- Focused workflow security test: 8/8.
- Prettier passed.
- Actionlint passed.
- `git diff --check` passed.
## Risks
Low risk. The change only advances an immutable reusable-workflow pin to
a revision whose full ordinary CI and security checks passed. Product
code and credentials are unchanged.
## Model Used
OpenAI Codex GPT-5 with agentic reasoning 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
- [x] I have linked the related public PR
- [x] I have not referenced internal issue links
- [x] My branch name describes the change
- [x] I have run focused tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have considered and documented risks
## Thinking Path
The trusted workflows currently invoke `pnpm/action-setup@v6` before
installing the repository Node version. On hosts whose ambient Node is
older than 22.13, the action downloads standalone `@pnpm/exe`, which has
repeatedly taken several minutes. Supplying Node 24 first lets the same
pinned pnpm action use its normal Node-backed path.
## What Changed
- install Node 24 before every trusted `pnpm/action-setup` invocation
- preserve the existing pnpm-store cache setup, pinned actions,
telemetry suppression, conditions, and secret boundaries
- enforce ordering, modern Node, condition parity, and cache counts in
the workflow security contract
## Verification
- workflow security: 7/7
- Prettier
- actionlint (excluding one pre-existing SC2129 in an untouched Daytona
shell block)
- `git diff --check`
## Risks
Low. Product code, providers, paid-runner selection, credentials, and
pnpm version are unchanged. Jobs that restore pnpm cache run
`setup-node` a second time after pnpm becomes available; the first setup
is deliberately cache-free.
## Model Used
Codex (GPT-5)
## Thinking Path
Trusted workflow-dispatch runs execute an authorized target SHA, but
GitHub context still describes the default-branch workflow revision.
Retained paid results and artifact names were therefore labeling
target-branch executions as master. The workflow must explicitly pass
its authorized target coordinates to target code and trusted reporting.
## What Changed
- emit the canonical authorized target ref alongside the immutable
target SHA
- pass those coordinates to paid cells and the trusted report
- name shared build/provider artifacts with the target SHA rather than
workflow SHA
- add workflow-security coverage for all trusted provenance wiring
## Verification
- focused workflow-security tests: 6/6 passed
- Prettier and git diff checks passed
- run 33823252706 independently proved the pre-fix defect: functionally
green target cells were retained as master SHA 0ad180b85 instead of
feature SHA 33c7646d3
## Risks
The execution checkout and secret boundary were already pinned
correctly; this changes retained attribution and artifact labels only.
Target-side report code on PR #12769 consumes these trusted environment
values and overwrites untrusted cell metadata.
## Model Used
Codex (GPT-5)
## Thinking Path
Every trusted PR and paid-workflow job invokes the pinned pnpm setup
action. Its internal npm install is currently waiting four to seven
minutes on npm audit telemetry before any Paperclip or provider code
runs. Audit, funding, and update notifications are not integrity
controls for this action; its committed lockfile still verifies
installed package bytes.
## What Changed
- disable npm audit, funding, and update-notifier telemetry narrowly on
all seven pinned setup steps in each of the trusted PR and full-stack
workflows
- add a workflow security contract proving every setup invocation
remains covered and the overrides do not leak elsewhere
## Verification
- focused workflow security tests: 6/6 passed
- Prettier and git diff checks passed
- observed unhealthy setup: 4-7+ minutes; historical healthy setup:
about four seconds
## Risks
This skips npm vulnerability-report telemetry for the setup action
bootstrap only. Repository dependency checks, lockfile integrity,
provider-secret authorization, and target-lock verification remain
unchanged.
## Model Used
Codex (GPT-5)
## Thinking Path
The reused Daytona image path already verifies the signed immutable
digest. It then downloads every filesystem layer only to read OCI config
fields. Buildx can retrieve the same config from that immutable digest
without pulling the layers. The assertions can therefore stay intact
while removing the expensive transfer.
## What Changed
- inspect the signed immutable Daytona image config through Buildx after
GHCR logout
- preserve digest, source revision, content ID, platform, user, and
provider-pack assertions
- extend the workflow contract test for the metadata-only path
## Verification
- Daytona image and workflow security tests: 10 passed
- Prettier and git diff checks passed
- observed full pull/prune cost: about 4m55s; metadata inspection: about
one second
## Risks
The current image has one runnable linux/amd64 platform plus its
attestation. A future genuinely multi-platform image would need explicit
linux/amd64 selection.
## Model Used
Codex (GPT-5)
## Thinking Path
> - Paperclip manages AI agents and their provider runtimes.
> - The paid runner workflow installs target dependencies with lifecycle
scripts disabled.
> - The bundled Daytona plugin depends on an audited repo-local plugin
SDK link.
> - The lifecycle-safe install path did not create that link.
> - This pull request restores only the trusted Daytona preparation step
before provider secrets are exposed.
> - The benefit is a working Daytona canary without enabling dependency
lifecycle scripts.
## Linked Issues or Issue Description
**What happened?**
The Daytona paid canary stopped before lease or provider startup. The
trusted paid job disabled root lifecycle scripts, so the repo-local
plugin SDK link was absent. The plugin install returned a missing
runtime dependency error for @paperclipai/plugin-sdk.
**Expected behavior**
The trusted workflow must prepare the bundled Daytona plugin without
running untrusted dependency lifecycle scripts. The paid cell must start
only after its runtime dependencies and entrypoints pass validation.
**Steps to reproduce**
1. Dispatch the runner full-stack paid workflow for
core-compatibility.runner-acpx-claude.daytona.message-marker.
2. Let the trusted job install root dependencies with lifecycle scripts
disabled.
3. Observe the Daytona plugin installation fail before a lease or
provider process starts.
**Paperclip version or commit**
Feature head 781ac7e08c. The failed canary
is Actions run 33803959325.
**Deployment mode**
GitHub Actions paid runner validation.
**Agent adapter(s) involved**
ACPX Claude through the bundled Daytona plugin.
**Additional context**
This is a small trusted-workflow prerequisite for public PR #12769. Old
green run 33118525827 created the SDK link through root postinstall.
This change keeps lifecycle scripts disabled and restores only the
audited prerequisite.
## What Changed
- Install standalone Daytona dependencies with lifecycle scripts
disabled.
- Run the audited repo-local plugin SDK linker before provider secrets
are exposed.
- Build the bundled Daytona plugin and verify its runtime dependency
plus both entrypoints.
- Add a security regression for ordering, scope, and secret isolation.
## Verification
- Five focused workflow-security tests passed.
- Seven focused linker tests passed.
- The exact Daytona preparation command completed locally in nine
seconds.
- Prettier and diff whitespace checks passed.
## Risks
Risk is low and limited to Daytona paid cells. The setup still disables
dependency lifecycle scripts. The trusted step runs before provider
credentials enter the job. Any missing or mismatched path fails closed
before provider startup.
## Model Used
OpenAI GPT-5 Codex with repository tools 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 this PR with the bug template labels
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change
- [x] Focused local tests pass
- [x] I added tests for the change
- [x] I updated the relevant trusted-workflow security regression
- [x] I documented the risks above
## Thinking Path
> - Paperclip manages AI agents and their provider runtimes.
> - Paid runner validation installs target dependencies with lifecycle
scripts disabled.
> - OpenCode leaves a sentinel executable until its package lifecycle
script runs.
> - Running arbitrary lifecycle code would weaken the paid-secret
boundary.
> - This pull request materializes one exact pinned binary before
secrets are exposed.
> - The benefit is working OpenCode validation without trusting
dependency install scripts.
## Linked Issues or Issue Description
**What happened?**
Every local OpenCode paid cell stopped before provider startup because
`pnpm install --ignore-scripts` correctly retained
`opencode-ai/bin/opencode.exe` as a sentinel.
**Expected behavior**
The trusted workflow must make the exact lockfile-pinned OpenCode
executable available without running package lifecycle scripts.
**Steps to reproduce**
Run a local legacy or native OpenCode paid cell from the trusted
workflow after the target dependency install. The provider health check
reports that the OpenCode postinstall script was not run.
**Paperclip version or commit**
Default branch commit `865b4854fb44d3689f1c0ff17e3e715d52aaea73`.
## What Changed
- Materialize only `opencode-linux-x64-baseline@1.18.17` into the
matching `opencode-ai@1.18.17` package.
- Verify package identity, version, regular-file type, SHA-256 equality,
executable permissions, and runtime `--version`.
- Invoke the helper for local OpenCode and breadth cells and for remote
provider-pack assembly.
- Retain `pnpm install --ignore-scripts`.
- Add helper and trusted-workflow security regressions.
## Verification
- Helper syntax checks passed.
- Helper unit tests passed: 2/2.
- Workflow-security tests passed: 5/5.
- Prettier, actionlint, and diff whitespace checks passed.
## Risks
Risk is low and contained to paid runner setup. The helper supports only
Linux x64, fails closed on package or version drift, and runs before
provider credentials enter the job.
## Model Used
OpenAI GPT-5 Codex with repository tools 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.
- [x] I have checked ROADMAP.md and confirmed this does not duplicate
planned core work.
- [x] I have searched GitHub for duplicate or related PRs and found
none.
- [x] I have described the issue in this PR with the bug template
labels.
- [x] I have not referenced internal or instance-local issues.
- [x] My branch name describes the change.
- [x] Focused local tests pass.
- [x] I added tests for the change.
- [x] I updated the runner E2E security documentation.
- [x] I documented the risks above.
## Thinking Path
> - Paid cells repeated the same TypeScript and Rust builds even when
one campaign selected dozens of cells.
> - The trusted workflow can compile once without provider credentials
and distribute run-scoped, digest-verified artifacts.
> - The paid cell can then disable install lifecycle scripts, verify
each artifact before extraction, and expose provider credentials only to
the final test step.
> - Local JS-backed providers also need the setup-node interpreter
permission-qualified before Rust verifies the launch artifact.
## Linked Issues or Issue Description
Run 33786122875 proved target-lock setup and catalog selection, then
failed before provider creation because trusted master did not yet
qualify the setup-node interpreter. The same workflow also rebuilt
TypeScript and Rust inside every matrix cell.
## What Changed
- Build runner TypeScript and native binaries once per campaign in a
credential-free job.
- Build the remote provider pack once only when selected Daytona cells
require it.
- Upload run-scoped bundles with SHA-256 manifests and verify before
extraction in each paid cell.
- Remove repeated TypeScript, provider-pack, and Rust builds from paid
cells.
- Qualify the local provider Node interpreter before verified launch.
- Propagate the resolved target lockfile through all five target-code
jobs.
- Keep local-only selection off Daytona and exclude Xiaomi from the
67-cell catalog.
## Risks
A shared build artifact could fan out a bad payload to many cells. The
producing jobs receive no provider credentials, use the exact authorized
target SHA and resolved lockfile, and publish run-scoped artifacts.
Every consuming job verifies SHA-256 before extraction. Paid dependency
setup keeps lifecycle scripts disabled and provider credentials remain
scoped to the final test step.
## Verification
- Focused runner workflow-security, catalog, and Daytona-image tests:
25/25 passed.
- Prettier passed.
- Actionlint passed with only the two pre-existing SC2129 style notices
ignored.
- Git diff check passed.
## Model Used
OpenAI Codex, GPT-5.
## Checklist
- [x] Build jobs are credential-free.
- [x] Paid installs disable lifecycle scripts.
- [x] Artifacts are run-scoped and digest-verified before extraction.
- [x] Trusted report and history jobs remain isolated from target
artifacts.
- [x] No Daytona or Xiaomi paid run was started for this change.
## Thinking Path
> - The trusted target-branch runner workflow checks out PR code before
paid tests.
> - PR policy intentionally forbids manual lockfile commits.
> - Some runner changes legitimately alter pnpm patch hashes.
> - Frozen installs therefore fail before test selection.
> - Resolve one script-disabled lockfile from the authorized immutable
target SHA and distribute it by exact artifact ID and digest.
> - Keep provider credentials and trusted reporting outside this
resolution job.
## Linked Issues or Issue Description
Target-branch paid runner campaigns currently fail frozen install when a
PR changes pnpm patch content, even though ordinary PR CI regenerates
the lockfile.
## What Changed
- Added one credential-free target-lock job that resolves the authorized
immutable target SHA with lifecycle scripts disabled.
- Uploaded the resolved lockfile with its SHA-256 and restored it by
exact artifact ID before every target-code frozen install.
- Left trusted reporting and history jobs on the workflow SHA.
- Changed the disabled-AWS fallback from unavailable ubuntu-latest-m to
ubuntu-latest.
## Risks
The workflow evaluates pnpm lockfile resolution from authorized target
code. That job receives no provider credentials, disables lifecycle
scripts, rejects unrelated workspace mutations, and exposes only a
digest-verified lockfile artifact. Paid-secret jobs consume only that
lockfile after exact artifact-ID and SHA-256 validation.
## Verification
- Runner workflow-security focused tests pass.
- actionlint passes.
- Prettier and git diff checks pass.
## Model Used
OpenAI Codex, GPT-5.
## Checklist
- [x] Change is narrowly scoped to paid runner orchestration.
- [x] Target lock resolution has no provider credentials and disables
lifecycle scripts.
- [x] Downloaded artifacts are selected by exact artifact ID and
verified by SHA-256.
- [x] Trusted reporting and history jobs remain on the workflow SHA.
Bumps
[paperclipai/paperclip/.github/workflows/pr-trusted.yml](https://github.com/paperclipai/paperclip)
from 39b8ee2960 to
f038633bf5.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/paperclipai/paperclip/blob/master/doc/RELEASE-AUTOMATION-SETUP.md">paperclipai/paperclip/.github/workflows/pr-trusted.yml's
changelog</a>.</em></p>
<blockquote>
<h1>Release Automation Setup</h1>
<p>This document covers the GitHub and npm setup required for the
current Paperclip release model:</p>
<ul>
<li>automatic canaries from <code>master</code></li>
<li>manual stable promotion from a chosen source ref</li>
<li>npm trusted publishing via GitHub OIDC</li>
<li>protected release infrastructure in a public repository</li>
</ul>
<p>Repo-side files that depend on this setup:</p>
<ul>
<li><code>.github/workflows/release.yml</code></li>
<li><code>.github/CODEOWNERS</code></li>
</ul>
<p>Note:</p>
<ul>
<li>the release workflows intentionally use <code>pnpm install
--no-frozen-lockfile</code></li>
<li>this matches the repo's current policy where
<code>pnpm-lock.yaml</code> is refreshed by GitHub automation after
manifest changes land on <code>master</code></li>
<li>the publish jobs then restore <code>pnpm-lock.yaml</code> before
running <code>scripts/release.sh</code>, so the release script still
sees a clean worktree</li>
</ul>
<h2>1. Merge the Repo Changes First</h2>
<p>Before touching GitHub or npm settings, merge the release automation
code so the referenced workflow filenames already exist on the default
branch.</p>
<p>Required files:</p>
<ul>
<li><code>.github/workflows/release.yml</code></li>
<li><code>.github/CODEOWNERS</code></li>
</ul>
<h2>2. Configure npm Trusted Publishing</h2>
<p>Do this for every public package that Paperclip publishes.</p>
<p>At minimum that includes:</p>
<ul>
<li><code>paperclipai</code></li>
<li><code>@paperclipai/server</code></li>
<li><code>@paperclipai/ui</code></li>
<li>public packages under <code>packages/</code></li>
</ul>
<h3>2.1. In npm, open each package settings page</h3>
<p>For each package:</p>
<ol>
<li>open npm as an owner of the package</li>
<li>go to the package settings / publishing access area</li>
<li>add a trusted publisher for the GitHub repository
<code>paperclipai/paperclip</code></li>
</ol>
<h3>2.2. Add one trusted publisher entry per package</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f038633bf5"><code>f038633</code></a>
feat(runner): reduce ACPX provider state (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12417">#12417</a>)</li>
<li><a
href="7bb6cebeae"><code>7bb6ceb</code></a>
feat(runner): normalize ACPX provider events (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12416">#12416</a>)</li>
<li><a
href="fe2ddfad2b"><code>fe2ddfa</code></a>
feat(runner): validate ACPX event payloads (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12415">#12415</a>)</li>
<li><a
href="3db24d9366"><code>3db24d9</code></a>
feat(runner): bind ACPX event scope (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12414">#12414</a>)</li>
<li><a
href="75708fec6d"><code>75708fe</code></a>
feat(runner): add ACPX sidecar transport (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12412">#12412</a>)</li>
<li><a
href="9ad8dbffa0"><code>9ad8dbf</code></a>
feat(runner): add Codex ACPX sidecar (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12410">#12410</a>)</li>
<li><a
href="b93ad538b6"><code>b93ad53</code></a>
test(runner): add question adapter conformance (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12409">#12409</a>)</li>
<li><a
href="4fe3189f02"><code>4fe3189</code></a>
feat(runner): bridge Codex ACPX questions (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12408">#12408</a>)</li>
<li><a
href="96421b0663"><code>96421b0</code></a>
feat(runner): recover settled Codex ACPX sessions (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12407">#12407</a>)</li>
<li><a
href="30ef14edd4"><code>30ef14e</code></a>
feat(runner): wire the Codex ACPX backend (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12406">#12406</a>)</li>
<li>Additional commits viewable in <a
href="39b8ee2960...f038633bf5">compare
view</a></li>
</ul>
</details>
<br />
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Use full dependency resolution in automated lockfile repair paths, add regression coverage, and refresh the stale Rollup snapshot.
Co-Authored-By: Dotta <cryppadotta@users.noreply.github.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: lockfile-bot <lockfile-bot@users.noreply.github.com>
## Thinking Path
> - Paperclip uses paid runner tests to qualify agent execution.
> - The runner workflow controls provider secrets and AWS runner access.
> - The trusted workflow must stay on the protected default branch.
> - The code under test often exists on a branch before merge.
> - CODEOWNERS need a safe way to select that branch.
> - This pull request separates workflow authority from the code under
test.
> - The benefit is pre-merge AWS testing without target-controlled
workflow code.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
The manual Runner Full-Stack E2E workflow can test only the default
branch.
**Subsystem affected**
GitHub Actions and the paid runner E2E security boundary.
**Current behavior**
A CODEOWNER must merge runner changes before the trusted AWS workflow
can test them.
Selecting another branch as the workflow ref is rejected.
**Proposed behavior**
A CODEOWNER starts the workflow from `master` and supplies a
same-repository branch in `target_branch`.
The authorization job resolves the branch to one commit SHA.
Catalog, image, and paid test jobs check out that SHA after
authorization.
Report sanitization and AWS publication use the trusted workflow SHA.
**Reason and benefit**
This permits paid pre-merge qualification on AWS.
It keeps the workflow definition, report sanitizer, history publisher,
environment deployment, and runner-group permission on `master`.
**Breaking changes**
None.
The new input is optional.
An omitted input still tests the default branch.
## What Changed
- Add the optional `target_branch` workflow input.
- Resolve only a branch in `paperclipai/paperclip` to an immutable SHA.
- Pin catalog, image, paid test, and Daytona provenance to the target
SHA.
- Pin report sanitization and AWS history publication to the trusted
workflow SHA.
- Disable persisted checkout credentials in every job.
- Key cancellation by the selected target branch.
- Add policy regression coverage and operator documentation.
## Verification
- `pnpm test:e2e:runner:unit` passes with 65 tests.
- `actionlint -ignore SC2129
.github/workflows/runner-full-stack-e2e.yml` passes.
- Prettier checks pass for all changed files.
- `git diff --check` passes.
## Risks
A CODEOWNER can authorize selected branch code to receive a cell-scoped
provider credential.
This is the intended trust decision.
The workflow rejects fork refs and target-controlled workflow
definitions.
The trusted workflow SHA owns report sanitization and AWS history
publication.
> 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.
The exact serving snapshot and context-window size are not exposed.
The model used tool-enabled reasoning 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
- [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 manages AI agents that perform work.
> - The paid runner matrix verifies complete runner behavior with real
providers.
> - Each matrix job currently repeats work on GitHub-hosted runners.
> - Paperclip has an ephemeral AWS runner fleet for trusted workflows.
> - The paid workflow needs a reviewed and fail-closed route to that
fleet.
> - This pull request adds that route and keeps the existing hosted
runner as the disabled-state fallback.
> - The benefit is faster paid campaigns with the same actor,
environment, and secret boundaries.
## Linked Issues or Issue Description
**What happened?**
The Runner Full-Stack E2E workflow always uses `ubuntu-latest-m`. It
limits the matrix to 57 parallel jobs. The repository AWS fleet can run
100 ephemeral jobs, but the paid workflow cannot select it.
**Expected behavior**
An explicit repository flag must select the reviewed AWS fleet label. A
missing or invalid flag must keep the existing hosted runner. The
workflow must authorize the stable actor identity before it routes any
paid job.
**Steps to reproduce**
1. Dispatch the Runner Full-Stack E2E workflow from `master`.
2. Inspect a paid matrix job.
3. Observe that the job requests `ubuntu-latest-m` even when the AWS
fleet should be used.
**Paperclip version or commit**
`da0947d3582ac7779d6bf11851c9938eca6c5c8c`
**Deployment mode**
GitHub Actions paid runner campaign.
## What Changed
- Add a fail-closed `RUNNER_E2E_AWS_ENABLED` switch.
- Select only the reviewed AWS fleet label or the existing hosted label.
- Permit up to 100 parallel jobs in AWS mode.
- Keep the hosted-runner limit at 57.
- Reauthorize paid execution before checkout and provider access.
- Stop paid checkouts from storing GitHub credentials.
- Cancel superseded validation-ref campaigns while preserving `master`
audit runs.
- Add workflow policy checks and operator documentation.
## Verification
- `git diff --check`
- `actionlint -ignore SC2129
.github/workflows/runner-full-stack-e2e.yml`
- The organization runner group permits this workflow only from
`refs/heads/master`.
- The repository AWS switch remains disabled until this pull request is
merged and a one-cell probe succeeds.
## Risks
- A wrong fleet policy can leave jobs queued. The disabled state keeps
the existing hosted runner.
- The AWS fleet uses paid compute. The workflow validates a configured
maximum of 100 jobs.
- The runner group, actor allowlist, and paid environment remain
separate enforcement layers.
> 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, GitHub API coordination, and static
workflow analysis. The exact deployed model identifier 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 (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
- [ ] 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 onboarding command starts the local server and opens the
first-run wizard.
> - Interactive onboarding stopped opening the browser by default.
> - Organization creation could also succeed in the API while the wizard
stayed on the name step.
> - The npm canary workflow did not prove that the published package
could complete this path.
> - This pull request repairs the startup and organization transitions
and adds an exact-version canary smoke gate.
> - The benefit is a quickstart that works for users and is tested after
each canary publish.
## Linked Issues or Issue Description
Related: #12557 covers a separate final-route onboarding handoff.
**What happened?**
Interactive `paperclipai onboard` runs did not open the onboarding page.
The organization API request could succeed while a same-company context
update caused the wizard to stay on the organization step. The canary
release lane did not test the exact published npm package through this
path.
**Expected behavior**
Interactive onboarding must open the browser once. A successful
organization request must advance to the first-agent step when the
surrounding context adopts the same organization. Each published canary
must install in a clean environment and reach the model connection step.
**Steps to reproduce**
1. Run `npx paperclipai@canary onboard --data-dir "$(mktemp -d
/tmp/paperclip-canary.XXXXXX)"` in an interactive terminal.
2. Enter an organization name while the company context refreshes from
the create response.
3. Observe that the browser does not open or that the wizard can remain
on the organization step after the API creates it.
4. Inspect the canary release lane and observe that no post-publish
onboarding test runs against the exact npm version.
**Paperclip version or commit**
The issue reproduced with `2026.901.0-canary.8` and the source state
before this pull request.
**Deployment mode**
Local trusted quickstart with embedded PostgreSQL. The install source
can be npm or a source checkout.
## What Changed
- Open the browser once for interactive foreground onboarding.
- Preserve explicit browser opt-outs and restore the prior environment
value after startup.
- Accept a same-company context update after organization creation and
reject a different-company takeover with an explicit error.
- Export the exact canary version from the publish job.
- Install and test that exact npm version in a clean Playwright smoke
job through the "Connect a model" step.
- Upload server logs, traces, screenshots, and the Playwright report
when the canary smoke fails.
- Document the interactive default and headless opt-outs.
## Verification
- `pnpm exec vitest run cli/src/__tests__/onboard.test.ts
ui/src/components/OnboardingWizard.step.test.tsx --reporter=dot` passes
with 37 tests.
- `node --test scripts/__tests__/release-verify-workflow.test.mjs`
passes with 9 tests.
- `pnpm exec playwright test --config tests/e2e/playwright.config.ts
tests/e2e/onboarding.spec.ts` passes with 2 tests.
- `PAPERCLIPAI_VERSION=2026.901.0-canary.8 pnpm run
test:canary-onboarding-smoke` passes against the published npm package.
- `pnpm check:token-gates` passes.
- `pnpm -r typecheck` passes.
- `pnpm build` passes.
- A fresh interactive source run opens the browser and reaches "Connect
a model" after organization and agent naming.
## Risks
- Low risk. Automatic browser opening only applies to interactive
foreground onboarding.
- `PAPERCLIP_NO_BROWSER=1` and `PAPERCLIP_OPEN_ON_LISTEN=false` keep
headless runs silent.
- A different organization context still blocks the pending create
transition.
- The canary package is immutable before the smoke runs. A smoke failure
leaves the package published but makes the release workflow red.
- This change does not modify REST APIs, database schemas, or shared
data types.
> 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 runtime does not expose the exact
deployment snapshot or context-window size. The model used reasoning,
browser automation, repository tools, shell commands, code editing, 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 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>
## Thinking Path
> - Paperclip uses GitHub Actions to verify pull requests before merge.
> - The pull request workflow calls a trusted reusable workflow at an
immutable commit SHA.
> - Pull request #12507 added stack-aware CI scope to that reusable
workflow.
> - The caller still points to the prior workflow commit, so the new
behavior is not active.
> - This pull request advances the caller to the merged commit from pull
request #12507.
> - The benefit is that native stack middle layers stop starting
redundant full CI matrices.
## Linked Issues or Issue Description
Refs #12507
**What existing behavior does this improve?**
The pull request workflow still calls the trusted CI definition that
predates stack-aware scope selection.
**Subsystem affected**
GitHub Actions pull request verification.
**Current behavior**
Every pull request layer in a native stack starts the complete test,
build, canary, and E2E matrix.
**Proposed behavior**
Call the trusted workflow from merged master commit
`39b8ee2960541d14b380f95365deecba6723d9bd`. That workflow runs full CI
for ordinary, top, and lowest-unmerged pull requests. It keeps policy
and required aggregate checks on middle layers.
**Reason and benefit**
This completes the two-step immutable workflow rollout from pull request
#12507. Large stacks will use fewer runners and will spend less time
waiting for duplicate jobs.
**Breaking changes**
Middle native stack layers no longer run the complete CI matrix.
Required aggregate checks remain present and fail closed if the stack
scope is missing or invalid.
## What Changed
- Pin `.github/workflows/pr.yml` to merged master commit
`39b8ee2960541d14b380f95365deecba6723d9bd`.
- Activate the stack-aware trusted workflow that merged in pull request
#12507.
## Verification
- `node --test scripts/__tests__/e2e-shard.test.mjs` — 11 tests passed.
- `actionlint .github/workflows/pr.yml .github/workflows/pr-trusted.yml`
— passed.
- `git diff --check origin/master...HEAD` — passed.
- Confirm that the caller SHA equals the merge commit for pull request
#12507.
## Risks
- The caller is immutable and points to a commit that exists on
`master`.
- Ordinary pull requests and merge-relevant stack layers still run full
CI.
- A rollback can restore the prior immutable SHA in one line.
> 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 suffix and context
window are not exposed. The model used reasoning, repository tools, code
execution, Git, and GitHub API access.
## 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
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip uses GitHub Actions to protect changes before they enter
`master`.
> - GitHub evaluates every pull request in a native stack against the
stack base.
> - The current workflow therefore starts the complete CI matrix for
every layer in a stack.
> - A large stack can queue many copies of the same integrated
verification and delay every pull request.
> - GitHub provides stack position and base metadata so workflows can
select merge-relevant layers.
> - This pull request keeps policy and required check names on every
layer, but runs full CI only for ordinary pull requests, the top layer,
and the lowest unmerged layer.
> - The benefit is much lower CI load without weakening the
required-check contract.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
The trusted pull request workflow currently runs every test, build,
canary, and E2E lane for every pull request in a native stack.
**Subsystem affected**
GitHub Actions pull request verification.
**Current behavior**
A stack with 61 pull requests can start 61 complete CI matrices after a
cascading rebase.
**Proposed behavior**
Run the always-on policy job and stable required-check aggregators for
every layer. Run the complete verification matrix only for ordinary pull
requests, the top stack layer, and the lowest unmerged stack layer.
**Reason and benefit**
The top layer verifies the integrated stack. The lowest unmerged layer
verifies the current merge candidate. Middle layers keep
branch-protection checks without consuming the complete runner matrix.
**Breaking changes**
Middle stack layers no longer run the complete CI matrix. Their `ci /
verify` and `ci / e2e` checks still require the policy job to pass and
require every expensive lane to be intentionally skipped.
## What Changed
- Add a fail-safe stack scope decision to the trusted PR runner gate.
- Run typecheck, general tests, build, serialized tests, canary, and E2E
shards only for ordinary, top, and lowest-unmerged pull requests.
- Preserve the required `ci / verify` and `ci / e2e` names on every
layer.
- Make the required aggregators distinguish valid middle-layer skips
from failures or missing scope decisions.
- Add regression coverage for ordinary, top, bottom, middle, and
malformed stack metadata.
## Verification
- `node --test scripts/__tests__/e2e-shard.test.mjs` — 11 tests passed.
- `actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml`
— passed.
- `git diff --check origin/master...HEAD` — passed.
- The caller remains pinned to the current trusted workflow. A separate
activation change must advance the immutable SHA after this pull request
lands.
## Risks
- Incorrect stack classification could skip important jobs. Missing or
malformed stack metadata defaults to full CI.
- Middle-layer required checks depend on the policy job and verify that
all expensive jobs have the `skipped` result.
- The reusable workflow change does not become active until the
immutable caller SHA advances in a separate change.
> 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 suffix and context
window are not exposed. The model used reasoning, repository tools, code
execution, Git, and GitHub API access.
## 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
- [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>
## Thinking Path
> - Paperclip uses trusted GitHub Actions workflows to verify each pull
request.
> - The caller pins the reusable workflow to an immutable merged commit.
> - Native stacked pull requests can inherit a stale lockfile from a
parent layer.
> - Pull request #12461 added safe merge-tree lockfile regeneration to
the trusted workflow.
> - The caller must now select that merged workflow version.
> - This pull request advances the immutable pin to the merge commit
from #12461.
> - The benefit is reliable verification for native stacked pull
requests.
## Linked Issues or Issue Description
Refs #12461
## What Changed
- Pin the trusted pull request workflow to merged commit
`1da6b37fc56dacf5e7ffbd31da35756a1cba41f8`.
- Activate stale lockfile regeneration for native stacked pull requests.
## Verification
- `actionlint .github/workflows/pr.yml`
- `node --test scripts/__tests__/e2e-shard.test.mjs`
## Risks
- Low risk. This changes only the immutable reusable-workflow pin.
- The target commit is merged into `master` and passed all required
checks.
- The target workflow adds one lockfile-only install to the policy job.
> 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 model `gpt-5`, 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
- [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 uses trusted GitHub Actions workflows to verify every pull
request
> - Native stacked pull requests use another pull request branch as
their base
> - A parent layer can change a package manifest without committing
`pnpm-lock.yaml`
> - A child layer can inherit that manifest change without changing a
manifest itself
> - The current policy skips lockfile regeneration for that child and
downstream frozen installs fail
> - This pull request validates the complete merge tree and shares a
regenerated lockfile only when needed
> - The benefit is reliable stacked pull request verification without
weakening the trusted workflow boundary
## Linked Issues or Issue Description
**What happened?**
A stacked child pull request inherited a package manifest change from
its parent. The child did not change a manifest itself. The policy job
skipped lockfile regeneration. Downstream jobs tried to restore an
artifact that did not exist and then failed during frozen dependency
installation.
**Expected behavior**
The policy job must validate the complete pull request merge tree. It
must upload a regenerated lockfile when the checked-in lockfile is
stale, including on a stacked child layer.
**Steps to reproduce**
1. Create a parent pull request that changes `package.json` without
committing `pnpm-lock.yaml`.
2. Create a child pull request on that branch without another manifest
change.
3. Run the trusted pull request workflow for the child.
4. Observe that frozen dependency installation fails because no
`pr-lockfile` artifact exists.
**Paperclip version or commit**
`f173ee09fa5c2ced7806bba47b54c3df853ab4df`
**Deployment mode**
GitHub Actions trusted pull request workflow.
**Agent adapter(s) involved**
Not adapter-specific. This is a core CI workflow bug.
## What Changed
- Regenerate the lockfile from every checked-out merge tree.
- Compare the generated lockfile with the checked-in copy before upload.
- Download the artifact only when the policy job reports that it
uploaded one.
- Fail closed when a reported artifact is missing.
- Add a workflow contract test for stacked lockfile handling.
- Keep the caller pinned to the last merged trusted SHA; after this
implementation merges, a separate activation PR will advance the
immutable pin to its merge commit.
## Verification
- `actionlint .github/workflows/pr-trusted.yml`
- `node --test scripts/__tests__/e2e-shard.test.mjs`
## Risks
- The policy job runs one lockfile-only install for every pull request.
This can add a small amount of CI time.
- A missing artifact now fails immediately when the policy job reports
an upload. This is intentional because it exposes workflow corruption.
- No runtime or product behavior changes.
- The implementation/activation split is intentional: unmerged
PR-authored workflow code must never execute on trusted runners.
## Model Used
OpenAI Codex with model `gpt-5`, 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
- [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 uses pull request checks to protect changes.
> - The trusted workflow selects GitHub or AWS runners.
> - Pull request #12462 removed an unreliable stale API equality.
> - The public caller must pin an immutable trusted workflow commit.
> - This pull request changes only that pin.
> - The benefit is correct AWS routing for trusted stacked pull
requests.
## Linked Issues or Issue Description
Refs #12339
Refs #12462
## What Changed
- Pin the thin pull request caller to
f9c32513b2.
## Verification
- actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml
- github-runners/tests/test-workflow.sh .github/workflows/pr-trusted.yml
.github/workflows/pr.yml
- The runner group authorizes this SHA and all in-flight prior SHAs.
## Risks
- Low risk. This changes one immutable workflow reference.
- Older workflow SHAs remain authorized while queued and active jobs
finish.
## Model Used
- OpenAI Codex, GPT-5.6, with reasoning and terminal 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 linked existing public pull requests
- [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
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open findings
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip uses pull request checks to protect changes.
> - The trusted CI gate selects GitHub or AWS runners.
> - GitHub can return an old pull request base SHA after the live base
advances.
> - The signed event, live Git ref, ancestry, and merge parents provide
the required proof.
> - The stale API field rejects a safe run even when those proofs pass.
> - This pull request removes that unreliable equality.
> - The benefit is correct AWS routing for trusted stacked pull
requests.
## Linked Issues or Issue Description
Refs #12339
Refs #12459
## What Changed
- Stop treating pull request base.sha as a current-state signal.
- Keep the signed event base SHA and live ref descendant check.
- Keep the live base or synthetic base merge-parent proof.
- Keep all numeric identity, repository, head SHA, and triggering actor
checks.
## Verification
- actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml
- github-runners/tests/test-workflow.sh .github/workflows/pr-trusted.yml
.github/workflows/pr.yml
- The corrected gate selected the Fleet label with the exact live event
data from PR #12339 run 33201610330.
## Risks
- The pull request API base SHA can be stale and is no longer compared.
- Replaced ancestry, a changed head, a changed merge parent, and a
changed merge tree still fail closed.
## Model Used
- OpenAI Codex, GPT-5.6, with reasoning and terminal 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 linked existing public pull requests
- [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
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation in the operations repository
- [x] I have considered and documented risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open findings
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip uses pull request checks to protect changes.
> - The trusted CI workflow selects GitHub or AWS runners.
> - Pull request #12459 fixed validation when a stacked base advances.
> - The public caller must use an immutable trusted workflow SHA.
> - This pull request changes only that SHA.
> - The benefit is automatic AWS routing for the affected trusted stack
runs.
## Linked Issues or Issue Description
Refs #12339
Refs #12459
## What Changed
- Pin the thin pull request caller to
f929355fb9.
## Verification
- actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml
- github-runners/tests/test-workflow.sh .github/workflows/pr-trusted.yml
.github/workflows/pr.yml
- The runner group already authorizes the old and new exact workflow
SHAs.
## Risks
- Low risk. This changes one immutable workflow reference.
- The old workflow SHA remains authorized while current runs finish.
## Model Used
- OpenAI Codex, GPT-5.6, with reasoning and terminal 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 linked existing public pull requests
- [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
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open findings
- [x] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip uses pull request checks to protect changes.
> - The trusted CI workflow selects GitHub or AWS runners.
> - Stacked pull requests can advance their base branch while a gate
waits.
> - The gate already proves that the live base descends from the event
base.
> - An earlier exact base check rejects that safe state before the
ancestry check runs.
> - This pull request removes the conflicting check and verifies live
API consistency.
> - The benefit is automatic AWS routing for trusted stacked pull
requests without weaker identity checks.
## Linked Issues or Issue Description
Refs #12339
Refs #12457
## What Changed
- Allow the live pull request base SHA to advance from the signed event
base snapshot.
- Require the pull request API base SHA to match the live Git ref during
validation.
- Keep the numeric author, sender, and triggering actor checks.
- Keep descendant ancestry and synthetic merge validation.
## Verification
- actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml
- github-runners/tests/test-workflow.sh .github/workflows/pr-trusted.yml
.github/workflows/pr.yml
- The routing suite covers a live stacked base advance and a base change
during validation.
## Risks
- A trusted stacked run can use a newer descendant base than its signed
event snapshot.
- Replaced ancestry still fails closed.
- A live base change during gate validation still fails closed.
## Model Used
- OpenAI Codex, GPT-5.6, with reasoning and terminal 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 existing issues or described the issue in
this pull request
- [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
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation in the operations repository
- [x] I have considered and documented risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open findings
- [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.
> - Pull request checks protect the application and its contributors.
> - The trusted CI gate must validate both direct and stacked GitHub
merge shapes.
> - Pull request #12457 added that validation at an immutable master
SHA.
> - The active caller still pins the prior workflow version.
> - This pull request pins the caller to the newly authorized SHA.
> - The benefit is safe automatic AWS routing for trusted stacked pull
requests.
## Linked Issues or Issue Description
Refs #12457
**What existing behavior does this improve?**
The active caller uses a gate that fails closed on GitHub synthetic
stacked merge parents.
**Subsystem affected**
GitHub Actions pull request routing.
**Current behavior**
Trusted stacked pull requests run on GitHub-hosted runners after the
merge-parent check rejects the synthetic base merge.
**Proposed behavior**
The caller uses the authorized workflow SHA
7b199fcafa.
**Reason and benefit**
The change lets approved stack jobs use the 100-runner AWS Fleet while
all numeric identity and live-state checks remain active.
**Breaking changes**
None for untrusted contributors. Their jobs continue on GitHub-hosted
runners.
**Additional context**
The runner group authorizes both this SHA and the previous SHA during
rotation.
## What Changed
- Pin the thin caller to the authorized stacked-merge validator SHA.
## Verification
- Ran actionlint on both pull request workflows.
- Ran the external routing suite against the trusted workflow and
caller.
- Verified that the runner group authorizes both immutable workflow
versions.
## Risks
A bad pin can stop CI. The target is on master, the previous SHA stays
authorized, and the caller change is a one-line reversible pin update.
> For core feature work, check 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 on GPT-5. The exact deployment ID and context-window size
are not exposed. The model used reasoning, tool use, GitHub API access,
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 linked an existing public item and 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
- [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.
> - Pull request checks protect the application and its contributors.
> - The trusted CI gate verifies signed event data against live GitHub
state.
> - GitHub gives a stacked pull request a synthetic merge commit for its
base stack.
> - The current validator expects the raw base SHA as the first merge
parent.
> - That assumption sends safe stacked pull requests to GitHub-hosted
runners.
> - This pull request validates the synthetic base merge and its
ancestry explicitly.
> - The benefit is safe automatic AWS routing for approved stacked pull
requests.
## Linked Issues or Issue Description
Refs #12455
Refs #12456
**What existing behavior does this improve?**
The trusted runner gate validates merge commits for master-based pull
requests but rejects GitHub synthetic base merges for stacked pull
requests.
**Subsystem affected**
GitHub Actions pull request identity and merge-state validation.
**Current behavior**
A trusted stacked pull request passes all numeric identity checks. The
gate fails closed because the first event merge parent is a GitHub
synthetic base merge instead of the raw base snapshot SHA.
**Proposed behavior**
The gate verifies the current base-ref tip, base-snapshot ancestry,
identical event and live merge parents, the child head parent, the
synthetic base merge parents, and identical event and live merge trees.
**Reason and benefit**
The change preserves fail-closed live-state validation while allowing
approved stacked pull requests to use the isolated AWS Fleet.
**Breaking changes**
None for untrusted contributors. Trusted stacked pull requests can
select AWS after the caller pins this workflow version.
**Additional context**
GitHub builds a stacked test merge in two steps. It first merges the
stack base into its own current base. It then uses that synthetic commit
as the first parent of the child test merge.
## What Changed
- Fetch and validate the current base branch ref.
- Require the event base snapshot to remain an ancestor of that ref.
- Validate direct and synthetic base merge parent shapes.
- Preserve the existing child-head, live-state, merge-tree, repository,
and actor checks.
## Verification
- Ran actionlint on the trusted workflow.
- Ran the external routing suite.
- Added positive coverage for the GitHub stacked merge shape.
- Added fail-closed coverage for replaced base ancestry and a synthetic
merge that omits the current base ref.
- Replayed the checks against the live merge shape for pull request
#12340.
## Risks
The gate has more GitHub API reads. Its five-minute timeout and
fail-closed behavior limit the effect of API errors. The accepted
synthetic commit must be the same in the event and live merge, must
include the current base branch as a direct parent, and must produce the
same merge tree.
> For core feature work, check 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 on GPT-5. The exact deployment ID and context-window size
are not exposed. The model used reasoning, tool use, GitHub API access,
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 linked existing public items and 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
- [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.
> - Pull request checks protect the application and its contributors.
> - The trusted CI workflow sends approved contributors to isolated AWS
runners.
> - Pull request #12455 added safe support for non-master base branches.
> - The active caller still filters for master and uses the previous
immutable workflow SHA.
> - This pull request removes the base-branch trigger filter and pins
the caller to the authorized stack-aware SHA.
> - The benefit is that trusted stacked pull requests can use the AWS
fleet automatically.
## Linked Issues or Issue Description
Refs #12455
**What existing behavior does this improve?**
The active pull request caller only triggers for master and selects the
master-only trusted workflow version.
**Subsystem affected**
GitHub Actions pull request routing.
**Current behavior**
Trusted stacked pull requests either do not trigger the default caller
or use a branch-local older caller. Their heavy jobs remain in the
GitHub-hosted queue.
**Proposed behavior**
The caller triggers for every pull request base branch. It uses the
authorized stack-aware workflow at full SHA
d6b33d6c16.
**Reason and benefit**
The change sends heavy jobs from approved stacked pull requests to the
100-runner AWS Fleet. Unlisted contributors still receive the normal
GitHub-hosted checks.
**Breaking changes**
Trusted pull requests with non-master base branches now use AWS.
Untrusted pull requests continue to use GitHub-hosted runners.
**Additional context**
The runner group currently authorizes both the old and new immutable
workflow SHAs. The old SHA stays authorized until outstanding runs
finish.
## What Changed
- Remove the caller base-branch filter so stacked pull requests trigger
CI.
- Pin the thin caller to the authorized stack-aware reusable workflow
SHA.
## Verification
- Ran actionlint on both pull request workflow files.
- Ran the external routing test suite against the new reusable workflow
and thin caller.
- Verified that the runner group authorizes both immutable SHAs and only
the Paperclip repository.
## Risks
A bad pin can stop pull request CI. The target SHA is on master,
contains the reviewed reusable workflow, and is already authorized
beside the prior SHA. This gives the rotation a rollback path. Every
untrusted run still uses a GitHub-hosted gate and route.
> For core feature work, check 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 on GPT-5. The exact deployment ID and context-window size
are not exposed. The model used reasoning, tool use, GitHub API access,
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 linked an existing public item and 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
- [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.
> - Pull request checks protect the application and its contributors.
> - The trusted CI workflow sends approved contributors to isolated AWS
runners.
> - The workflow currently limits AWS routing to pull requests that
target master.
> - Stacked pull requests target another branch and stay in the GitHub
queue.
> - This pull request keeps the identity checks and accepts a live
nonempty base branch in the Paperclip repository.
> - The benefit is that trusted stacked pull requests can use the AWS
fleet automatically.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
The trusted pull request runner gate currently sends only master-based
pull requests to AWS.
**Subsystem affected**
GitHub Actions pull request routing.
**Current behavior**
A trusted contributor can pass all numeric identity checks. The gate
still selects GitHub-hosted runners when the pull request targets
another branch in a stack.
**Proposed behavior**
The gate accepts any nonempty base branch in the Paperclip base
repository. It still verifies the live base ref, base SHA, head SHA,
merge SHA, author ID, sender ID, and triggering actor ID.
**Reason and benefit**
Large pull request stacks currently add all heavy jobs to the limited
GitHub-hosted queue. This change lets approved contributors use the
isolated AWS fleet for those jobs.
**Breaking changes**
Trusted pull requests that target a non-master branch now use AWS
instead of GitHub-hosted runners. Untrusted pull requests keep the
current GitHub-hosted route.
**Additional context**
Pull request #12339 is the master-based first item in a stack. Its child
pull requests show this queue pattern.
## What Changed
- Replace the master-only route check with a nonempty base-ref check.
Keep the existing base-repository and live-state checks.
## Verification
- Ran actionlint on .github/workflows/pr-trusted.yml.
- Ran the external routing test suite. It passed the trusted
stacked-base case and all fail-closed identity cases.
## Risks
The AWS trust boundary now includes code from a non-master base branch
when the current pull request author, sender, and triggering actor are
all approved numeric GitHub IDs. An approved account can already submit
arbitrary head code. The runner remains isolated and has no production
access or repository secrets.
> For core feature work, check 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 on GPT-5. The exact deployment ID and context-window size
are not exposed. The model used reasoning, tool use, GitHub API access,
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
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [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
## Summary
- validate the event base ref/SHA against the live PR state instead of
requiring the moving `master` branch tip to remain unchanged while a
hosted gate queues
- retain exact event/live merge parent and tree validation, plus
author/sender/rerun checks
## Canary finding
A seven-minute hosted-gate queue allowed `master` to advance. Requiring
the live branch tip to equal the event base snapshot would route
otherwise valid trusted runs back to GitHub-hosted indefinitely on a
busy repository.
## Validation
- actionlint and workflow-contract tests pass
- internal routing harness passes
- replaced PR base snapshot, stale head, changed merge parent/tree, and
untrusted actors all remain fail-closed
- AWS routing remains disabled during rotation
## Summary
- use `${{ github.sha }}` as the event merge commit validated by the
trusted gate
- retain exact base/head parent and identical-tree comparison against
the current live merge ref
## Canary finding
GitHub leaves `pull_request.merge_commit_sha` empty on some `opened`
payloads even though the workflow runs against a valid merge ref. The
gate safely fell back to GitHub-hosted and no EC2 instance launched.
## Validation
- `actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml`
- `node --test ./scripts/__tests__/e2e-shard.test.mjs`
- internal routing harness passes and asserts `EVENT_MERGE_SHA` is
sourced from `${{ github.sha }}`
- AWS routing remains disabled during rotation
## Summary
- rotate the thin PR caller from
`3b295b05dc8c8dd82c12e4a9c6f721446c5cb2e8` to
`b88fadb0390d2113933d5d155f16b07cbd5dafee`
- keep both exact SHAs authorized during the rotation
- keep AWS routing disabled until the caller and boundary verify
## Validation
- `actionlint .github/workflows/pr.yml .github/workflows/pr-trusted.yml`
- `node --test ./scripts/__tests__/e2e-shard.test.mjs`
- internal routing harness requires exactly one runner output per gate
execution
## Summary
- emit exactly one `runner` job output from the trusted gate
- write `ubuntu-latest` only inside fail-closed paths
- write the Fleet label only after every identity, PR-state,
merge-equivalence, and rerun-actor check passes
## Canary finding
The live gate reached the trusted success notice, but GitHub retained
the first of two duplicate `runner=` outputs, so policy still requested
`ubuntu-latest`. No EC2 instance launched. Routing was disabled
immediately.
## Validation
- `actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml`
- `node --test ./scripts/__tests__/e2e-shard.test.mjs`
- internal routing harness passes and now requires exactly one runner
output in all cases
- AWS routing remains disabled during rotation
## Summary
- rotate the thin PR caller from
`d9fc93d8383ece6fba721881a7aba638867f4996` to
`3b295b05dc8c8dd82c12e4a9c6f721446c5cb2e8`
- keep both exact workflow SHAs authorized in the runner group during
the rotation
- keep `AWS_CI_ENABLED=false` until this caller is merged and verified
## Validation
- `actionlint .github/workflows/pr.yml .github/workflows/pr-trusted.yml`
- `node --test ./scripts/__tests__/e2e-shard.test.mjs`
- full AWS/GitHub boundary verification passes with zero active runners
## Summary
- validate the live master ref and event base SHA before AWS routing
- accept GitHub synthetic merge commits only when the event and live
commits have the exact expected base/head parents and identical tree
- preserve fail-closed routing for malformed, stale, replaced, or
untrusted events
## Canary finding
A trusted reopened PR produced two synthetic merge SHAs with different
timestamps but identical current base/head parents and tree. The former
exact-SHA comparison safely fell back to GitHub-hosted runners, but
could not route a valid event to AWS.
## Validation
- `actionlint .github/workflows/pr-trusted.yml .github/workflows/pr.yml`
- `node --test ./scripts/__tests__/e2e-shard.test.mjs`
- real-event gate simulation selects the Fleet label for equivalent
merge commits
- negative simulations keep an untrusted sender and replaced head on
`ubuntu-latest`
- AWS routing remains disabled during rotation
## Thinking Path
> - Paperclip uses pull request CI to validate each proposed change
> - The existing workflow defines every heavy job in a PR-controlled
file
> - A trusted reusable workflow now contains the synchronized CI
definition
> - The caller must use an immutable default-branch SHA
> - This pull request replaces the duplicate job list with that pinned
caller
> - The benefit is automatic secure runner selection without workflow
drift
## Linked Issues or Issue Description
Refs #12436
Refs #12438
**What existing behavior does this improve?**
This improves how the pull request workflow selects trusted CI capacity.
**Subsystem affected**
Cross-cutting CI automation.
**Current behavior**
The active workflow contains a duplicate list of all heavy jobs. It
cannot use the administrator-controlled runner gate.
**Proposed behavior**
The active workflow calls the synchronized trusted workflow at an
immutable SHA. The trusted workflow selects GitHub-hosted or isolated
AWS capacity from the validated contributor identity.
**Reason and benefit**
The thin caller prevents pull request changes from replacing the
external-runner security gate. It also keeps runner selection automatic.
**Breaking changes**
The check names gain the reusable workflow job prefix. AWS routing
remains disabled until the canary starts.
## What Changed
- Replaced the duplicated heavy CI job list with one reusable-workflow
call.
- Pinned the call to the reviewed default-branch commit.
- Limited the caller token to actions, contents, and pull request read
access.
## Verification
- actionlint on both workflow files
- Trusted-routing tests
- Confirmed the pinned SHA contains the workflow and is an ancestor of
master
- Full AWS and GitHub runner-boundary verification with routing disabled
## Risks
The check context names change when GitHub expands the reusable
workflow. The rollout verifies the new aggregate contexts before branch
rules change. The repository kill switch remains off during this pull
request.
## Model Used
OpenAI Codex with GPT-5, 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 a related public PR or described the issue
with the matching template fields
- [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
- [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
- [ ] I will address all Greptile and reviewer comments before
requesting merge
## Thinking Path
> - Paperclip uses pull request CI to protect changes before merge
> - The trusted reusable workflow will select isolated AWS capacity
> - The active workflow changed while the reusable workflow waited for
merge
> - The reusable policy must contain every current CI policy step before
activation
> - This pull request synchronizes the migration-order check and shell
validation
> - The benefit is one reviewed workflow version with verified job
parity
## Linked Issues or Issue Description
Refs #12436
**What existing behavior does this improve?**
This improves the pull request CI workflow synchronization before AWS
runner activation.
**Subsystem affected**
Cross-cutting CI automation.
**Current behavior**
The active workflow validates migration order. The new reusable workflow
does not yet contain that check.
**Proposed behavior**
Both workflow definitions contain the same heavy jobs and policy steps
before the active workflow becomes a thin caller.
**Reason and benefit**
The synchronization prevents policy drift during the two-step secure
rollout.
**Breaking changes**
None. AWS routing remains disabled.
## What Changed
- Added the current migration-order validation to the trusted workflow.
- Added the existing shellcheck intent annotation to the active
workflow.
- Verified normalized heavy-job parity between both definitions.
## Verification
- actionlint on both workflow files
- Local trusted-routing and normalized workflow-parity tests
- git diff --check
## Risks
Low risk. The migration check already runs in active CI. This change
copies it into the inactive trusted definition. AWS routing stays
disabled.
## Model Used
OpenAI Codex with GPT-5, 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 a related public PR or described the issue
with the matching template fields
- [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
- [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
- [ ] 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
> - Pull request checks protect the quality of the control plane
> - The current checks depend only on the shared GitHub-hosted runner
limit
> - Busy periods leave many pull request jobs queued even when external
capacity is available
> - Public pull request code must not select or directly access private
runner infrastructure
> - This pull request adds an inactive reusable workflow with a
fail-closed identity gate
> - A later pull request can pin this workflow by its full master commit
SHA
> - The benefit is automatic, controlled access to isolated runner
capacity without changing current CI during bootstrap
## Linked Issues or Issue Description
**What existing behavior does this improve?**
This improves the pull request CI workflow. It prepares the existing
checks to use an administrator-controlled runner selection.
**Subsystem affected**
Cross-cutting GitHub Actions CI configuration.
**Current behavior**
Every pull request job uses `ubuntu-latest`. Jobs wait when the
GitHub-hosted concurrency limit is full.
**Proposed behavior**
Add a reusable copy of the current PR workflow. A GitHub-hosted gate
validates durable numeric user IDs and current GitHub API state. The
gate emits one runner label. The default and every validation failure
use `ubuntu-latest`. The AWS label is possible only when an
administrator enables it and every identity check passes.
This bootstrap pull request does not change the active
`.github/workflows/pr.yml` caller. A follow-up change will call this
workflow by the full master commit SHA.
**Reason and benefit**
The split bootstrap creates an immutable trust boundary before external
runners are reachable. It also keeps CI automatic for contributors.
Contributors do not select a runner.
**Breaking changes**
None in this bootstrap pull request. The active PR workflow does not
change.
## What Changed
- Added an inactive `workflow_call` copy of the current PR checks.
- Added a GitHub-hosted routing gate that checks the repository ID, pull
request author ID, event sender ID, rerun actor ID, base branch, head
SHA, merge SHA, and current pull request state.
- Made every validation failure select `ubuntu-latest`.
- Pinned every third-party action to a full commit SHA.
- Disabled persistent checkout credentials for all jobs.
- Limited the workflow token to Actions read, contents read, and pull
request read access.
## Verification
- `actionlint .github/workflows/pr-trusted.yml`
- Ran the dedicated workflow routing test harness against
`.github/workflows/pr-trusted.yml`.
- Compared the job keys with `.github/workflows/pr.yml`. The new
workflow contains every existing job plus the gate.
- Verified each pinned action commit against its current GitHub
major-version tag.
## Risks
The gate could route a trusted pull request to the wrong runner if an
identity check is incomplete. The gate checks durable numeric IDs from
the event and current GitHub API state. It checks the rerun actor
separately. It defaults to GitHub-hosted capacity before any validation
runs.
This file is inactive in this pull request. The follow-up caller and
runner-group restriction must use the exact commit that reaches
`master`.
> 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 serving model ID and
context-window size are not exposed in this environment. The model used
high-reasoning, terminal, GitHub API, browser, and web-research
capabilities.
## 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
> - A release is gated by the release smoke: it installs the published
`paperclipai` artifact into a Docker container and drives the sign-in →
onboarding → first-agent path with Playwright
> - That suite runs only from the release pipeline, never on a pull
request, so it sees the UI only after the UI has already changed
> - The onboarding wizard was rebuilt into the agent arc. The "Name your
organization" step, the "Start Onboarding" launcher, and the agent role
picker are all gone
> - The spec still waited for those, so it failed on its first assertion
and blocked every nightly and beta release
> - The failure was also hard to read. The workflow uploaded no
container logs, because it learned the container's name only after the
harness succeeded, and the harness ran the container with `--rm` and
deleted it before anything read it
> - This pull request rewrites the spec to follow the current arc, and
repairs the log capture at both ends
> - The benefit is that nightly and beta releases are unblocked, and the
next failure arrives with the logs attached
## Linked Issues or Issue Description
No existing issue. Describing it inline, following
`.github/ISSUE_TEMPLATE/bug_report.yml`.
Refs #12274 (removed the company-naming step from the wizard).
Refs #12135 (the previous alignment of this spec, before #12274).
Refs #12316 (open; also edits `scripts/docker-onboard-smoke.sh`, in the
bootstrap helpers rather than the container lifecycle, so the two
changes do
not overlap. Whichever lands second should rebase and re-run).
**What happened?**
The release smoke fails.
`tests/release-smoke/docker-auth-onboarding.spec.ts`
never gets past its first wait:
```
✘ tests/release-smoke/docker-auth-onboarding.spec.ts:43:3 › Docker authenticated onboarding smoke › logs in, completes onboarding, and hires the lead agent
Error: expect(locator).toBeVisible() failed — element(s) not found (timeout 20000ms)
> 33 | await expect(wizardHeading.or(startButton)).toBeVisible({ timeout: 20_000 });
```
The spec waits for an `h3` reading "Name your organization" or a
"Start Onboarding" button. Neither exists. #12274 removed the
company-naming
step; the string now survives only in a code comment and in
`ui/src/components/OnboardingWizard.step.test.tsx`, which asserts it is
*absent*. The steps after the first wait are stale too: the CTA on step
1 is
"Continue" and not "Next", the organization input's placeholder changed,
and
the agent step's `#onboarding-agent-role` picker is gone, so every
onboarding
hire is filed under the neutral `general` role.
The suite runs only from the release pipeline, so nothing on a pull
request
saw the drift. Both `smoke_nightly` and `smoke_beta` call the same
reusable
workflow, so every nightly and every beta was blocked.
The failure also arrived without diagnostics. The job's "Capture Docker
logs"
step is `if: always()`, but it is guarded on `SMOKE_CONTAINER_NAME`,
which the
"Launch Docker smoke harness" step writes to `$GITHUB_ENV` only *after*
the
harness returns. On any failure before that the guard is false, the step
does
nothing, and the upload reports "No files were found". Below that,
`scripts/docker-onboard-smoke.sh` starts the container with
`docker run -d --rm`, so the `docker stop` in its EXIT trap deletes the
container and its logs together — and a container that crashes on its
own is
removed the instant its process exits.
**Expected behavior**
The spec walks the onboarding arc the app actually presents, and proves
the
company is created, the lead agent is hired, and the first task is
seeded and
dispatched. When the smoke fails, the run's artifact carries the
container's
logs.
**Steps to reproduce**
1. Run the Release Smoke workflow against a published artifact that
carries
#12274, or run it locally:
`PAPERCLIPAI_VERSION=2026.828.0-canary.3 SMOKE_DETACH=true
./scripts/docker-onboard-smoke.sh`
2. Run `pnpm run test:release-smoke` against that container.
3. The single spec fails at `openOnboarding()` after 20 seconds.
4. In CI, open the run's `release-smoke` artifact. It has no
`docker-onboard-smoke.log`.
**Paperclip version or commit**
`2026.828.0-canary.3` (commit 8316ceb0b).
**Deployment mode**
Docker.
**Installation method**
npm / pnpm global install (the container runs `npx
paperclipai@<version>`).
**Node.js version**
v24.20.0 inside the container.
**Relevant logs or output**
```
Running 1 test using 1 worker
✓ 1 [chromium] › tests/release-smoke/docker-auth-onboarding.spec.ts:76:3 › Docker authenticated onboarding smoke › logs in, completes onboarding, and hires the lead agent (7.0s)
1 passed (8.7s)
```
That is the result after this change. Before it, the same command failed
at
the first wait, as quoted above.
## What Changed
- `tests/release-smoke/docker-auth-onboarding.spec.ts` now follows the
current
arc. It signs in, opens `/onboarding`, names the organization and
presses
"Continue" (which creates the company and routes straight to the agent
step,
because onboarding no longer asks for a mission), names the lead and
presses
"Next", presses "Connect" on the default adapter to hire, then presses
"Get started" to launch.
- The spec addresses controls by role and accessible name, or by id
where one
exists (`#onboarding-agent-name`). Step 1's field has no id and no
associated
label, so it is found as the wizard's only text box rather than by its
placeholder copy.
- The spec asserts the hired agent's role is `general`, which is what
the arc
files every onboarding hire under. Every other API assertion is
unchanged.
- The spec navigates to `/onboarding` explicitly and drops any saved
onboarding
draft first, so it can run twice against one instance. The suite retries
once
in CI. It still asserts that a company-less board routes sign-in into
onboarding, guarded on the board actually being empty.
- `scripts/docker-onboard-smoke.sh` accepts `SMOKE_CONTAINER_NAME`,
drops
`--rm`, removes the container itself, and dumps `docker logs` to
`SMOKE_LOG_FILE` before the teardown.
- `.github/workflows/release-smoke.yml` pins the container name in the
job's
`env`, so every `always()` step has it before anything runs. The capture
step
refreshes the log from a live container when there is one, keeps the
harness's dump when there is not, and writes a one-line explanation when
there is neither. The upload's paths are literals, and
`if-no-files-found: error` makes a broken diagnostics path fail rather
than
warn.
- `scripts/docker-onboard-smoke.test.mjs` pins that wiring. It is added
to
`test:release-registry`, which runs on every pull request.
- `doc/DOCKER.md` documents `SMOKE_CONTAINER_NAME` and `SMOKE_LOG_FILE`.
## Verification
The spec was run against a real container built from the published
`2026.828.0-canary.3` artifact, exactly as the workflow runs it.
```sh
SMOKE_CONTAINER_NAME=release-smoke-onboard \
HOST_PORT=3232 DATA_DIR=<tmp>/smoke-data \
PAPERCLIPAI_VERSION=2026.828.0-canary.3 \
SMOKE_READY_TIMEOUT_SECONDS=420 SMOKE_DETACH=true \
SMOKE_METADATA_FILE=<tmp>/release-smoke.env \
SMOKE_LOG_FILE=<tmp>/docker-onboard-smoke.log \
./scripts/docker-onboard-smoke.sh
PAPERCLIP_RELEASE_SMOKE_BASE_URL=http://localhost:3232 \
PAPERCLIP_RELEASE_SMOKE_EMAIL=smoke-admin@paperclip.local \
PAPERCLIP_RELEASE_SMOKE_PASSWORD=paperclip-smoke-password \
PAPERCLIP_PLAYWRIGHT_CHANNEL=chrome \
pnpm run test:release-smoke
```
```
Running 1 test using 1 worker
✓ 1 [chromium] › tests/release-smoke/docker-auth-onboarding.spec.ts:76:3 › Docker authenticated onboarding smoke › logs in, completes onboarding, and hires the lead agent (7.0s)
1 passed (8.7s)
```
The same command was run a second time against the same, now non-empty,
instance. That covers the retry path, and it also passes.
The log capture was verified by making the container die during startup:
```sh
PAPERCLIPAI_VERSION=0.0.0-no-such-version \
SMOKE_CONTAINER_NAME=release-smoke-onboard SMOKE_LOG_FILE=<tmp>/fail.log \
./scripts/docker-onboard-smoke.sh
```
`<tmp>/fail.log` was written and carried the cause:
```
npm error code ETARGET
npm error notarget No matching version found for paperclipai@0.0.0-no-such-version.
```
The container was removed afterwards. On `master` this file is never
written,
because `--rm` deletes the container the moment its process exits.
The workflow's capture step was run by hand against three states: a live
container (258 lines), a removed container with the harness's dump
already on
disk (258 lines kept), and neither (a one-line explanation).
Unit coverage:
```sh
pnpm run test:release-registry # 93 tests, 93 pass
```
Nothing under `ui/` changed, so `pnpm --filter @paperclipai/ui
typecheck` was
not required. `tests/release-smoke` is outside the TypeScript project
references; Playwright compiles it at run time, which the runs above did
three
times.
## Risks
Low risk. Nothing ships to users. The change touches one Playwright
spec, one
smoke script, and one workflow.
Points worth a reviewer's attention:
- **This suite gates every nightly and beta, and it runs only
post-merge.**
`smoke_nightly` and `smoke_beta` both call `release-smoke.yml`, and no
pull
request runs it. Drift between the wizard and this spec is therefore
invisible until a release is already blocked, which is how this bug
reached
a release train. I think the arc deserves an earlier check. The cheapest
version is the one added here: `scripts/docker-onboard-smoke.test.mjs`
runs
on every pull request and pins the harness wiring. The full container
smoke
is too slow for the pull request path, but a UI-level test of the arc's
step
sequence would catch exactly this class of drift, and
`ui/src/components/OnboardingWizard.step.test.tsx` is already the right
home for it. I did not add it here, to keep this change to the repair.
- **Dropping `--rm`.** The container is now removed by the script's
cleanup
instead of by Docker. The script already ran `docker rm -f` before
starting,
and the workflow's final step removes it too, so a leaked container is
cleaned up on the next run either way. A developer who kills the script
with
`SIGKILL` will leave a stopped container behind, where previously they
would
not.
- **`if-no-files-found: error` on the upload.** The capture step now
always
writes the log file, so the upload always has at least one path to
match. If
that ever stops being true, the job fails instead of warning. That is
deliberate.
- **The spec drops the saved onboarding draft before it walks.** A stale
draft
makes step 1 skip company creation and hire into the previous run's
company.
That state only exists when the spec runs twice against one instance. A
fresh
release-smoke container never has it.
## Model Used
Claude (Anthropic), Claude Opus, 1M context, extended thinking, agentic
tool
use via Claude Code. The container, the Playwright runs, and the failure
injection were driven as real commands on a local Docker host.
## 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 that people use to manage AI agents
for work.
> - Paperclip applies database migrations in numeric order.
> - Two branches can create the same migration number before either
branch merges.
> - The existing repository check can find duplicates only after both
histories are present in one checkout.
> - A pull request must compare its new migrations with the target
branch before merge.
> - This pull request adds that comparison to the existing PR policy
job.
> - The benefit is an early failure with exact renumbering instructions.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
The PR policy check for files in `packages/db/src/migrations`.
**Current behavior**
A stale branch can add the same migration number as the target branch.
The existing check does not compare PR additions with the target branch
migration tip.
**Proposed behavior**
The policy job fails when a new PR migration number is not greater than
every migration on the target branch. The error names the conflict, the
next safe number, and the related files to update.
**Reason and benefit**
This prevents duplicate or out-of-order migration numbers from reaching
`master`. It also gives contributors and agents a direct repair
procedure.
**Breaking changes**
None. The change rejects migration numbering that is already unsafe.
## What Changed
- Added a dependency-free check that compares new PR migration files
with the target branch tip.
- Added the check to the existing PR policy job.
- Added tests for no-op, valid, duplicate, and lower-number cases.
## Verification
- `node --test '.github/scripts/tests/*.test.mjs'` passed 133 tests.
- `pnpm -r typecheck` passed.
- `pnpm build` passed.
- `pnpm test:run` passed 4,889 tests and failed 24 unrelated macOS path
and wildcard-listener tests that also affect the current `master`
checkout.
## Risks
- Low risk. The check reads Git history and does not modify migrations.
- The check permits gaps. It only requires each new migration number to
follow the target branch tip.
- The existing migration check continues to validate duplicate numbers,
snapshots, and journal entries inside the PR.
## Model Used
OpenAI Codex, GPT-5. The exact serving model ID and context-window size
are not exposed in this session. Reasoning, tool use, web access, 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 (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
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip supports opt-in Sentry error monitoring for server and
browser errors.
> - The hosted image must include the server package when an operator
sets SENTRY_DSN.
> - The server package is an optional peer in the source tree, so the
image did not include it.
> - This pull request installs the declared server package in the hosted
image and checks the result.
> - The benefit is a hosted tenant can send server errors without a
manual package install.
## Linked Issues or Issue Description
No public issue exists for this change.
**What happened?**
The hosted image did not include the declared @sentry/node server
package. A hosted tenant could set SENTRY_DSN, but the server could not
load the package from the image.
**Expected behavior**
The hosted image must include the exact @sentry/node version from
server/package.json. The self-hosted image must remain without this
optional package.
**Steps to reproduce**
1. Build or pull the hosted image.
2. Resolve @sentry/node from the server package path.
3. Compare its version with server/package.json.
4. Confirm that the tsx loader path still resolves.
**Paperclip version or commit**
Commit b6ff556a33ebdbe764b7f495951cd59009776608.
**Deployment mode**
Docker hosted image.
## What Changed
- Add a cloud-server-deps Docker stage that installs the declared
@sentry/node version in isolation.
- Copy the isolated package into the cloud image without changing the
production image.
- Add a probe that checks the tsx loader and the resolved Sentry
version.
- Run the probe after the hosted image push in the Docker workflow.
- Add server tests and update the observability documentation.
## Verification
- Run `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/cloud-image-sentry.test.ts`.
- Confirm that the changed test passes in CI.
- Confirm that all pull request checks pass.
- Note that the Docker workflow does not run for pull requests. It runs
after a push to master, for configured tags, or after manual dispatch.
## Risks
- Low risk. The production image body stays unchanged.
- The cloud image adds the declared Sentry package and a small
dependency tree.
- The workflow probe fails if the image loses the tsx loader or resolves
a different Sentry version.
## Model Used
OpenAI GPT-5; exact model version supplied by the execution service;
tool use and code execution; context window not specified.
## 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
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agent runs execute inside the server container, and they spawn many
short-lived descendants: git, the adapter CLI, esbuild, sh
> - The server image sets `ENTRYPOINT ["docker-entrypoint.sh"]`, and
that entrypoint ends in `exec`, so node becomes PID 1
> - Node reaps only the children it spawned itself. It installs no
`SIGCHLD`/`waitpid` handler for orphans that the kernel re-parents onto
PID 1, so those orphans stay as zombies forever
> - Zombies accumulate monotonically. When the cgroup pid limit is
reached, every `fork()` in the container fails and the instance is dead
> - This pull request installs `tini` and makes it PID 1 in front of the
existing entrypoint, adds a behavioural test that proves reaping, and
adds a `pids_limit` backstop to both compose files
> - The benefit is that a long-running container no longer degrades into
total fork failure, and a future regression is caught by CI instead of
by an outage
Depends-on: none — this change is self-contained in the image build and
its tests, and it touches no other in-flight branch
## Linked Issues or Issue Description
No public GitHub issue exists for this defect. It was found on a live
long-running instance. Description follows the bug report template.
**What happened?**
The server container ran for 22 hours and reached 2039 of 2048 pids in
its cgroup. Of 1760 processes, 1731 were zombies, and all 1731 had PID 1
as their parent. PID 1 was `node --import
./server/node_modules/tsx/dist/loader.mjs server/dist/index.js`. Zombies
accrued at about 79 per hour and were never reaped. The oldest zombie
was 20.8 hours old against a container uptime of 22.0 hours, so nothing
had been reaped since boot. Once the pid limit was reached, `git` and
`gh` failed with `pthread_create failed: Resource temporarily
unavailable`.
**Expected behavior**
PID 1 reaps orphaned processes that the kernel re-parents onto it. The
pid count of a long-running container stays flat instead of growing
without bound.
**Steps to reproduce**
1. Start the server image without `docker run --init` and without `init:
true`.
2. Run agent work that spawns descendants which outlive their immediate
parent.
3. Read `/sys/fs/cgroup/pids.current` and count processes in `Z` state
over several hours.
4. The zombie count grows monotonically and every zombie has PPID 1.
**Relevant logs or output**
```
cgroup pids.current / pids.max : 2039 / 2048
total processes : 1760
zombies : 1731 (98.4%)
parent of every zombie : PID 1 (1731/1731)
PID 1 cmdline : node --import .../tsx/dist/loader.mjs server/dist/index.js
container uptime : 22.0 h
oldest zombie : 20.8 h median: 14.4 h
zombie names : git 717, claude 280, MainThread 167, sleep 141,
esbuild 138, postgres 76, sh 65, sccache 50
```
**Additional context**
The fix pattern is already in this repository.
`docker/agent-runtime/Dockerfile.base` installs `tini` and sets
`ENTRYPOINT ["/usr/bin/tini", "--"]`. It was never applied to the server
image.
## What Changed
- `Dockerfile`: install `tini` in the `base` stage and set `ENTRYPOINT
["/usr/bin/tini", "--", "docker-entrypoint.sh"]`. The entrypoint stays
in the exec chain, so UID/GID remapping, `gosu`, and graceful shutdown
are unchanged.
- `scripts/assert-orphan-reaping.sh` (new): a behavioural probe. It
spawns a leader that forks a grandchild, exits the leader, and asserts
that the orphaned grandchild leaves `Z` state instead of persisting. It
fails closed if the grandchild is not re-parented onto PID 1, so a pass
cannot mean the check ran too early.
- `.github/workflows/docker.yml`: run that probe against the pushed
image after the publish step. The publish step is multi-arch with `push:
true`, so nothing is loaded into the runner daemon and the pushed tag is
the only thing to test. The cloud variant is `FROM production` and
inherits the same `ENTRYPOINT`.
- `scripts/docker-build-test.sh`: run the same probe against a local
build.
- `docker/docker-compose.yml` and
`docker/docker-compose.quickstart.yml`: add `pids_limit: 2048` as a
backstop, so a future leak dies visibly at its own ceiling instead of
starving the host of pids.
- `server/src/__tests__/container-init-reaping.test.ts` (new): 13
assertions that guard the configuration the probe depends on.
No per-orchestrator init lever was added. The image owning PID 1 covers
compose, plain `docker run`, the quadlet units, and the ECS task
definition in one place. Adding `init: true` in compose or
`initProcessEnabled` on the ECS task would nest a second init around
`tini`, and `tini` then warns on every boot that it is not PID 1. The
new test asserts the absence of both levers across all three manifests,
so the decision survives the next edit.
## Verification
| Check | Result |
|---|---|
| `scripts/assert-orphan-reaping.sh` against a real init | Grandchild
re-parented to PPID 1, then reaped. Exit 0. |
| Same probe forced against a genuine zombie | Reports `Z` and fails.
The failure branch is not vacuous. |
| Config guard against the pre-fix files | Exactly the 3 relevant
assertions turn red. |
| Config guard with `tini` removed from `apt-get` but the comments kept
| Red. It checks the install, not a mention of the name. |
| `cd server && npx vitest run
src/__tests__/container-init-reaping.test.ts` | 13 passed |
| `npx tsc --noEmit -p server` | Clean |
| `node scripts/check-docker-deps-stage.mjs` | PASS |
| `node --test scripts/release-verify-workflow.test.mjs` | 8 passed |
Not verified locally: no container runtime is available in the authoring
environment, so the probe has not run against a build of this image. The
new `docker.yml` step runs it against the pushed image on this PR.
## Risks
Low risk, but it is an image and entrypoint change, so it affects
deployments.
- `tini` adds one small package to the `base` stage.
`docker/agent-runtime/Dockerfile.base` already installs it from the same
Debian archive.
- Signal handling changes shape: `tini` receives `SIGTERM` and forwards
it to the entrypoint, which `exec`s node. `tini` forwards signals to its
direct child by default, and the exec chain keeps node as that child, so
graceful shutdown is preserved. A reviewer should confirm this on a real
stop.
- `pids_limit: 2048` is new for compose users. A deployment that
legitimately needs more than 2048 processes would now hit the ceiling.
The measured steady state on a busy instance was under 400.
- If a deployment already passes `--init` or `init: true`, `tini` runs
under another init and prints a warning that it is not PID 1. Reaping
still works because the outer init handles it. The compose files in this
repository do not set `init: true`.
## Model Used
Claude Opus 5 (`claude-opus-5`), extended thinking, with tool use and
code execution in an agent harness.
## 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 issues or links
- [x] My branch name describes the change and contains no internal
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 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: zannis <1011451+zannis@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner package now has protocol, transport, provider, catalog,
and authorization foundations.
> - Its first upstream package boundary should expose only the
implemented runtime and test-helper surfaces.
> - Rust correctness belongs in the repository existing build
verification, without introducing a parallel release process.
> - Direct package creation must build the files declared by the package
manifest.
> - This pull request defines the minimal package API and verifies the
optimized runner binaries in the existing PR and release Build jobs.
> - The benefit is a production-ready runner package boundary with
minimal build-process change.
## Linked Issues or Issue Description
Refs #11962
This pull request replaces one bounded part of the archived large runner
change. It follows the package-local authorization change in #12126.
## What Changed
- Export only `@paperclipai/paperclip-runner` and
`@paperclipai/paperclip-runner/testing`.
- Keep Node-only fixture loading and semantic conformance helpers out of
the runtime root.
- Add a provider-neutral semantic conformance kit with stable JSON
comparison and fail-closed input checks.
- Keep deferred SDK, eval, browser, React, lab, and command surfaces
private.
- Pin the runner Rust toolchain to 1.97.1 with the minimal profile and
`rustfmt`.
- Run the Rust workspace tests in release mode.
- Launch the optimized `paperclip-runnerd` and fake-harness binaries in
process-level integration coverage.
- Add one `pnpm --filter @paperclipai/paperclip-runner check:all` step
to each existing PR and release Build job.
- Make the existing server `prepack` lifecycle run its existing build
after it prepares UI assets.
- Document that no production adapter starts runnerd yet.
This revision adds no standalone GitHub Actions job. It adds no server
runner dependency or runner vendoring. It adds no Docker bootstrap or
clean-consumer harness. It does not change `pnpm-lock.yaml`.
## Verification
- `pnpm --filter @paperclipai/paperclip-runner check:all`
- 66 TypeScript tests
- 8 protocol contract tests
- 56 Rust unit and integration tests
- Release-mode integration coverage launches the optimized runnerd and
fake-harness binaries.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/server-package-build-script.test.ts` (2 tests)
- Clean `pnpm pack` from `server/` rebuilt the server and produced both
`package/dist/index.js` and `package/dist/index.d.ts`.
- `node --test scripts/__tests__/release-verify-workflow.test.mjs` (8
tests)
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm check:token-gates`
- `git diff --check`
- No `pnpm-lock.yaml` diff.
- The diff changes 12 files.
## Risks
The runner adds Rust work to the existing Build jobs. These jobs can
take longer on a cold cache. The pinned toolchain makes contributor and
CI behavior reproducible. Cargo tests use `--release` to verify
optimized executables. The server prepack lifecycle now performs the
build that its published entry points require. This can make direct
server packing slower. This pull request does not wire runnerd into the
server. It does not select runnerd for any adapter. Existing application
execution and finalization paths remain unchanged.
## Model Used
OpenAI Codex with GPT-5. Agentic coding mode used repository tools, code
execution, and automated tests.
## 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
Co-authored-by: Paperclip <noreply@paperclip.ing>