Files
PaperClipAI/tests/runner-e2e/codex-ci-sandbox.ts
T
4577d10029 fix: prepare everyday artifact and Codex sandbox prerequisites in CI (#13516)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The runner E2E workflow executes paid everyday workflow stories on
disposable CI hosts
> - The everyday artifact oracle requires a pinned Python image and
fails closed when it is absent
> - Fresh CI hosts did not prepare this image before the paid cell, so
project stories failed during preflight
> - This pull request prepares and verifies the pinned image before the
affected everyday cells
> - The benefit is reliable artifact isolation checks on fresh trusted
CI hosts

## Linked Issues or Issue Description

**What happened?**

Fresh trusted CI runners did not have the pinned Python artifact oracle
image.

**Expected behavior:**

The workflow prepares and verifies the pinned image before an everyday
project story starts.

**Steps to reproduce:**

Run an everyday project story on a fresh CI host without the image
cached. The `everyday-artifact.py --preflight` check fails before task
creation.

**Paperclip version or commit:**

`master` at `bd51f157e`.

**Deployment mode:**

Other: GitHub Actions trusted paid workflow.

## What Changed

- Add a matrix-gated CI step for everyday project and recovery cells.
- Check Docker, pull the fixed digest with bounded timeouts, and verify
the exact repo digest.
- Apply the existing Codex sandbox preparation to both native Codex
profiles, including the mini profile.
- Add workflow security assertions for ordering, condition, digest,
timeouts, and secret isolation.
- Document that CI prepares the pinned oracle image.
- Check provisioning eligibility against every catalog cell, and scope
the Daytona registry inspection assertion to the Daytona image job.

## Verification

- `pnpm test:e2e:runner:unit` — 24 files and 222 tests passed.
- `pnpm test:e2e:runner:typecheck` — passed.
- `pnpm exec vitest run --config tests/runner-e2e/vitest.config.ts
workflow-security.test.ts` — 10 tests passed.
- Python artifact oracle calibration — 12/12 passed.
- `git diff --check` — passed.

## Risks

Low risk. The image step runs only for everyday cells that execute the
artifact preflight. The Codex setup now covers both native Codex
profiles. It uses a fixed public image digest and has no provider
credentials.

## Model Used

OpenAI gpt-5.6-luna (implementation subagent) and gpt-6-astra (review
fixes and orchestration), using code execution and repository tools.
Context window size is not exposed by 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>
Co-authored-by: Paperclip <paperclip@paperclip.ing>
2026-09-16 07:49:48 -05:00

74 lines
2.4 KiB
TypeScript

import { execFileSync } from "node:child_process";
import { mkdir, realpath } from "node:fs/promises";
import { createRequire } from "node:module";
import path from "node:path";
import type { MatrixExecution } from "./types.js";
export function requiresCodexCiSandbox(execution: MatrixExecution): boolean {
return execution.environment.id === "local"
&& execution.profile.generation === "native"
&& execution.profile.provider === "codex";
}
/** Probe the existing Linux sandbox before spending provider credentials.
* Host policy is provisioned by the trusted workflow, never by target test code.
*/
export async function prepareCodexCiSandbox(
repositoryRoot: string,
temporaryRoot: string,
) {
if (process.platform !== "linux" || process.env.GITHUB_ACTIONS !== "true")
return;
const runnerRequire = createRequire(
path.join(repositoryRoot, "packages/paperclip-runner/package.json"),
);
const acpRequire = createRequire(
runnerRequire.resolve("@agentclientprotocol/codex-acp/package.json"),
);
const codexRequire = createRequire(
acpRequire.resolve("@openai/codex/package.json"),
);
const arch =
process.arch === "x64" ? "x64" : process.arch === "arm64" ? "arm64" : null;
if (!arch) throw new Error("Unsupported Codex CI architecture");
const platformPackage = codexRequire.resolve(
`@openai/codex-linux-${arch}/package.json`,
);
const triple =
arch === "x64" ? "x86_64-unknown-linux-musl" : "aarch64-unknown-linux-musl";
const binary = await realpath(
path.join(path.dirname(platformPackage), "vendor", triple, "bin", "codex"),
);
const probeHome = path.join(temporaryRoot, "codex-sandbox-probe");
await mkdir(probeHome, { mode: 0o700 });
try {
execFileSync(
binary,
[
"sandbox",
"--permission-profile",
"paperclip-e2e-probe",
"-c",
'permissions.paperclip-e2e-probe.filesystem={":root"="read"}',
"-c",
"permissions.paperclip-e2e-probe.network.enabled=false",
"-C",
temporaryRoot,
"--",
"/bin/true",
],
{
cwd: temporaryRoot,
env: { PATH: process.env.PATH, CODEX_HOME: probeHome },
timeout: 15_000,
stdio: "pipe",
},
);
} catch (error) {
throw new Error(
"Codex sandbox preflight failed. The trusted CI runner must provision its user-namespace policy before paid tests run.",
{ cause: error },
);
}
}