mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 20:34:57 +02:00
fix(runner-e2e): align Daytona image and provider pack provenance (#13814)
## Thinking Path > - Paperclip is an open source app people use to manage AI agents for work. > - The Daytona runner image provides the native runner and its provider package. > - The controller also sends a provider package to native Daytona cells when the image package does not match. > - A stale image and a package from another source revision caused a 1.8 GB upload before Claude could run. > - This pull request refreshes the reviewed lock checksum and documents how to reuse the exact package from an immutable image. > - The benefit is a reproducible setup path and clear evidence when image and package provenance do not match. ## Linked Issues or Issue Description **What happened?** A Daytona native Claude run used image source revision `45c99a0d06cbd5b04982b06b79de321149930ac5` with a controller provider package from revision `294853dc...`. Runtime verification rejected the image package and staged a large package upload before model execution. A hosted campaign also failed during image setup because the Dockerfile expected lock checksum `d7d96cf0...` while the resolved lockfile checksum was `4b796c312833ebf2be4c38228babc0292c76774fb40d43bd18b54bd6b205753d`. Related public work reviewed: [#12795](https://github.com/paperclipai/paperclip/pull/12795), [#12862](https://github.com/paperclipai/paperclip/pull/12862), and [#12887](https://github.com/paperclipai/paperclip/pull/12887). **Expected behavior** The Daytona image and controller provider package must come from the same verified build. A package extracted from the immutable image must pass the existing manifest, source revision, lockfile, binary, bridge, and artifact checks before a native Claude run starts. **Steps to reproduce** 1. Set `PAPERCLIP_E2E_DAYTONA_IMAGE` to the old immutable image digest. 2. Set `PAPERCLIP_RUNNER_REMOTE_PROVIDER_PACK_PATH` to a package built from a different source revision. 3. Run a native Claude Daytona cell. 4. Observe provider package verification failure followed by the large staging upload. 5. Build the image with the stale Dockerfile lock checksum and observe the checksum failure. **Paperclip version or commit** `3b8df3dcd6f99e99277faa45f3351989edb5c239`. **Deployment mode** Daytona native runner E2E. **Install method** Built from source. **Agent adapter(s) involved** Claude Code through the native ACPX runner. **Database mode** Not database-related. **Access context** Not applicable to the setup failure. ## What Changed - Refreshed `PAPERCLIP_RUNNER_LOCK_SHA256` in `docker/daytona-runner/Dockerfile` to the resolved lockfile checksum. - Added a local guide for extracting the provider package from an immutable verified image with Docker. - Documented the required provenance checks and the expected manifest-matched runtime log. - Documented that cold package upload coverage must remain separate from recovery coverage. - Pinned the preview-service test guest to the test runner’s Node executable and logged guest startup and bound ports for readiness diagnostics. ## Verification - 442 focused E2E tests passed. - Typecheck passed. - `pnpm build` passed. - Five provider-pack reuse tests passed. - Six Daytona image contract tests passed. - Fixed Daytona campaign [35740613581](https://github.com/paperclipai/paperclip/actions/runs/35740613581) passed. - The overall hiring campaign [35739993219](https://github.com/paperclipai/paperclip/actions/runs/35739993219) failed because of an unrelated Mini metadata failure; its Claude cell passed. - Full local `pnpm test:run` was attempted but did not complete. The isolated Postgres install was repaired and its 15-test probe passed. - After the fixture change, all seven preview reservation tests passed locally and CI server shard 6/12 passed on `20234f75f`. This removes login-shell Node resolution variance; the exact cause of the earlier CI-only timeout is not established. - CI run [35766034635](https://github.com/paperclipai/paperclip/actions/runs/35766034635) passed on `20234f75f`. All server, runner, browser, build, and typecheck gates passed. - The signoff browser case initially failed waiting for an approver run. All five signoff tests passed locally without changes; the one allowed CI retry passed all 19 shard tests. This is recorded as an intermittent failure, not a demonstrated product fix. - Greptile reviewed `20234f75f`: 5/5, no actionable findings. - [Follow-up report](https://pages.paperclip.ing/runner-daytona-hiring-20260922/) includes timings, evidence links, and the remaining hiring configuration failure. - Review the immutable image source revision and extracted `provider-pack.json` before another paid recovery run. ## Risks - The Dockerfile checksum gate intentionally fails when the resolved lockfile changes. A future dependency change must refresh the reviewed checksum with the image change. - The local extraction guide requires Docker and a pullable immutable image. - An image built from an older source revision can still fail runtime manifest verification. The guide does not bypass that check. - The change does not alter runner prompts, approval policy, or recovery behavior. ## Model Used OpenAI Codex using the primary GPT-6 backend; the exact backend deployment ID is not exposed. Repository analysis and code execution used tool access. Assistance also came from OpenAI gpt-5.6-luna. ## 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>
This commit is contained in:
1 parent
2788f20fc0
commit
a68f3d8e35
3 files changed
+50
-3
No files matched your search
@@ -28,7 +28,7 @@ COPY cli/package.json ./cli/package.json
|
||||
# The complete resolved lock (including transitive integrity hashes) is reviewed.
|
||||
# Reject registry-time drift BEFORE installing packages or running lifecycle code.
|
||||
# Refresh this digest together with source/provider dependency changes.
|
||||
ARG PAPERCLIP_RUNNER_LOCK_SHA256=d7d96cf0d98cf0946f6195e29ba173b03711a947a1c38f312b67cda56c254c22
|
||||
ARG PAPERCLIP_RUNNER_LOCK_SHA256=4b796c312833ebf2be4c38228babc0292c76774fb40d43bd18b54bd6b205753d
|
||||
RUN pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile \
|
||||
&& printf '%s pnpm-lock.yaml\n' "${PAPERCLIP_RUNNER_LOCK_SHA256}" > /tmp/provider-lock.sha256 \
|
||||
&& sha256sum -c /tmp/provider-lock.sha256 \
|
||||
|
||||
@@ -269,9 +269,14 @@ const DECLARED_EXPOSE = {
|
||||
* loopback, matching the real managed lane. Readiness then has a real listener
|
||||
* to probe, so the lifecycle runs to `ready` exactly as in production.
|
||||
*/
|
||||
// Use the test runner's executable. A login shell can resolve bare `node` to
|
||||
// another installation. Keep startup facts in the service log so a readiness
|
||||
// failure identifies which executable started and which ports it bound.
|
||||
const guestNode = `'${process.execPath.replace(/'/g, "'\\''")}'`;
|
||||
const GUEST_COMMAND =
|
||||
"node -e \"const http=require('node:http');const p=Number(process.env.PORT);"
|
||||
+ "for(const q of [p,p+10000])http.createServer((_,r)=>{r.statusCode=200;r.end('ok')}).listen(q,'127.0.0.1');"
|
||||
`${guestNode} -e "const http=require('node:http');const p=Number(process.env.PORT);`
|
||||
+ "console.log('guest-start',process.execPath,p);"
|
||||
+ "for(const q of [p,p+10000])http.createServer((_,r)=>{r.statusCode=200;r.end('ok')}).listen(q,'127.0.0.1',()=>console.log('guest-listening',q));"
|
||||
+ "setInterval(()=>{},1000)\"";
|
||||
|
||||
(embeddedPostgresSupport.supported ? describe : describe.skip)(
|
||||
|
||||
@@ -325,6 +325,48 @@ pullable, includes the provider pack, and advertises `dial_ws_loopback`,
|
||||
the image job deliberately fails its anonymous-pull check otherwise. Existing
|
||||
content tags are never rebuilt or overwritten by the workflow.
|
||||
|
||||
### Match the local controller package to the Daytona image
|
||||
|
||||
Native ACPX (including Claude) and OpenCode Daytona cells also require
|
||||
`PAPERCLIP_RUNNER_REMOTE_PROVIDER_PACK_PATH` on the controller. The package and
|
||||
the image must come from the same verified build. Equal provider version numbers
|
||||
are insufficient: verification compares the complete manifest, source revision,
|
||||
Node executable, lockfile, and built bridge hashes. An independently rebuilt
|
||||
package can fail that comparison and trigger a large upload before any model
|
||||
work begins.
|
||||
|
||||
Prefer the hosted workflow: it builds the image and controller package together,
|
||||
and uses the image's recorded source revision when reusing an image. For a local
|
||||
run, use the immutable image from the campaign for the code under test and copy
|
||||
its exact package. Do not copy credentials or change manifest fields to force a
|
||||
match. Docker must be running; the temporary container below is never started.
|
||||
|
||||
```sh
|
||||
(
|
||||
set -eu
|
||||
: "${PAPERCLIP_E2E_DAYTONA_IMAGE:?Set the verified immutable image digest}"
|
||||
case "$PAPERCLIP_E2E_DAYTONA_IMAGE" in
|
||||
*@sha256:*) ;;
|
||||
*) echo "Use an immutable image digest" >&2; exit 1 ;;
|
||||
esac
|
||||
docker pull --platform linux/amd64 "$PAPERCLIP_E2E_DAYTONA_IMAGE"
|
||||
pack_dir="$(mktemp -d "${TMPDIR:-/tmp}/paperclip-e2e-provider-pack.XXXXXX")"
|
||||
container_id="$(docker create --platform linux/amd64 --network none \
|
||||
--entrypoint /bin/true "$PAPERCLIP_E2E_DAYTONA_IMAGE")"
|
||||
trap 'docker rm "$container_id" >/dev/null' EXIT
|
||||
docker cp "$container_id:/opt/paperclip-runner/provider-pack/." "$pack_dir/"
|
||||
test -f "$pack_dir/provider-pack.json"
|
||||
printf 'Set PAPERCLIP_RUNNER_REMOTE_PROVIDER_PACK_PATH to: %s\n' "$pack_dir"
|
||||
)
|
||||
```
|
||||
|
||||
Export the printed path in the shell that launches the eval. Runtime verification
|
||||
still checks all package artifacts. The run log must show
|
||||
`using manifest-matched provider pack from the sandbox image`; after reuse it can
|
||||
instead show `reusing manifest-matched provider pack from the workspace`. A setup failure before provider
|
||||
execution does not measure Claude recovery. Keep cold-upload coverage separate
|
||||
from the recovery test, and retain mismatched or failed attempts as evidence.
|
||||
|
||||
## Evidence and cleanup
|
||||
|
||||
Packaged, access-controlled evidence is written beneath
|
||||
|
||||
Reference in new issue
Block a user