Files
PaperClipAI/packages/paperclip-runner/scripts/runner-protocol-eval-workflow-security.test.mjs
T
DottaandPaperclip 85e4d12856 fix(evals): report controlled protocol cases from safe records (#19070)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Runner protocol qualification records real provider behavior in
TestOps.
> - Controlled attempts keep raw records and accounting in private
storage.
> - A live case passed, but a later public-render command rejected its
controlled context.
> - The missing public report caused the final result to be stored as
broken.
> - This pull request creates the closed TestOps result from existing
sanitized records inside the private report job.
> - The benefit is correct result reporting while the privacy guard
stays effective.

## Linked Issues or Issue Description

Refs #18985 and #19011. The related-PR search found no separate repair
for this failure.

**What happened?**

[The live
canary](https://github.com/paperclipai/paperclip/actions/runs/41916513378)
passed all four provider checks. Its controller verified cleanup and
closed its request accounting. The private canonical report recorded one
passed case. The next command used `--public-viewer` while the
controlled manifest remained set. The eval program rejected it with
`controlled campaign evidence must remain private`. The closed exporter
then had no public payload and correctly stored a broken result.

**Expected behavior**

Report the original case grade from sanitized structured records. Keep
raw controlled records private. Keep the original checks, source
bindings, and missing-evidence rules.

**Steps to reproduce**

Run one controlled protocol case with TestOps reporting enabled. Let the
private canonical report finish. The following public-render step fails
before the closed result can read the grade.

**Paperclip version or commit**

Observed at `4740d251b7032e714d33039ba66a56948238c38f`. This repair is
based on `c6cb7e2a2d42743c0c3c4ead2cbad4a299f9a2e7`.

**Deployment mode**

GitHub-hosted Linux, controlled ACPX Claude protocol qualification,
first workflow attempt, zero automatic retries.

## What Changed

- Add a narrow `export-runs` path to the existing TestOps helper. Bind
the sanitized score and artifact to the exact attempt, case, model,
provider, driver, and public-projection schema. Reuse the existing
strict grade and check validation.
- Keep sanitization inside the private report job. Use structured
records for controlled TestOps reporting. Keep the public viewer for
legacy publication.
- Add record identity, privacy, grade, command, and workflow routing
controls. Wire the existing helper and workflow-security files into the
required Node registry. Document the reporting path.

## Verification

- 43 focused campaign/sanitizer, TestOps and workflow security tests
passed. The command regression writes a raw attempt, calls the
production sanitizer, and runs `export-runs` on its output. It also
repeats that chain after a failed-check mutation. Both new-control files
now run through required `test:typescript:prep`, not only the optional
publisher script.
- The actual required prep ran locally with provider credentials
excluded. The eval-kernel and Rust builds passed. Its Node registry
executed the new producer-to-export and security controls by name: 403
passed, 2 failed, 11 existing skips. The two original package-contract
failures cannot resolve this isolated clone's Codex ACP and Claude ACP
package links. This is not a green local full-prep claim. The first
attempt also stopped at a missing Node-types link, before an ignored
clone-local link to the locked cached version restored that
prerequisite.
- 72 release workflow tests passed with the existing locked pnpm 9.15.4
wrapper and local loopback permission. The first sandbox run failed
because loopback fixtures could not listen; the default local tool path
also failed two install fixtures. No test assertions were changed to
resolve these failures.
- The actual repaired workflow shell ran locally against the retained
canary campaign and sanitized records. It emitted one closed passed
result with all four original checks. No provider request or upload
occurred.
- Missing or failed checks still reject a pass. Private-only markers
stay absent from the closed output. Wrong model, provider, driver,
attempt, case, and schemas reject.
- The unmodified, pinned eval program still rejects a controlled
public-viewer command.
- Actionlint and `git diff --check` passed. [Full final-head
CI](https://github.com/paperclipai/paperclip/actions/runs/41927878886)
passed on its first attempt at `0d0c090b1b`. Required verify/e2e gates,
all four Runner lanes, typecheck, build, backend and browser shards
passed. Package suite: 4951 passed, 10 existing skips across 297 passed
files and one existing file skip.
- The required static log contains passing lines for both the production
sanitizer-to-export command regression and the closed-projection
security control. The prior optional-script registration did not run
those tests in required CI; the one script-entry fix now enforces them.
Provider/runtime and workflow bytes are unchanged by the
verification-only follow-ups.
- Greptile: fresh 5/5 on `0d0c090b1b`, with no actionable new issues.
The separate Paperclip Review requested a producer-to-consumer
regression. That regression and its required-CI wiring are complete. The
addressed thread is resolved; no open review threads remain.
- No fresh deployed reporting pass is claimed. The failed workflow and
its stored broken result remain unchanged.

## Risks

The exporter reads structured records inside the private job. It copies
only the closed result fields and requires the existing sanitized marker
and identity checks. The final reporting job still receives only those
files and verifies the saved result bindings. The Python privacy guard,
provider path, case assertions, budgets, deadlines, and retries are
unchanged.

## Model Used

OpenAI Codex, exact model `gpt-6.1-sol`, max reasoning effort. The
inherited runtime reports a 475,000-token context window. Capabilities
used: reasoning, code changes, and repository, shell, and GitHub tools.

## 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-11-04 22:03:59 -06:00

521 lines
29 KiB
JavaScript

import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { spawnSync } from "node:child_process";
import { tmpdir } from "node:os";
import { resolve } from "node:path";
import test from "node:test";
const repositoryRoot = resolve(import.meta.dirname, "../../..");
const workflowPath = resolve(
repositoryRoot,
".github/workflows/runner-protocol-live-evals.yml",
);
const trustedPrWorkflowPath = resolve(
repositoryRoot,
".github/workflows/pr-trusted.yml",
);
test("Grok subscription credentials require explicit catalog selection and observed auth evidence", async () => {
const workflow = await readFile(workflowPath, "utf8");
const paid = workflow.slice(workflow.indexOf(" steps: &direct_eval_steps"), workflow.indexOf(" eval_shard_1:"));
assert.match(workflow, /grok_authentication:\n[\s\S]*?type: choice\n[\s\S]*?default: api_key/u);
assert.match(workflow, /--grok-authentication "\$GROK_AUTHENTICATION"/u);
assert.ok(paid.includes("PAPERCLIP_ACPX_GROK_AUTH_JSON_SECRET: ${{ matrix.credentialName == 'PAPERCLIP_ACPX_GROK_AUTH_JSON_SECRET' && secrets.GROK_AUTH_JSON || '' }}"));
assert.ok(paid.includes("XAI_API_KEY: ${{ matrix.credentialName == 'XAI_API_KEY' && secrets.XAI_API_KEY || '' }}"));
assert.match(paid, /--summary-path cell-output\/roster-summary\.json/u);
assert.match(paid, /JSON\.parse\(readFileSync\("cell-output\/roster-summary\.json", "utf8"\)\)\.authenticationMode/u);
assert.match(paid, /authenticationMode !== expected/u);
for (const job of [workflow.slice(0, workflow.indexOf(" eval_shard_0:")), workflow.slice(workflow.indexOf(" report:"))]) {
assert.doesNotMatch(job, /secrets\.GROK_AUTH_JSON/u);
}
});
test("direct live eval workflow keeps paid execution behind stable actor authorization", async () => {
const workflow = await readFile(workflowPath, "utf8");
assert.match(workflow, /^\s{2}authorize:/mu);
assert.match(workflow, /RUNNER_E2E_ALLOWED_ACTOR_IDS/u);
assert.match(workflow, /github\.actor_id/u);
assert.match(workflow, /github\.triggering_actor/u);
assert.match(workflow, /refs\/heads\/\$DEFAULT_BRANCH/u);
assert.match(workflow, /Reauthorize paid execution before provider access/u);
const copilotCredential = "COPILOT_GITHUB_TOKEN: ${{ matrix.credentialName == 'COPILOT_GITHUB_TOKEN' && secrets.COPILOT_GITHUB_TOKEN || '' }}";
assert.equal(workflow.split(copilotCredential).length - 1, 1);
const paid = workflow.slice(workflow.indexOf(" eval_shard_0:"), workflow.indexOf(" eval_shard_1:"));
assert.ok(paid.includes("'runner-e2e-private-paid' || 'runner-e2e-paid'"));
assert.ok(paid.includes(copilotCredential));
const reauthorizationIndex = paid.indexOf("Reauthorize paid execution before provider access");
assert.ok(reauthorizationIndex >= 0);
assert.ok(reauthorizationIndex < paid.indexOf(copilotCredential));
assert.match(
workflow,
/Reauthorize paid execution before provider access[\s\S]*actions\/checkout@[0-9a-f]{40}[\s\S]*Run one immutable direct protocol cell/u,
);
assert.doesNotMatch(
workflow,
/^\s{2}(?:pull_request|pull_request_target|push|workflow_call|workflow_run):/mu,
);
const actions = [
...workflow.matchAll(/^\s*(?:-\s*)?uses:\s*([^\s#]+)/gmu),
].map((match) => match[1]);
assert.ok(actions.length > 0);
for (const action of actions) assert.match(action, /^[^@]+@[0-9a-f]{40}$/u);
});
test("pull request CI builds the canonical Evalbook viewer", async () => {
const workflow = await readFile(trustedPrWorkflowPath, "utf8");
const buildJob = workflow.slice(
workflow.indexOf(" build:"),
workflow.indexOf(" verify_serialized_server:"),
);
assert.match(
buildJob,
/name: Build Runner Evalbook viewer[\s\S]*pnpm --filter @paperclipai\/paperclip-runner build:issue-thread/u,
);
});
test("resolves both repositories immutably and bounds total matrix concurrency", async () => {
const workflow = await readFile(workflowPath, "utf8");
const targetLock = workflow.slice(
workflow.indexOf(" target_lock:"),
workflow.indexOf(" catalog:"),
);
assert.match(targetLock, /ref: \$\{\{ needs\.authorize\.outputs\.target_sha \}\}/u);
assert.match(targetLock, /pnpm install --ignore-scripts --no-frozen-lockfile --lockfile-only/u);
assert.match(targetLock, /Upload resolved target lockfile/u);
const build = workflow.slice(
workflow.indexOf(" build_runner:"),
workflow.indexOf(" eval_shard_0:"),
);
assert.match(build, /Download resolved target lockfile/u);
assert.match(build, /Restore resolved target lockfile/u);
const authorize = workflow.slice(
workflow.indexOf(" authorize:"),
workflow.indexOf(" catalog:"),
);
assert.match(authorize, /repos\/\$REPOSITORY\/branches\/\$encoded_branch/u);
assert.match(authorize, /\^\[0-9a-f\]\{40\}\$/u);
assert.match(
authorize,
/repos\/paperclipai\/paperclip-evals\/commits\/\$EVALS_SHA/u,
);
assert.match(authorize, /COMMITPERCLIP_KEY/u);
assert.match(authorize, /GH_REPO: paperclipai\/paperclip-evals/u);
assert.match(
authorize,
/GH_TOKEN: \$\{\{ steps\.evals_token\.outputs\.value \}\}/u,
);
assert.match(authorize, /test "\$resolved" = "\$EVALS_SHA"/u);
const catalog = workflow.slice(
workflow.indexOf(" catalog:"),
workflow.indexOf(" build_runner:"),
);
assert.match(
catalog,
/ref: \$\{\{ needs\.authorize\.outputs\.evals_sha \}\}/u,
);
assert.match(catalog, /RUNNER_E2E_MAX_PARALLEL/u);
assert.match(catalog, /max_parallel_per_shard/u);
const privateCheckouts = [
...workflow.matchAll(
/repository: paperclipai\/paperclip-evals[\s\S]*?persist-credentials: false/gmu,
),
];
assert.equal(privateCheckouts.length, 4);
const privateTokenSteps = [
...workflow.matchAll(
/^ - name: Generate private eval-repository token\n(?<body>(?:^ {8,}.*\n?)*)/gmu,
),
];
assert.equal(privateTokenSteps.length, 5);
for (const tokenStep of privateTokenSteps) {
assert.match(
tokenStep.groups.body,
/^ {10}GH_REPO: paperclipai\/paperclip-evals$/mu,
"every private-eval token must be minted from the eval repository installation",
);
}
for (const checkout of privateCheckouts) {
assert.match(
checkout[0],
/token: \$\{\{ steps\.evals_token\.outputs\.value \}\}/u,
);
}
assert.match(workflow, /matrix_0/u);
assert.match(workflow, /matrix_1/u);
assert.match(
workflow,
/pnpm --filter @paperclipai\/paperclip-runner deploy --prod/u,
);
assert.match(
workflow,
/runner_cli=runner-protocol-build\/extracted\/portable\/dist\/cli\/eval-session\.js/u,
);
assert.doesNotMatch(workflow, /npm install --prefix/u);
});
test("publishes only the separately sanitized Evalbook through trusted OIDC code", async () => {
const workflow = await readFile(workflowPath, "utf8");
const report = workflow.slice(
workflow.indexOf(" report:"),
workflow.indexOf(" publish_history:"),
);
assert.match(
report,
/Render the access-controlled canonical Evalbook report/u,
);
assert.match(
report,
/--viewer-root runner-protocol-build\/extracted\/dist-issue-thread/u,
);
assert.match(report, /runner-protocol-eval-campaign\.mjs sanitize/u);
assert.match(
report,
/Upload legacy canonical Evalbook and raw attempts/u,
);
assert.match(report, /Upload publisher-only sanitized Evalbook/u);
assert.match(
report,
/verify-runner-evalbook-viewer\.mjs --report-root runner-protocol-merged\/public-report/u,
);
assert.match(
report,
/verify-runner-evalbook-viewer\.mjs --report-root runner-protocol-merged\/report/u,
);
assert.match(
report,
/--viewer-root runner-protocol-build\/extracted\/dist-issue-thread\s*\\\n\s*--public-viewer/u,
);
assert.equal([...report.matchAll(/--viewer-root /gu)].length, 2);
const publisher = workflow.slice(workflow.indexOf(" publish_history:"));
assert.match(publisher, /ref: \$\{\{ github\.sha \}\}/u);
assert.match(publisher, /id-token: write/u);
assert.match(publisher, /runner-protocol-eval-public-/u);
assert.match(publisher, /publish-runner-protocol-eval-history\.mjs/u);
assert.match(publisher, /runner-protocol-evals/u);
assert.match(publisher, /runner-protocol-viewer-/u);
assert.match(publisher, /PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR/u);
assert.match(publisher, /url: \$\{\{ steps\.publish\.outputs\.report_url \}\}/u);
assert.match(publisher, /Publish versioned report and refresh the root index\n\s+id: publish/u);
assert.doesNotMatch(publisher, /(?:OPENAI|ANTHROPIC|OPENROUTER)_API_KEY/u);
assert.doesNotMatch(publisher, /paperclipai\/paperclip-evals/u);
assert.doesNotMatch(publisher, /downloaded-runner-protocol-evals/u);
});
test("provisions the Codex userns profile before any provider credentials", async () => {
const workflow = await readFile(workflowPath, "utf8");
const direct = workflow.slice(
workflow.indexOf(" steps: &direct_eval_steps"),
workflow.indexOf(" eval_shard_1:"),
);
const profile = direct.indexOf("Provision Codex sandbox on the disposable trusted runner");
const credentials = direct.indexOf("Prepare short-lived AgentCore web identity");
assert.ok(profile >= 0 && credentials >= 0 && profile < credentials);
assert.match(direct, /apparmor_restrict_unprivileged_userns/u);
assert.match(direct, /apparmor_parser/u);
assert.match(direct, /userns,/u);
assert.match(direct, /runner-protocol-build\/extracted\/portable/u);
assert.match(direct, /matrix\.provider == 'codex'/u);
assert.match(direct, /matrix\.rosterId == 'protocol-live-acpx-codex-control'/u);
assert.doesNotMatch(direct, /matrix\.profileId/u);
const build = workflow.slice(
workflow.indexOf(" build_runner:"),
workflow.indexOf(" eval_shard_0:"),
);
assert.match(build, /Materialize the pinned OpenCode executable before packaging/u);
assert.match(build, /materialize-opencode-binary\.mjs/u);
});
test("report preparation stays on the trusted lock and install mode", async () => {
const workflow = await readFile(workflowPath, "utf8");
const report = workflow.slice(
workflow.indexOf(" report:"),
workflow.indexOf(" publish_history:"),
);
assert.match(report, /ref: \$\{\{ github\.sha \}\}/u);
assert.doesNotMatch(report, /Download resolved target lockfile/u);
assert.doesNotMatch(report, /Restore resolved target lockfile/u);
assert.doesNotMatch(report, /--no-frozen-lockfile|cache: pnpm/u);
assert.match(report, /pnpm install --frozen-lockfile --ignore-scripts --store-dir "\$RUNNER_TEMP\/runner-report-pnpm-store"/u);
});
test("trusted catalog, direct eval, and report orchestration stay on workflow revision", async () => {
const workflow = await readFile(workflowPath, "utf8");
for (const [job, next] of [
[" catalog:", " build_runner:"],
[" eval_shard_0:", " eval_shard_1:"],
[" report:", " publish_history:"],
]) {
const section = workflow.slice(workflow.indexOf(job), workflow.indexOf(next));
assert.match(section, /ref: \$\{\{ github\.sha \}\}/u, `${job} must use the trusted workflow revision`);
assert.doesNotMatch(section, /ref: \$\{\{ needs\.authorize\.outputs\.target_sha \}\}/u, `${job} must not execute target orchestration code`);
}
});
test("authentication failures retain cell metadata without leaking malformed summary content", async () => {
const workflow = await readFile(workflowPath, "utf8");
const script = workflow.match(/CELL_EXIT_CODE="\$status" node --input-type=module <<'NODE'\n([\s\S]*?) NODE/u)?.[1];
assert.ok(script);
const root = await mkdtemp(resolve(tmpdir(), "grok-cell-evidence-"));
try {
await mkdir(resolve(root, "cell-output"));
const summary = resolve(root, "cell-output/roster-summary.json");
for (const [content, expectedFailure, expectedMode] of [
[undefined, "grok_authentication_evidence_unreadable", undefined],
["{SENSITIVE_SENTINEL", "grok_authentication_evidence_unreadable", undefined],
["null", "grok_authentication_evidence_unreadable", undefined],
['{"authenticationMode":"SENSITIVE_SENTINEL"}', "grok_authentication_evidence_mismatch", undefined],
['{"authenticationMode":"api_key"}', "grok_authentication_evidence_mismatch", "api_key"],
['{"authenticationMode":"subscription"}', undefined, "subscription"],
]) {
if (content === undefined) await rm(summary, { force: true });
else await writeFile(summary, content);
const result = spawnSync(process.execPath, ["--input-type=module", "-e", script], {
cwd: root, encoding: "utf8",
env: { CREDENTIAL_NAME: "PAPERCLIP_ACPX_GROK_AUTH_JSON_SECRET", CELL_ID: "cell-1", CASE_ID: "context", ROSTER_FILE: "grok.json", CELL_EXIT_CODE: "7" },
});
assert.equal(result.status, expectedFailure ? 1 : 0);
const retained = await readFile(resolve(root, "cell-output/cell.json"), "utf8");
const metadata = JSON.parse(retained);
assert.equal(metadata.cellId, "cell-1");
assert.equal(metadata.caseId, "context");
assert.equal(metadata.exitCode, 7);
assert.equal(metadata.authenticationEvidenceFailure, expectedFailure);
assert.equal(metadata.authenticationMode, expectedMode);
assert.doesNotMatch(retained + result.stdout + result.stderr, /SENSITIVE_SENTINEL/u);
}
} finally {
await rm(root, { recursive: true, force: true });
}
});
function workflowShell(workflow, name) {
const step = workflow.slice(workflow.indexOf(` - name: ${name}\n`)).split(/\n - /u)[0];
assert.ok(step.startsWith(` - name: ${name}\n`));
const script = step.split(' run: |\n')[1];
assert.ok(script);
return script.split('\n').map(line => line.slice(10)).join('\n');
}
test('controlled admission rejects missing policy, automatic retries and job reruns before paid work', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const script = workflowShell(workflow, 'Validate accounting mode and retry envelope');
const root = await mkdtemp(resolve(tmpdir(), 'runner-workflow-admission-'));
try {
for (const [mode, event, attempt, retries, policy, status] of [
['controlled', 'workflow_dispatch', '1', '0', '', 1],
['controlled', 'workflow_dispatch', '2', '0', JSON.stringify({schema:'paperclip.runner-eval.hosted-policy/v1',controllerLockSha256:'e'.repeat(64)}), 1],
['controlled', 'workflow_dispatch', '1', '1', JSON.stringify({schema:'paperclip.runner-eval.hosted-policy/v1',controllerLockSha256:'e'.repeat(64)}), 1],
['legacy', 'schedule', '1', '1', '', 1],
['controlled', 'workflow_dispatch', '1', '0', JSON.stringify({schema:'paperclip.runner-eval.hosted-policy/v1',controllerLockSha256:'e'.repeat(64)}), 0],
['legacy', 'workflow_dispatch', '1', '1', '', 0],
]) {
const result = spawnSync('bash', ['-c', script], { encoding: 'utf8', env: { PATH: process.env.PATH,
ACCOUNTING_MODE: mode, EVENT_NAME: event, GITHUB_RUN_ATTEMPT: attempt, RETRIES: retries,
CONTROLLER_POLICY_JSON: policy, TARGET_SHA: 'a'.repeat(40), GITHUB_SHA: 'a'.repeat(40), GITHUB_OUTPUT: resolve(root, 'output') } });
assert.equal(result.status, status, `${mode}/${event}/${attempt}/${retries}: ${result.stderr}`);
}
} finally { await rm(root, { recursive: true, force: true }); }
});
test('controlled workers keep the required GitHub-hosted origin even when the legacy AWS fleet is enabled', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const script = workflowShell(workflow.slice(0, workflow.indexOf('\n target_lock:')), 'Select paid test runner');
const root = await mkdtemp(resolve(tmpdir(), 'runner-controlled-origin-'));
try {
for (const [mode, aws, runner] of [['controlled', 'true', 'ubuntu-latest'], ['controlled', 'false', 'ubuntu-latest'],
['legacy', 'true', 'runs-on/fleet=paperclip-public-pr-x64/env=public-ci'], ['legacy', 'false', 'ubuntu-latest']]) {
const output = resolve(root, `${mode}-${aws}`);
const result = spawnSync('bash', ['-euc', script], { encoding: 'utf8', env: { PATH: process.env.PATH,
ACCOUNTING_MODE: mode, AWS_PAID_RUNNER_ENABLED: aws, GITHUB_OUTPUT: output } });
assert.equal(result.status, 0, result.stderr);
assert.match(await readFile(output, 'utf8'), new RegExp(`^runner=${runner}$`, 'm'));
}
} finally { await rm(root, { recursive: true, force: true }); }
});
test('requested parallelism respects both configured capacity and the selected runner cap', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const script = workflowShell(workflow, 'Build the two bounded roster-plus-case matrices');
const prefix = script.slice(0, script.indexOf('node packages/paperclip-runner/scripts/runner-protocol-eval-campaign.mjs'));
assert.ok(prefix.includes('REQUESTED_MAX_PARALLEL'));
for (const [configured, limit, requested, expected] of [['100', '57', '2', '2'], ['100', '57', '', '57'],
['100', '100', '', '100'], ['8', '57', '16', null], ['100', '57', '58', null], ['100', '57', '1', null]]) {
const result = spawnSync('bash', ['-euc', `${prefix}\nprintf '%s' "$MAX_PARALLEL"`], { encoding: 'utf8', env: {
PATH: process.env.PATH, MAX_PARALLEL: configured, MAX_PARALLEL_LIMIT: limit, REQUESTED_MAX_PARALLEL: requested } });
assert.equal(result.status === 0, expected !== null, `${configured}/${limit}/${requested}: ${result.stderr}`);
if (expected !== null) assert.equal(result.stdout, expected);
}
});
test('the provider uses an owned same-byte Node before credentials without changing shared host permissions', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const paid = workflow.slice(workflow.indexOf(' steps: &direct_eval_steps'), workflow.indexOf(' eval_shard_1:'));
const staging = paid.indexOf('Stage an owned provider Node before credentials');
assert.ok(staging > paid.indexOf('Verify and extract portable runner'));
assert.ok(staging < paid.indexOf('Obtain private archive credentials for transfer steps only'));
assert.ok(staging < paid.indexOf('Prepare short-lived AgentCore web identity'));
assert.ok(staging < paid.indexOf('Run one immutable direct protocol cell'));
const command = workflowShell(paid, 'Stage an owned provider Node before credentials');
assert.match(command, /node packages\/paperclip-runner\/scripts\/stage-runner-provider-node\.mjs/u);
assert.match(command, /"\$RUNNER_TEMP\/runner-protocol-provider-node"/u);
assert.match(command, /portable\/dist\/cli\/acpx-runtime-sidecar\.cjs/u);
assert.match(command, /cell-output\/provider-node-receipt\.json/u);
assert.doesNotMatch(command, /secrets\.|\bsudo\b|\bchmod\b|hostedtoolcache/u);
});
test('TestOps admission preserves the selected mode gates, current source and dedicated roster', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const script = workflowShell(workflow, 'Validate accounting mode and retry envelope');
const root = await mkdtemp(resolve(tmpdir(), 'runner-testops-admission-'));
try {
for (const [mode, source, roster, policy, retries, expected] of [
['legacy', 'a', 'protocol-live-testops-claude', true, '0', 0],
['controlled', 'a', 'protocol-live-testops-claude', true, '0', 0],
['controlled', 'a', 'protocol-live-testops-claude', false, '0', 1],
['controlled', 'a', 'protocol-live-testops-claude', true, '1', 1],
['controlled', 'b', 'protocol-live-testops-claude', true, '0', 1],
['legacy', 'b', 'protocol-live-testops-claude', true, '0', 1],
['legacy', 'a', 'all', true, '0', 1],
]) {
const result = spawnSync('bash', ['-c', script], { encoding: 'utf8', env: { PATH: process.env.PATH,
ACCOUNTING_MODE: mode, EVENT_NAME: 'workflow_dispatch', GITHUB_RUN_ATTEMPT: '1', RETRIES: retries,
TESTOPS_REQUESTED: 'true', REQUESTED_ROSTERS: roster,
CONTROLLER_POLICY_JSON: policy ? JSON.stringify({schema:'paperclip.runner-eval.hosted-policy/v1',controllerLockSha256:'e'.repeat(64)}) : '',
TARGET_SHA: source.repeat(40), GITHUB_SHA: 'a'.repeat(40), GITHUB_OUTPUT: resolve(root, 'output') } });
assert.equal(result.status, expected, `${mode}/${source}/${roster}: ${result.stderr}`);
}
} finally { await rm(root, { recursive: true, force: true }); }
});
test('TestOps receives only a closed projection created inside the report boundary', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const report = workflow.slice(workflow.indexOf(' report:'), workflow.indexOf(' testops_report:'));
const publisher = workflow.slice(workflow.indexOf(' testops_report:'), workflow.indexOf(' publish_history:'));
const publicationCondition = publisher.match(/^ if: (.+)$/mu)?.[1];
assert.ok(publicationCondition);
const admits = (cancelled, enabled = true, selected = true) => Function('always', 'cancelled', 'needs',
`return ${publicationCondition}`)(() => true, () => cancelled,
{ authorize: { outputs: { testops_enabled: String(enabled) } }, testops_selection: { result: selected ? 'success' : 'failure' } });
assert.equal(admits(false), true);
assert.equal(admits(true), false);
assert.equal(admits(false, false), false);
assert.equal(admits(false, true, false), false);
assert.match(report, /environment:.*runner-e2e-private-history/u);
assert.match(report, /Create the closed TestOps projection inside the report boundary[\s\S]*runner-protocol-testops\.mjs export/u);
const publicGrid = report.slice(report.indexOf(' - name: Render the same canonical grid'), report.indexOf(' - name: Create the closed TestOps projection'));
assert.match(publicGrid, /if: needs\.authorize\.outputs\.accounting_mode == 'legacy'/u);
assert.doesNotMatch(publicGrid, /testops_enabled|env -u|unset /u);
const sanitize = report.slice(report.indexOf(' - name: Project safe run records'), report.indexOf(' - name: Render the same canonical grid'));
assert.match(sanitize, /accounting_mode == 'legacy' \|\| needs\.authorize\.outputs\.testops_enabled == 'true'/u);
assert.match(sanitize, /runner-protocol-eval-campaign\.mjs sanitize/u);
const projection = workflowShell(report, 'Create the closed TestOps projection inside the report boundary');
assert.match(projection, /controlled\)[\s\S]*export-runs[\s\S]*campaign\.json runner-protocol-merged\/public-runs/u);
assert.match(projection, /legacy\)[\s\S]*export \\[\s\S]*runner-protocol-merged\/public-report/u);
assert.match(report, /Transfer only the closed TestOps results and receipt[\s\S]*path: runner-protocol-testops-projection\//u);
assert.match(publisher, /name: runner-protocol-testops-projection-/u);
assert.doesNotMatch(publisher, /runner-protocol-eval-public-|runner-protocol-merged|private-history|AWS_ACCESS_KEY_ID|BRAINTRUST_API_KEY/u);
assert.match(publisher, /steps\.projection\.outcome != 'success'/u);
});
test('controlled capacity counts the filtered selection before any provider work', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const filter = workflow.indexOf(' - name: Apply the exact selected protocol cases before fan-out');
const limit = workflow.indexOf(' - name: Enforce controlled limits against the selected catalog');
const upload = workflow.indexOf(' - name: Upload immutable campaign catalog');
assert.ok(filter > 0 && filter < limit && limit < upload);
const script = workflowShell(workflow, 'Enforce controlled limits against the selected catalog');
const cell = { provider: 'acpx', acpxAgent: 'claude', driver: 'acpx_runtime' };
const root = await mkdtemp(resolve(tmpdir(), 'runner-selected-capacity-'));
try {
for (const [cells, maximum, expected] of [[[cell], 1, 0], [Array(41).fill(cell), 1, 1],
[[cell, cell], 1, 1], [[{ ...cell, provider: 'unsupported' }], 1, 1], [[cell], 0, 1]]) {
await writeFile(resolve(root, 'runner-protocol-eval-catalog.json'), JSON.stringify({ cells }));
const result = spawnSync('bash', ['-euc', script], { cwd: root, encoding: 'utf8', env: { PATH: process.env.PATH,
CONTROLLER_POLICY_JSON: JSON.stringify({ maxCells: maximum }) } });
assert.equal(result.status === 0, expected === 0, result.stderr);
}
} finally { await rm(root, { recursive: true, force: true }); }
});
test('controlled evidence uses private storage and cloud credentials stay out of provider execution', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const paid = workflow.slice(workflow.indexOf(' steps: &direct_eval_steps'), workflow.indexOf(' eval_shard_1:'));
const model = paid.slice(paid.indexOf(' - name: Run one immutable direct protocol cell'), paid.indexOf(' - name: Retain controlled cell evidence'));
assert.ok(paid.indexOf('Verify current worker admission before provider credentials') < paid.indexOf('Run one immutable direct protocol cell'));
assert.ok(paid.indexOf('Provision Codex sandbox') < paid.indexOf('Obtain private archive credentials'));
assert.doesNotMatch(model, /AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|AWS_SESSION_TOKEN/u);
assert.match(model, /runner_cli="\$root\/runtime\/controlled-evals\/run.mjs"/u);
for (const step of workflow.split(/\n - /u).filter(step => /uses: actions\/upload-artifact/u.test(step))) {
if (/path: (?:cell-output|runner-protocol-merged)\//u.test(step)) assert.match(step, /accounting_mode == 'legacy'/u);
assert.doesNotMatch(step, /path: .*runner-protocol-control\//u);
}
for (const step of workflow.split(/\n - /u).filter(step => /uses: aws-actions\/configure-aws-credentials/u.test(step) && step.includes('id: private_history_credentials'))) {
assert.match(step, /output-env-credentials: false/u);
assert.match(step, /disable-retry: true/u);
}
assert.match(workflow, /upload protocol-control/u);
assert.match(workflow, /upload "protocol-cell-\$CELL_ID"/u);
assert.match(workflow, /upload protocol-report/u);
assert.match(workflow, /--controller-manifest "\$PAPERCLIP_RUNNER_EVAL_CONTROL_MANIFEST"/u);
});
test('Braintrust-controlled workers do not require an unused direct provider credential', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const script = workflowShell(workflow, 'Run one immutable direct protocol cell');
const credentialCheck = script.slice(script.indexOf('if [ "$ACCOUNTING_MODE"'), script.indexOf('if [ "$PROVIDER"'));
assert.ok(credentialCheck.includes('test -n "${!CREDENTIAL_NAME:-}"'));
for (const [mode, credential, status] of [['controlled', '', 0], ['legacy', '', 1], ['legacy', 'synthetic-only', 0]]) {
const result = spawnSync('bash', ['-euc', credentialCheck], { encoding: 'utf8', env: {
ACCOUNTING_MODE: mode, CREDENTIAL_NAME: 'OPENAI_API_KEY', OPENAI_API_KEY: credential,
} });
assert.equal(result.status, status, result.stderr);
}
});
test('private archive jobs consume verified controller code without resolving dependencies', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const builder = workflow.slice(workflow.indexOf(' build_controller:'), workflow.indexOf(' prepare_controller:'));
const preparer = workflow.slice(workflow.indexOf(' prepare_controller:'), workflow.indexOf(' eval_shard_0:'));
assert.doesNotMatch(builder, /id-token: write|environment:|secrets\./u);
assert.match(builder, /build-runtime.mjs/u);
assert.doesNotMatch(builder, /--no-frozen-lockfile/u);
assert.match(builder, /test "\$RESOLVED_LOCK_SHA256" = "\$EXPECTED_LOCK_SHA256"/u);
assert.match(builder, /pnpm install --frozen-lockfile --ignore-scripts --store-dir/u);
assert.doesNotMatch(preparer, /pnpm install|build-runtime.mjs|cache: pnpm/u);
assert.ok(preparer.indexOf('verify-runtime.mjs') < preparer.indexOf('hosted.mjs" prepare'));
assert.match(preparer, /needs\.build_controller\.outputs\.runtime_receipt_sha256/u);
for (const job of workflow.split(/\n(?= [A-Za-z0-9_]+:)/u).filter(value => value.includes('id-token: write'))) {
assert.doesNotMatch(job, /--no-frozen-lockfile|cache: pnpm|actions\/cache/u);
for (const install of job.matchAll(/pnpm install[^\n]*/gu)) {
assert.match(install[0], /--frozen-lockfile/u);
assert.match(install[0], /--ignore-scripts/u);
assert.match(install[0], /--store-dir/u);
}
}
});
test('controller dependency lock rejects resolution or byte drift before installation', async () => {
const workflow = await readFile(workflowPath, 'utf8');
const script = workflowShell(workflow, 'Build portable controller in an unprivileged job');
const pin = script.slice(script.indexOf('test "$RESOLVED_LOCK_SHA256"'), script.indexOf('pnpm install'));
const root = await mkdtemp(resolve(tmpdir(), 'runner-reviewed-lock-'));
const digest = createHash('sha256').update('reviewed dependencies').digest('hex');
try {
await mkdir(resolve(root, 'runner-controller-lock'));
for (const [index, resolved, bytes, status] of [[0, digest, 'reviewed dependencies', 0], [1, 'f'.repeat(64), 'reviewed dependencies', 1], [2, digest, 'changed dependencies', 1]]) {
const cwd = resolve(root, `worker-${index}`); await mkdir(cwd);
await writeFile(resolve(root, 'runner-controller-lock/pnpm-lock.yaml'), bytes);
const result = spawnSync('bash', ['-euc', pin], { cwd, encoding: 'utf8', env: {
PATH: process.env.PATH, RUNNER_TEMP: root, EXPECTED_LOCK_SHA256: digest, RESOLVED_LOCK_SHA256: resolved,
} });
assert.equal(result.status, status, result.stderr);
if (status === 0) assert.equal(await readFile(resolve(cwd, 'pnpm-lock.yaml'), 'utf8'), bytes);
else await assert.rejects(readFile(resolve(cwd, 'pnpm-lock.yaml')), { code: 'ENOENT' });
}
} finally { await rm(root, { recursive: true, force: true }); }
});