Commit Graph
4 Commits
Author SHA1 Message Date
42ecc96556 perf(ci): prefetch the consumer image and Grok binary during package staging (#15849)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Pull requests to this repository run a large CI matrix. The slowest
check sets the time a contributor waits for a green run.
> - After #15826 leveled the server shards and #15831 split
`workspaces-b`, the `Build` lane is the slowest check: 634s in run
38107578322 (2026-10-11 03:11Z), ahead of the next server shard at 574s.
It was 533s in run 38107430861 and 543s to 568s in earlier runs that
night.
> - `Build` is about 100s of setup, 148s to 195s of `pnpm build`, and
278s to 320s of `scripts/verify-grok-npm-install.mjs`. The verifier
stages and packs the public packages first (84s to 100s of CPU work),
and only then pulls the pinned `node:24-trixie` consumer image (8
layers, 440 MB compressed) and downloads the Grok binary (65 MB gzip,
166 MB verified). Both network waits sit after the staging work on the
critical path.
> - Neither download depends on the packed packages. The image is pinned
by digest and the Grok binary is checked against its pinned sha256 by
the provisioning script, so starting them early changes no verified
property.
> - This pull request starts both downloads as background children
before staging and joins them where they are needed: the image before
the first sandbox, the binary after the negative probe. It also prints
one timing line per phase so the next slow part is visible in the CI
log.
> - The benefit is that the whole pull and the whole Grok download hide
behind the staging phase. The verifier should shed the full pull time on
a fresh runner, and the next cycle can read the phase split instead of
inferring it from log gaps.

## Linked Issues or Issue Description

**Describe the enhancement**

The `Build` PR check is now the slowest check in the run. Its last step,
the public npm install verifier, pulls the consumer Docker image and
downloads the Grok binary only after it has finished staging and packing
the eighteen public packages. Both downloads could run while the
packages are staged.

**Why is this needed**

The slowest check decides the wall-clock time of every PR run. In the
most recent fully green run (38107578322), `Build` took 634s: `pnpm
build` 195s and the verifier 320s. The verifier's log shows the staging
phase ends at +100s and the Grok result arrives at +267s. The image pull
and the Grok download are inside that second window, in series with the
consumer install and the probes.

**Proposed solution**

Start `docker pull --platform linux/amd64 <pinned image>` and
`provision-grok.mjs` as background children at the top of the verifier,
before the staging loop. Await the image right before the first sandbox
call. Provision Grok into a prefetch directory and rename it into
`native/grok` only after the "npm must not provision Grok" assertion and
the negative probe, so that assertion keeps its meaning and only the
positive probe sees the binary. Print a
`paperclip.public-npm-install.timing.v1` line with wall time per phase
and the time the main flow waited on each prefetch.

**Alternatives considered**

- Run the verifier as its own job in parallel with `Build`. It needs the
full `pnpm build` output, so that job would be as long as `Build` is
now.
- Warm the consumer npm cache from an Actions cache. That changes the
"clean public npm install" property the verifier reports. Not done here;
the new timing line will show whether the consumer install is worth that
discussion.
- Pack the eighteen packages concurrently. The three bundled packages
run their own npm installs in series inside `prepareBundledPackage`,
which is synchronous. A larger restructure; left for a later cycle with
timing data.
- Overlap the Pi runtime setup with the Grok probes. Both use the same
consumer directory from separate containers, and the probes after
provisioning are short. Not worth the concurrency on shared state.

## What Changed

- `scripts/prefetch-child.mjs` (new): `startPrefetch({ name, cmd, args,
cwd, env, logPath })` spawns a background child with no inherited
terminal and its stdout and stderr in a log file, and returns `{ name,
child, done, durationMs }`. `done` resolves on exit 0 and otherwise
rejects with the tail of the log; the rejection is observed inside the
helper, so an unawaited failure never becomes an unhandled rejection.
`durationMs` ends at the child's last write to its log (the exit event
is observed late when the caller is blocked in synchronous work; both
downloads print their final status line as they complete).
`stopPrefetches(prefetches, { graceMs })` sends `SIGTERM` to every child
still running, escalates to `SIGKILL` after the grace period, and
resolves only once all of them have exited.
- `scripts/verify-grok-npm-install.mjs`: the image pull and the Grok
provisioning start through the helper inside the `try` block, before the
staging loop. The main flow awaits the image before the first `npm
install` sandbox and awaits Grok after the negative probe, then renames
the staged binary into `native/grok` before the positive probe. The
`finally` block awaits `stopPrefetches` and only then removes the
scratch root, so cleanup never races a child's writes. The script prints
one timing JSON line after the Pi probe, with the per-phase wall times,
the two `*_wait_ms` values, and the corrected `*_prefetch_ms` values.
- `scripts/__tests__/prefetch-child.test.mjs` (new): seven tests run
small local Node children, with no Docker or network: a successful
prefetch resolves and keeps its output; the duration ends at the
download even when the parent is blocked for 1.5s; a nonzero exit
rejects with the log tail; a command that cannot start rejects instead
of hanging the join or the stop; stopping a running child waits for its
exit; a child that ignores `SIGTERM` is killed after the grace period;
an unawaited failure does not produce an unhandled rejection in a fresh
process.
- `scripts/__tests__/grok-public-install-sandbox.test.mjs`: the source
test asserts that exactly the pinned image is pulled for `linux/amd64`,
that Grok is provisioned into the prefetch directory and never straight
into `native/grok`, that both prefetches start before staging, that the
image is joined before the first sandbox, that the rename happens after
the npm assertion and the negative probe and before the positive probe,
that the verifier imports the helper, and that `stopPrefetches` is
awaited before the single `rmSync(root, ...)`.
- `package.json`: `test:release-registry` runs the new test file.

## Verification

- `node --test scripts/__tests__/prefetch-child.test.mjs
scripts/__tests__/grok-public-install-sandbox.test.mjs
scripts/__tests__/cursor-public-install-sandbox.test.mjs
scripts/__tests__/retain-runner-qualification-packages.test.mjs`: 22
pass (7 new in `prefetch-child.test.mjs`, 12 in the sandbox file). The
new file passed on three consecutive runs.
- `node --check scripts/verify-grok-npm-install.mjs` passes.
- `node packages/paperclip-runner/scripts/provision-grok.mjs
<scratch>/prefetch/grok` on this host: `Verified Grok 1.0.13
(linux-x64)` in 1.0s, `native/grok` absent before the move, 166079904
bytes in place after it.
- Docker is not installed on the host that produced this PR, so the
verifier was not run end to end locally. This PR's own `Build` job runs
`node scripts/verify-grok-npm-install.mjs` from the branch checkout, so
it exercises the change in full.

**Measured on this PR's first CI run**
([38108964834](https://github.com/paperclipai/paperclip/actions/runs/38108964834),
created 03:37Z), against two `master` runs created in the same twelve
minutes:

| | 38108878166 (`master`, 03:35Z) | 38108995800 (`master`, 03:37Z) |
38108964834 (this PR, 03:37Z) |
| --- | --- | --- | --- |
| Verifier step | 307s | 248s | 219s |
| `Build` job | 555s | 484s | 459s |
| Slowest check in the run | `Build` 555s | server (8/12) 535s | server
(12/12) 547s |

The PR run's timing line: `stage_packages_ms 84464`, `image_wait_ms 1`,
`consumer_install_ms 97305`, `lifecycle_rebuild_ms 3545`,
`codex_probe_ms 1211`, `grok_wait_ms 0`, `grok_probes_ms 626`,
`pi_setup_ms 22194`, `pi_probe_ms 7753`. Both waits are at most 1ms, so
the staging phase hid both downloads in full. The window from the end of
staging to the Grok result went from 161s to 167s on the two runs that
motivated this PR to 102s here, so the hidden downloads were about 60s
on a fresh `runs-on` spot instance. Against the two same-window `master`
runs, the verifier step is 29s to 88s shorter and `Build` is 25s to 96s
shorter; the spread is runner variance in `pnpm build` and the consumer
install.

The `image_prefetch_ms` and `grok_prefetch_ms` values on that first run
(84550 and 186696) were the parent's late observation of each exit, not
download times; that is the first Greptile finding, fixed by the second
commit, which measures from the download's last output instead.

## Risks

- Low risk. The verifier proves the same properties in the same order.
The only ordering change is that the downloads start earlier; every
assertion, probe, and sandbox argument is unchanged.
- The Grok binary is written under the scratch root in a `prefetch`
directory during staging. That directory is never mounted into a
container, and the `native/grok` assertion still runs against an absent
path.
- A prefetch that fails early now fails the step at its join point,
where the old code would have failed at the same download a few seconds
later. Same outcome, same message source.
- `*_prefetch_ms` is a last-output time. A child that printed early and
then kept working silently would be under-reported; neither `docker
pull` nor `provision-grok.mjs` behaves that way (both print their final
status as they finish), and a silent child falls back to the observed
exit time.
- Cleanup now waits up to 5s for a child that ignores `SIGTERM` before
killing it, so a failing run can end up to 5s later than before. Both
children exit promptly on `SIGTERM` in practice.
- The timing line is one more stdout line. Nothing in the repository
parses the verifier's stdout; the two workflows that run it write the
output to the job log or to a file.

## Model Used

- Claude (Anthropic), model ID `claude-fable-5-1`, running as a
Paperclip agent through the Claude local adapter with tool use (shell,
file edits, GitHub CLI). The agent collected the CI data, made the
change, ran the tests, and wrote this description.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [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

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-10 21:10:13 -07:00
DottaandPaperclip 57e977be72 feat: integrate Pi 1.0 into the experimental Runner (#14921)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - The experimental Runner owns provider processes and durable
sessions.
> - Pi needs working task execution and human controls.
> - The five-PR stack must preserve changes already on master.
> - Each layer now carries the complete integrated source for a safe
sequential fallback.
> - This PR belongs to native GitHub stack #15602, ending at #14956.

## Linked Issues or Issue Description

Refs #14436, #14631, #14743 and #14956.

Ship Pi 1.0 through the experimental Paperclip Runner. The five PRs are
#14921, #14922, #14923, #14924 and #14956. The user authorized the
complete merge after checks pass. Existing `pi_local` execution is
unchanged. Accounting and wider provider/platform qualification remain
deferred.

## What Changed

- Recover missing final replies after workspace finalization changes
owners, using accepted-turn evidence without rerunning work or granting
external-chat publication.
- Preserve the admitted Pi instruction root across warm runs, while
retaining changed-root rejection.
- Give Pi a bounded 15-second default shutdown grace so stop, drain
acknowledgement and durable suspension can complete. Explicit deadlines
and other providers retain their existing behavior.
- Integrate the Pi 1.0 runtime and master contracts.
- Use Pi profile 22. Preserve explicit caller-selected models and exact
native thinking levels. Keep Pi's wrapper, helper, extension and
question/control behavior unchanged from the qualified profile-19
runtime.
- Preserve master's Dot lifecycle and consent fields, configured task
environment, status guards and current Codex/Claude dependency versions.
Cursor stays qualified. Copilot stays pending; profile 17 binds the
changed shared protocol validation sources.
- Exclude general AWS IAM credentials from Pi static/custom provider
bindings and selected task projections; preserve the provider-scoped
Bedrock bearer key. Profile 21 is retained as historical provenance.
Rust and cloud install probes use the current declaration.
- Patch bundled brace-expansion 5.0.9 to the exact official 5.0.12
payload. Pin the patch and complete runtime closures. Include the patch
in normal installed setup tooling. Keep the upstream Pi shrinkwrap as
provenance and permit only this exact security correction.
- Include current attestation files in the Docker build context. Keep
the repository lockfile unchanged from master. CI and private image
builds resolve manifest changes before their frozen installation.

## Verification

- Full local `pnpm -r typecheck` passes, including Runner Rust, server
and UI. Focused integration checks pass: 194 Runner
admission/environment tests, 63 profile/credential tests with one
expected skip, 152 Dot/UI configuration tests, and Pi transcript/notice
tests.
- Full local `pnpm build` passes on the final source.
- Fresh final-source checks pass: all 698 Rust workspace tests (32
binaries), 156 credential/profile/controller tests with one expected
skip, Runner TypeScript typecheck, and 20 package/setup/sandbox tests.
- The profile-21 Pi materializer passes on the native host with the
official pinned Node 24.21.0 and its npm. It verifies all 150 locked
packages, the patched dependency and the exact closure. Setup/package
bundle tests and UI token gates pass.
- The old hashes were reproduced for all three supported targets before
calculating the patched graph. New closure hashes are darwin-arm64
`282022db10150c6632b3444df421342e7d534bdf5d5fb1097a2e79d0625a2bcf`,
darwin-x64
`64e251e19009f755c0b04f73ce2138246faab71a961b0f13d75ebfcc34bef12e`, and
linux-x64
`713b1fdff42fb56a1518bdc084f181d70bee8ebadc3e4b1d76321ed9108c8410`.
Independent native platform execution is separate from graph identity
reproduction.
- Historical cloud qualification remains unchanged: all seven core cases
pass on shipping source `10dc43c9ec65d88c2f782d62afb296d09494f215`,
harness `1a4408a48cfb5a1f094a311141c257c92cd7a893`, image
`sha256:5b3a775b383591bda1b0c1889e509acc70ce7f37c53f09733c81d59037f02280`,
and accepted Sonnet 4.6/low fixture. All 215 canonical files and all
seven cleanup checks pass independent verification. These are profile-19
results and are not relabeled as fresh profile-22 runs.
- Current Pi digest:
`sha256:e92078bee3c23bec4100aa589013a44613d054cd686826534025d8019e9f39a9`.
[The readiness
plan](https://github.com/paperclipai/paperclip/blob/codex/pi-production-readiness/doc/plans/2026-10-02-pi-production-readiness.md)
preserves campaign and failed-attempt provenance.
- Merge only after every PR's current-head CI and fresh review pass.
Linux CI covers the full suites, build and browser tests. The local
embedded Postgres API-authority suite cannot start on this macOS/Node 26
host, so Linux CI must confirm that suite.

### Fresh profile-22 core qualification — 2026-10-08

All seven accepted core cases pass canonically on Pi profile 22, with
`openrouter/anthropic/claude-sonnet-4.6` and native-confirmed low
thinking. This model is a fixture; production accepts the caller's
explicit Pi provider/model.

Runtime/install source: `3241a992f2a7703e59e97ed0fd3e5d6405de4401`.
Frozen accepted harness: `1a4408a48cfb5a1f094a311141c257c92cd7a893`.
Immutable cloud image:
`ghcr.io/paperclipai/paperclip-daytona-runner@sha256:506f22db7edd78f37c0c40bec1cc084af1850455026dbf467194bfbb8fcef141`.
Pi digest:
`sha256:e92078bee3c23bec4100aa589013a44613d054cd686826534025d8019e9f39a9`.

[Hosted Linux image and clean-install
verification](https://github.com/paperclipai/paperclip/actions/runs/37868328023)
passes, including all 20 source-bound archives, normal CLI/Pi setup,
companion import and the production pack reader. This exact installation
source includes the latest master integration and the corrected Pi warm
instruction-root fence. Full local typecheck/build and current-head
hosted CI verify the final stack. All 13 focused real-root regressions
pass. The full local executor suite passed 662 tests; 15 database tests
could not start the Mac embedded PostgreSQL service. Hosted Linux CI
passes the full required verification and E2E checks. These fresh
results keep their own source identity; profile-19 results remain
historical.

| Core path | Canonical campaign | Retained archive SHA-256 |
| --- | --- | --- |
| File edit, validation, download and Done |
`pi-core22-replyfix-0-1791511228` | 23 files;
`a473e8603a3dd4737863291f8d3d1e392391f0b16d433c3e0e0e9d8baf7a97b0` |
| Pending question and controller restart |
`pi-core22-replyfix-1-1791511376` | 33 files;
`6b829c4eb74e1f32a89c692a4ae7130dbfc1c6d3cf13915effe2103d9e242c8e` |
| Three-turn session/process/workspace continuity |
`pi-core22-replyfix-2-1791511587` | 23 files;
`7a87021f8f9a3fdd3c58bb4467f8d82c635e3ea4795d6e75f144d9aa14818df8` |
| Four typed questions and browser reconnects |
`pi-core22-replyfix-3-1791511881` | 42 files;
`9e31755252be1f4f9cb0626c984c142d4d1ae5f5bee3a7af08444db8d12c280a` |
| Plan approval and completion | `pi-core22-replyfix-4-1791512031` | 22
files;
`a0383ce1aab38e7b5a25ce0e9dd3bebea5c037ebd96ae6b29dae19015da2ae2c` |
| Same-turn steering and permission denial |
`pi-core22-replyfix-5-1791512261` | 39 files;
`c929b8c7070f0b66aedc17e65ca46e6beab1e363926ac9f7e2a75fb250f05949` |
| Stop during pending permission | `pi-core22-replyfix-6-1791512390` |
33 files;
`7f0a58ae0f4d5bfc76149435f4e322537089c5bd16e7ffe9b5ad71f10a621a07` |

All 215 canonical files (28714587 bytes) are independently
hash-verified. All seven cleanup grades pass, with no owned runtime
process or temporary root after each case. Automatic retries are zero.
The owned cloud host stopped normally after retention. The prior
profile-22 warm attempt remains failed and separately retained: archive
SHA-256
`1e54eba5ec72b50cee1534b23d1d1d4f21a090006b8a64501ba70db972abfde5`. Its
original canonical classification is preserved. Diagnosis reproduced a
product bug comparing an agent-files root against an unset
checkpoint-only field. The fix stores the admitted physical root
separately from the adopted per-run collection capability. The real-root
regression fails before the fix and passes afterward, including
rejection of a changed physical root. Fixture, grader, model and all
seven accepted case IDs are unchanged; this fresh campaign tests
final-reply publication after file registration first. The intermediate
restart attempt also remains failed and retained: archive SHA-256
`5dcaefdf1d17cf4cd54fd4cf810f45e736667392339b8ce7caf08bb4e225277f`. Its
original canonical classification is preserved. Pi resumed, wrote the
verified answer and completed its task; exact runner suspension was
proven, but idle stop consumed about 5.2s and left under 3s for the
drain acknowledgement. The Pi-only default shutdown grace is now 15s,
preserving a full 5s drain round trip and a finite suspension reserve.
Explicit caller deadlines, other provider defaults, literal drain
receipts and exact suspension identity checks remain unchanged. The
timing regression fails before this correction and passes afterward; all
18 focused settlement tests and Runner typecheck pass. The final-source
file attempt is also preserved as failed (`candidate_failure`), archive
SHA-256
`db6767b6773ea618997927ac77bdb005a5ac81492c7b9c0ffbc900449f829bc9`.
Native edit, validation, exact downloadable artifact and Done/succeeded
all passed, and the exact final reply was durably recorded. A workspace
recovery owner completed before the live heartbeat reached presentation,
leaving that reply absent from task chat. Recovery now materializes only
a completed final reply from the accepted turn of an ordinary internal
Done task, preserving issue/run/contract binding, suppression,
external-chat authorization and same-run deduplication. The database
regression covers the generated file-preparation receipt, suppression,
unapproved external continuation and replay. Server typecheck and all 49
response-selection tests pass; hosted Linux verifies the database
regression because embedded PostgreSQL cannot start on this Mac.

The delayed-final-answer database regression passes on [the final
root-source Linux server
shard](https://github.com/paperclipai/paperclip/actions/runs/37868262553/job/113628594152),
alongside 1,108 passing tests. The first root Runner shard had one
unchanged durable-resume test exceed its 5-second timeout; the identical
top-source shard and the isolated exact test passed. One rerun of that
failed job and its required aggregate passed without source or test
changes. The original failed job log and the single-rerun receipt remain
retained.

### October 9 merge verification

Current merge head: `5a8fe63512a7166aaef5cf50065a25008aa8b44b`. All
current-head checks pass, including `ci / verify` and `ci / e2e`;
exact-head Greptile review is 5/5 with no unresolved threads. Current
master conflicts are resolved. The user authorized the maintainer
override of the code-owner review gate after these checks. The seven
retained live core cases remain bound to source
`3241a992f2a7703e59e97ed0fd3e5d6405de4401` and its recorded cloud image.

## Risks

- The security correction changes the dependency closure and profile
identity. Old sessions must reopen on the new profile. Exact identities
and credential bindings fail closed.
- The runner remains experimental and requires explicit selection.
Legacy Pi Local is unchanged. Caller model IDs pass through; the E2E
model is a fixture.
- Accounting and the broad platform/provider matrix remain deferred.
This merge does not publish a release or deploy a service.

## Model Used

OpenAI GPT-6 through Codex assisted with reasoning, repository
inspection, editing and tool use. The exact serving ID and context
window are not exposed in this session. Final live qualification uses Pi
1.0.0 with `openrouter/anthropic/claude-sonnet-4.6` and native-confirmed
low thinking.

## 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
#` 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>
2026-10-09 08:56:41 -05:00
DottaandPaperclip 6f9d0a56ba fix: resolve installed Codex and preserve npm host dependencies (#15555)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Installed agents need their own runtime dependencies.
> - Native Codex startup and browser login could depend on a global CLI.
> - npm bundles do not inherit workspace dependency overrides or
patches.
> - Codex native packages must come from npm for the consumer host.
> - This PR fixes executable resolution and the required npm dependency
layout.
> - Usable installed CLI versions can run without a numeric version
gate.

## Linked Issues or Issue Description

Refs: #15422. This is a prerequisite for the later Codex-default change.

**What happened?**

Packaged native Codex startup and browser login could require a global
Codex CLI. The server's vendored runner lacked its own declared Codex
bridge. A bundled wrapper could retain producer-host binaries or select
the legacy adapter's separate platform version.

**Expected behavior**

Prefer the installed Codex executable. Accept usable older or newer
versions. Use the selected host's PATH when the dependency is absent.
Keep the patched JavaScript graph. Let npm install official native
packages for the consumer platform. Grant sandbox reads only to the
selected vendor resources.

**Steps to reproduce**

1. Prepare a clean npm installation without a global Codex command.
2. Start native Codex or its browser login.
3. Inspect the selected executable, installed host package, and sandbox
resource paths.

**Paperclip version or commit**

Frozen master base: `1881894973a2b25838d8abed9bd8aeebc3af4441`.

**Deployment mode**

Source and packaged self-hosted installation.

## What Changed

- Share installed Codex command resolution across native startup, direct
evals, and browser login. Accept usable version differences. Preserve
explicit commands, recorded sessions, remote host boundaries, and the
Linux ARM64 legacy login path. Return safe errors for missing
executables and failed login terminals.
- Declare the server's Codex bridge. Retain the patched JavaScript
dependency graph in npm packages. Strip Codex native payloads. Declare
official optional host packages on the published server manifest so
native and legacy versions remain separate. Preserve consumer esbuild
platform dependencies.
- Adjust resource lookup for npm's separate platform packages. Retain
package identity, path containment, resolver ownership, and narrow
sandbox reads. Keep exact version and digest checks for explicit ACPX
artifact qualification.
- Extend existing packaging, installed-consumer, login, selection,
recovery, and integrity tests. Document the retained behavior.

The diff is now 23 files, 1,709 additions, and 53 deletions. The prior
diff had 35 files and about 3,700 changed lines. Removed work is
preserved on `codex/runner-packaging-full-snapshot` at
`6f3060beaa842ffcee21af058370d3cab5f571e9`.

Removed from this PR: release workflows and assembly, provider-pack
changes, Docker materialization, Git installer changes, extra login
HOME/working-directory isolation, and unrelated CI fixture repairs. This
PR does not change agent defaults, stored runner choices, UI, schema, or
provider qualification.

## Verification

- Final candidate: `dde37d7ed0121c10b60b8801eda80f8dc17909ad`. [All 47
ordinary CI jobs
passed](https://github.com/paperclipai/paperclip/actions/runs/37842288835)
on attempt 1, including typecheck, tests, build, E2E, runner checks, and
the installed-consumer canary. [Fresh Greptile
review](https://github.com/paperclipai/paperclip/pull/15555#issuecomment-6059581546)
is 5/5 on this head; no unresolved threads remain. Human approval is
still outstanding.
- Reused focused controls passed with Node 24: 71 initial narrowed
checks, then 56 affected Codex/selection/eval checks after the relative
PATH correction. Runner TypeScript no-emit, syntax, and diff checks
passed. The existing fixture reproduces the original relative PATH
failure and verifies working-directory selection, empty entries,
ordering, and absolute launch. One CLI entrypoint test was initially
blocked by sandbox IPC and passed with its existing local socket
allowed. Login HOME, config-directory assignments, and working directory
match master.
- [The actual clean Linux npm
consumer](https://github.com/paperclipai/paperclip/actions/runs/37842288835/job/113535523044)
passed on the final candidate's CI integration. All 17 Paperclip
tarballs omit native Codex payloads. Official npm host packages retain
their own integrity and `inBundle=false`. Native Codex selects 0.160.0;
the legacy closure retains 0.156.1. Package admission, command leases,
narrow native sandbox resources, consumer hooks, preserved lock, and
offline lifecycle controls passed. Provider calls were zero. No new
workflow is added.
- Actual official Codex 0.156.1 passed on the final committed source on
macOS ARM64: installed dependency preference, absolute and relative PATH
fallback, narrow resource lookup, and app-server initialize/initialized.
Executed module hashes match the candidate. One scripts-disabled
install, three version probes, and one handshake completed in 26
seconds; owned files and process group were removed. No login,
account/model request, or provider task ran.
- CI checked out `dfda8e708e87306d22ed735d78bdfcbe770e99b7` on base
`65b558180533039a891ee0cd1ccab9988aa79adc`. Its 13 upstream paths do not
overlap this PR's 23 paths or alter packaging/Codex inputs. The consumer
report records producer `7b8e94c08657b5ddc265946459762340f125726c`,
after the existing canary staged a generated-lock-only commit (one file,
three insertions). These identities are kept separate; raw
generated-lock bytes were not retained.
- No local Docker or Rust build, paid provider turn, merge, or
deployment. Later PRs must prove live onboarding and production cloud
packaging before changing defaults.

## Risks

- npm must install optional host dependencies. Missing dependencies
still return errors. Paperclip tarballs do not pre-bundle Codex
executables.
- The repository requires CI-owned lockfile updates. PR CI resolves the
changed manifest and stages its own producer lockfile. A raw source
Docker build with `--frozen-lockfile` must wait for the existing master
lockfile bot to merge its refresh, or use a disposable resolved
checkout. No Docker build or deployment is qualified by this PR.
- Ordinary Codex startup accepts version differences. Actual protocol or
login failures remain errors. Explicit ACPX artifact checks retain their
release pins.
- The published server delegates platform installation outside its
bundled JavaScript graph. Focused negative controls reject unsafe
package metadata and paths. The hosted consumer test passed on this
candidate’s CI integration.
- Existing agents retain their stored runner choices. There is no data
migration or automatic upgrade. Release pipeline and platform
qualification work remain separate prerequisites for later defaults.

## Model Used

OpenAI Codex, GPT-6, with reasoning, tool use, code execution, and
parallel agents. The exact serving snapshot and context window are not
exposed in this session.

## 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>
2026-10-09 08:19:51 -05:00
DottaandPaperclip 18e8c121d9 fix(runner): include Grok support in public installs with sandbox prerequisites (#14024)
## Thinking Path

> - Paperclip manages agents through a shared native runner.
> - Built-in harness support should ship with Paperclip's public
distribution.
> - Grok already speaks ACP; it does not require a new public bridge
package.
> - Sandbox provisioning owns the native executable and its pinned
version.
> - The runner must verify that prerequisite without downloading it
during npm installation.
> - This change separates built-in launcher identity from external
runtime identity.
> - Clean npm installation and live staging checks verify the
distribution boundary.

## Linked Issues or Issue Description

Refs #13882, #13973, #13977, #13979.

This follow-up now targets master after #13882 was squash-merged. It
replaces the private `@paperclipai/grok-acp` workspace package with
runner-owned assets. Current master is included so the branch also
contains the merged scheduler, complete-event capture, and durable
cleanup fixes.

## What Changed

- Ship Grok launcher and qualification metadata inside the runner's
compiled output and the public server's vendored runner tree.
- Remove the separate Grok npm package and all package-manager install
hooks for this runtime.
- Require the checksum-verified Grok Build 1.0.13 binary at
`/opt/paperclip/providers/grok/1.0.13/grok` in the selected execution
environment. Provision it explicitly in the Daytona image and CI setup.
- Keep native binaries outside the provider pack. Bind the built-in
launcher into the pack manifest.
- Preserve executable leases, descriptor-backed startup, credential
fences, permissions, and exact ACP model admission.
- Use `builtin:grok-acp` and `native:grok` as profile identities.
Historical package-profile sessions fail closed on resume rather than
being silently reinterpreted.
- Resolve built-in assets from the authenticated sidecar location,
including public server npm layouts. Keep the controller path out of
provider environments.
- Add clean npm tarball installation verification to the existing
trusted canary CI job and the admitted manual EC2 verification path. It
stages a unified release version and runs npm lifecycle scripts, then
verifies missing-prerequisite rejection and admission after separate
provisioning without credentials or inference.
- Include the controller-owned provider pack in stamped Cloud images.
Unstamped local images omit the pack and remain usable; remote ACPX
requires full source provenance.
- Correct CLI approval-page metadata for an already authenticated Cloud
board user; approval authorization remains unchanged.
- Honor explicit native-runner enablement in the Cloud agent picker and
direct setup page, keeping the flag disabled by default.
- Allow selecting the execution environment before connecting
credentials. Include Grok in the existing authenticated hello-probe
flow, targeting its pinned native prerequisite for runner setup.
- Recover an existing subscription sign-in conflict through an explicit
cancel-and-retry action, serialized after cancellation succeeds.
- Preserve the selected ACPX harness before normalizing config fields,
so new Grok agents use the Grok default model.
- Keep the credential-free Cloud provider pack root-owned and readable
after runtime UID remapping; verify manifest and referenced asset access
under an unrelated unprivileged UID during image builds.
- Archive prior failover backups alongside explicitly replaced harness
state, preserving evidence while preventing stale backups from blocking
a fresh replacement.
- Update Daytona image content inputs and contract tests for the
built-in assets and explicit provisioner.
- Document and regression-test the shared `approve-all` default for Grok
setup, saved configuration, and native execution. Explicitly saved
restrictions remain unchanged.

## Verification

Current merge-repair head `df09eb3e1a619430ad8419a0ee9aedd486689b05`
incorporates master `f1a394bd30cb56fb9e479f98b9f50176fe921858` after the
base PR was squash-merged. All 12 conflicts came from incoming files
identical to the tested pre-squash base. The final tree exactly matches
a three-way merge using that original base, preserving built-in Grok
distribution and removal of the obsolete private package. All 252
focused runner/UI tests, six npm-isolation tests, and token gates pass.
Fresh exact-head Greptile review is 5/5 with no outstanding findings;
security scans and EC2 native compilation pass. All current-head CI is
green: 56 successful checks/statuses and four intentional skips ([run
36468768035](https://github.com/paperclipai/paperclip/actions/runs/36468768035)).
The repository owner explicitly authorized bypassing code-owner approval
after all checks passed; no CI checks or repository protection settings
are bypassed or changed. The only remaining PR was removed from the
completed stack metadata to permit native auto-merge.

Earlier integration head `78cb306ecc41b5c96577c26c1d89153b0ef865a1`
includes master `3447609d2247e75e55d91493dda91a608364f672` (2026-09-28).
Two master advances during verification overlapped the eval catalog; the
final merge preserves Grok qualification, completion updates, and
bounded API-response reading in all 348 cells. All 77 focused
catalog/eval/workflow tests pass. Both native stack layers (#14397) are
mergeable, and both exact-head Greptile reviews are 5/5 with successful
security scans and no unresolved review threads. All current-head CI is
green: 56 successful checks/statuses and four intentional skips ([CI
attempts](https://github.com/paperclipai/paperclip/actions/runs/36447124691)).
The initial attempt lost two EC2 runners to shutdown signals and stalled
a third shard during dependency preparation; all three passed the
same-commit failed-job-only retry. Trunk code-owner requirements remain
enforced. The review summary’s non-blocking saved-asset offset
classification note concerns code already merged in #14301; those
runtime files are identical to master and outside this stack’s diff.
Historical live evidence below retains its original source revisions.
[Final public npm
verification](https://github.com/paperclipai/paperclip/actions/runs/36445542764)
passed on `76ea70cd4d13786a042af9df82f0fd7a8c85ae30`: 17 public
packages, an executed offline lifecycle sentinel, unchanged consumer
lock, built-in launcher, missing-prerequisite rejection, and verified
separately provisioned binary/command lease. Provisioning and cleanup
require no host privilege elevation; only the positive probe mounts the
temporary native binary read-only. The verifier is unchanged by the
final master merge. All six isolation tests and an offline npm smoke
test pass. The prior head had 56 green CI checks and a 5/5 review after
two unchanged tests timed out and passed a failed-job-only retry ([CI
attempts](https://github.com/paperclipai/paperclip/actions/runs/36444597313)).
All 56 recovery-display/lineage tests pass; re-review cleared the
already-covered missed-retry concern. Earlier EC2 failures remain
retained: [npm lockfile
rejection](https://github.com/paperclipai/paperclip/actions/runs/36436311203),
[missing compiler in the slim
image](https://github.com/paperclipai/paperclip/actions/runs/36440210984),
and the aggregate 15-minute test timeouts in those broad runs. Both
broad attempts passed typecheck, token gates, Product E2E type/unit
checks and build. The focused EC2 lane preserves the existing
trusted-actor and immutable-source gates.


Earlier documentation/test checkpoint
`ff244c4fd78a7ede5a3e00efe09f475f133ef33e` leaves runtime behavior
unchanged. 154 focused tests pass across configuration building, native
provider resolution, permission policy, credentials, UI configuration,
and new-agent setup (including both Grok auth modes); token gates pass.
All fresh CI is green for this head: 56 successful checks/statuses and
two intentional skips ([run
36367065119](https://github.com/paperclipai/paperclip/actions/runs/36367065119)).
Greptile is 5/5 with no new findings. Grok already inherits the shared
`approve-all` default, so unattended setup requires no manual permission
change.

Runtime head `bb5a9307991f1ac567b781970ef11b39d518e19b` fixes a final
staging continuation failure before provider startup: explicit
replacement archived the old harness but left its failover backups
active, which caused `runner_harness_state_mismatch`. The regression
fails before the fix and passes after it; all eight adjacent
recovery-safety cases also pass. Old backups remain inspectable inside
the continuity archive. All fresh CI is green at this head ([run
36360839248](https://github.com/paperclipai/paperclip/actions/runs/36360839248)),
with a 5/5 review. One unrelated Cursor test timed out in the initial
server shard; the same-commit failed-job rerun passed, and both attempts
are retained. Staging deployment is confirmed healthy on this revision.
The controller image is
`ghcr.io/paperclipai/paperclip@sha256:6ad91c487910ccd2596ff7aed0a3a3ea5233d12b51b83cd6e1402237749b9673`.
The final browser-created staging task passed on this exact revision
with API authentication: context read → structured human question →
controller restart → answer submission → same native provider session
resumed → document saved → task Done. The two turns took approximately
119s and 77s. The actual write receipt was applied, and the saved
document has exactly one revision containing the selected answer and
requested marker. Usage and cost were not reported. [Controller image
build](https://github.com/paperclipai/paperclip/actions/runs/36360889243).

- Previous integration head `a44f7dbb6b6f77cd9ed893756ca453307f281e5f`:
all CI green (53 successful checks/statuses, two intentional skips),
including repository typecheck/build/tests, native Runner tests, browser
shards, and canary installation checks. [CI run
36358672529](https://github.com/paperclipai/paperclip/actions/runs/36358672529).
Greptile is 5/5 with no unresolved findings.
- Focused checks cover Grok credentials, executable admission, launcher
assets, provider-pack paths/permissions, workflow contracts, setup
defaults, CLI authorization, and subscription conflict recovery. All 39
protocol definitions validate. Final integration checks pass 124
catalog/evidence/cache tests and nine project-form tests; token gates
pass. Some local dependency checks could not load the stale installed
dependency tree; the corresponding fresh EC2 checks pass.
- Clean public npm installation passed on EC2 at
`8b172ebcf8e02e30662d830c00f3961e3bd459ec` ([run
36164964900](https://github.com/paperclipai/paperclip/actions/runs/36164964900)):
17 unified-version packages, lifecycle scripts enabled, built-in
launcher present, no separate Grok package or npm-downloaded binary,
missing prerequisite rejected, separately provisioned native executable
and command lease verified. No credentials or inference were used.
Subsequent changes preserve this npm asset layout.
- The immutable Daytona prerequisite image is
`ghcr.io/paperclipai/paperclip-daytona-runner@sha256:98957d5be0ac774d086b6402b5849e8e6356fec70fb8c09fca6eb4ed6de918e0`,
built from `5a2db471f3ddabe77f9f80e76ed27f996cb97fba`. The previous
Cloud controller image was
`ghcr.io/paperclipai/paperclip@sha256:fd914e1ab1e45f741e8e078ff452d16f082d7ac05f9b4b3506d3a3c64150d204`,
built from `a44f7dbb6b6f77cd9ed893756ca453307f281e5f`; it is superseded
by the latest image above. Its EC2 build verified provider-pack access
under an unrelated unprivileged UID.
- Browser staging at `40f898bc4cba73c1dff4e6344a3983ba0fb247ef` passed
full Grok onboarding with the correct `grok-4.7` model, saved credential
delivery, and pinned Daytona execution. A browser-created task read
context and asked the structured human question. After a controller
restart, answering the persisted question resumed the same native
provider session, saved the requested document, and completed the task.
Actual tool outcomes and durable state agree: one question and one
document revision. The two successful turns took 42.7s and 63.1s; usage
and cost were not reported.
- Restricted policy returned the expected `approval_required` outcome.
Functional staging tests explicitly selected `approve-all`; controller
authorization and governed approvals remain enforced. Temporary board
CLI access was revoked and verified rejected (HTTP 401), and the
disposable onboarding agent was paused.

Failures remain retained: the pre-fix continuation failure (its task
remains blocked; the passing final task is fresh), the original Cloud
provider-pack permission failure, the expected restricted-policy denial,
the superseded npm staging failure, and an earlier monolithic CI
infrastructure timeout. Browser CI exposed a project alias/form race;
the final stack uses master's stronger draft-preservation fix and all
browser shards pass. Historical full subscription/API protocol and
Product rosters retain their original source revisions and do not
qualify this packaging revision. No local Docker or Rust build was used.

## Risks

The branch includes master’s draft-preservation fix for project URL
aliases. It keeps the same project’s edit form mounted and clears prior
data when the project or company changes.

Custom sandboxes and local execution hosts must provision the pinned
binary before Grok starts. Missing, changed, unsupported-platform, and
symlinked executables fail admission. The new builtin profile cannot
resume sessions created with the former private-package profile.
Existing Claude/Codex npm bridge profiles retain their package pins.
Grok restricted modes preserve the selected policy but cannot
automatically admit Paperclip calls: ACP permission metadata does not
independently bind tool authority, so those calls stop with
`approval_required`. New Grok configurations default to `approve-all`,
including API configurations that omit the mode. Existing explicitly
restricted configurations remain restricted; controller authorization
and governed approvals remain enforced.

## Model Used

OpenAI GPT-6 through Codex, with tool use and code execution. The exact
serving model identifier and context-window size are not exposed in this
session.

## 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>
2026-09-28 14:54:44 -05:00