mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-12 08:19:54 +02:00
## 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>
74 lines
2.4 KiB
TypeScript
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 },
|
|
);
|
|
}
|
|
}
|