Files
PaperClipAI/tests/runner-e2e/stock-harness-checks.mjs
DottaandPaperclip b85668c406 Fix skill capability metadata and publish the bounded repair comparison
Regenerate both capability inventories for the early issue-document recipe and reject stale derived manifests before stock-harness provider admission. Preserve the eight measured repair results, automatic recovery cost, and OpenCode delivery diagnosis.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-02 16:30:31 -05:00

222 lines
16 KiB
JavaScript

// Deterministic prerequisite for the stock-harness Product E2E suite. No model
// calls or credentials. Reuse the existing protocol assertions, not model
// self-reports about what its system instructions contain.
import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
const root = resolve(import.meta.dirname, "../..");
export const stockHarnessGates = [
{ id: "SH-1", name: "Native Codex additive instructions", cwd: "packages/paperclip-runner", files: [
"src/drivers/codex/codex-app-server-driver.test.ts",
"src/drivers/codex/codex-app-server-driver.lifecycle.test.ts",
"src/live/runnerd-codex-transport.test.ts",
"src/live/live-session.test.ts",
"src/cli/eval-provider-runtime.test.ts",
], testPattern: "adds Paperclip developer instructions|preserves stock Codex instructions|captures exact provider frames and correlates|direct eval provider runtime|exposes native completion consistently on fresh and resumed sessions|passes caller-supplied native system instructions",
required: ["adds Paperclip developer instructions", "preserves stock Codex instructions on task recovery",
"preserves stock Codex instructions on prepared recovery", "preserves stock Codex instructions on direct recovery",
"captures exact provider frames and correlates", "exposes native completion consistently on fresh and resumed sessions",
"passes caller-supplied native system instructions"] },
{ id: "SH-2", name: "Production-default hire bundle", cwd: ".", files: [
"server/src/__tests__/agent-skills-routes.test.ts",
"server/src/services/onboarding-first-task-assets.test.ts",
], required: ["materializes minimal default instructions for non-CEO agents with no prompt template"] },
{ id: "SH-3", name: "Shared legacy startup and continuation", cwd: ".", files: [
"packages/adapter-utils/src/server-utils.test.ts",
"packages/adapter-utils/src/prompt-sections.test.ts",
"packages/adapter-utils/src/acpx-engine/execute.test.ts",
"packages/adapters/pi-local/src/server/execute.remote.test.ts",
"packages/adapters/opencode-local/src/server/execute.test.ts",
"packages/adapters/cursor-cloud/src/server/execute.test.ts",
"server/src/__tests__/codex-local-execute.test.ts",
], required: ["keeps task and chat defaults to identity and connection guidance",
"does not restore generic procedures on resume or with the legacy opt-in"] },
// Hermes is not in the root Vitest project list. Run its package config so
// the requested file cannot silently disappear from discovery.
{ id: "SH-3-hermes", name: "Hermes shared-prompt delivery", cwd: "packages/adapters/hermes",
files: ["src/server/prompt-rendering.test.ts"],
required: ["renders standard assignment wake with task authority", "renders scoped planning wake authority"] },
{ id: "SH-eval", name: "Independent oracle and qualification admission", cwd: ".",
config: "tests/runner-e2e/vitest.config.ts",
files: ["tests/runner-e2e/stock-harness-manifest.test.ts", "tests/runner-e2e/paperclip-document.test.ts", "tests/runner-e2e/stock-harness.test.ts", "tests/runner-e2e/stock-harness-checks.test.mjs",
"tests/runner-e2e/stock-harness-admission.test.ts", "tests/runner-e2e/stock-harness-digest.test.ts",
"tests/runner-e2e/select-rerun-artifacts.test.ts"],
required: ["requires generated capability manifests for the current skill sources", "rejects old SHA before providers", "allows toolchain paths and excludes every present or future credential",
"changes when the evaluated server/src/onboarding-assets/default/AGENTS.md changes",
"changes when the evaluated packages/adapter-utils/src/server-utils.ts changes",
"changes when the evaluated packages/shared/src/connection-intent-guidance.ts changes",
"retains credential-free prerequisites inside the exact campaign root"] },
];
export function gradeGate(gate, report, exitCode) {
const assertions = (report?.testResults ?? []).flatMap(file => file.assertionResults ?? []);
const requirements = (gate.required ?? []).map(name => ({ name,
passed: assertions.some(assertion => assertion.fullName?.includes(name) && assertion.status === "passed") }));
const files = gate.files.map(file => ({ file,
passed: (report?.testResults ?? []).some(result => result.name?.endsWith(file) && result.status === "passed" &&
result.assertionResults?.some(assertion => assertion.status === "passed") &&
result.assertionResults.every(assertion => assertion.status === "passed" ||
(gate.testPattern && assertion.status === "skipped" && !(gate.required ?? []).some(name => assertion.fullName?.includes(name))))) }));
return { id: gate.id, name: gate.name, passed: exitCode === 0 && requirements.every(row => row.passed) && files.every(row => row.passed),
exitCode, files, requirements, total: report?.numTotalTests ?? 0, passedTests: report?.numPassedTests ?? 0,
failedTests: report?.numFailedTests ?? 0, pendingTests: report?.numPendingTests ?? 0 };
}
export function sourceFingerprint() {
const hash = createHash("sha256");
const sources = new Set([
...stockHarnessGates.flatMap(gate => gate.files.map(file => join(gate.cwd, file))),
"tests/runner-e2e/stock-harness.ts", "tests/runner-e2e/stock-harness-checks.mjs", "tests/runner-e2e/catalog.ts",
"tests/runner-e2e/stock-harness-admission.ts", "tests/runner-e2e/launch.ts", "tests/runner-e2e/runner.spec.ts",
"tests/runner-e2e/context-integrity-cases.ts", "tests/runner-e2e/context-integrity-flow.ts", "tests/runner-e2e/context-integrity-scoring.ts",
"tests/runner-e2e/stock-harness-manifest.ts", "packages/paperclip-runner/scripts/generate-capability-contract.mjs",
"packages/paperclip-runner/spec/capability/source-contract.json",
"packages/paperclip-runner/scripts/check-capability-inventory.mjs", "packages/paperclip-runner/scripts/lib/capability-inventory.mjs",
"packages/paperclip-runner/spec/capability/capabilities.yaml", "packages/paperclip-runner/spec/capability/eval-traceability.yaml",
"packages/paperclip-runner/spec/capability/mcp-tool-map.yaml", "packages/paperclip-runner/spec/capability/inventory.schema.json",
"packages/paperclip-runner/src/generated/capability-contract.ts", "packages/paperclip-runner/docs/capability-contract.md",
...["capabilities.yaml", "mcp-tool-map.yaml", "eval-traceability.yaml", "capability-contract.md", "downstream-handoff.md"]
.map(file => `packages/paperclip-runner/generated/capability/${file}`),
"packages/adapter-utils/src/server-utils.ts", "packages/shared/src/connection-intent-guidance.ts",
"server/src/onboarding-assets/default/AGENTS.md", "server/src/routes/agents.ts", "scripts/ensure-plugin-build-deps.mjs",
"packages/paperclip-runner/src/drivers/codex/codex-app-server-driver-impl.ts",
"packages/paperclip-runner/src/live/runnerd-codex-transport.ts",
"packages/paperclip-runner/src/live/live-session.ts",
"packages/paperclip-runner/runner/crates/runner-core/src/codex_provider.rs",
"packages/paperclip-runner/runner/crates/runner-core/src/bin/fake-codex-app-server.rs",
"packages/paperclip-runner/runner/Cargo.toml", "packages/paperclip-runner/runner/Cargo.lock",
"packages/paperclip-runner/runner/crates/runner-core/Cargo.toml",
"packages/paperclip-runner/runner/crates/runner-core/tests/codex_provider.rs",
]);
const sourceErrors = [];
sources.add("skills/paperclip/SKILL.md");
sources.add("skills/paperclip/references/issue-documents.md");
for (const source of [...sources].sort()) {
hash.update(source);
try { hash.update("present\0").update(readFileSync(join(root, source))); }
catch (error) {
if (source === "skills/paperclip/references/issue-documents.md" && error.code === "ENOENT") hash.update("absent\0");
else sourceErrors.push(source);
}
}
return { fingerprint: hash.digest("hex"), sourceErrors };
}
export function assertPreflightReceipt(report, current) {
const expected = [...stockHarnessGates.map(gate => gate.id), "SH-1-rust"];
if (report?.schema !== "paperclip.stock-harness-preflight.v3" || report.passed !== true ||
report.setup?.passed !== true || report.setup?.exitCode !== 0 ||
report.setup?.sdkExitCode !== 0 || report.setup?.runnerdExitCode !== 0 ||
report.setup?.runnerdSha256 !== current.runnerdSha256 ||
report.setup?.fakeCodexSha256 !== current.fakeCodexSha256 ||
report.providerCalls !== 0 || report.sourceSha !== current.sha ||
report.sourceFingerprint !== current.fingerprint || report.sourceErrors?.length !== 0 ||
!Array.isArray(report.gates) || report.gates.length !== expected.length ||
expected.some(id => report.gates.filter(gate => gate.id === id && gate.passed === true && gate.exitCode === 0).length !== 1)) {
throw new Error("Stock harness requires passing prerequisites for this exact source SHA and fingerprint.");
}
return report;
}
export function main(args = process.argv.slice(2)) {
if (args.includes("--list")) {
console.log(JSON.stringify({ gates: stockHarnessGates, rust: "runtime_instructions_are_additive_for_codex_on_start_and_resume", live: "not invoked" }, null, 2));
return;
}
if (args.some(arg => !arg.startsWith("--output-dir=") && !arg.startsWith("--verify=") && arg !== "--allow-rust-network"))
throw new Error("Use --list, --output-dir=<path>, --verify=<receipt>, or --allow-rust-network; never paid providers.");
const git = spawnSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" });
const verify = args.find(arg => arg.startsWith("--verify="))?.slice("--verify=".length);
if (verify) {
const source = sourceFingerprint();
if (git.status !== 0 || source.sourceErrors.length) throw new Error("Cannot verify stock harness source provenance.");
const report = assertPreflightReceipt(JSON.parse(readFileSync(verify, "utf8")), {
sha: git.stdout.trim(), fingerprint: source.fingerprint,
runnerdSha256: createHash("sha256").update(readFileSync(join(root,
"packages/paperclip-runner/runner/target/debug", `paperclip-runnerd${process.platform === "win32" ? ".exe" : ""}`))).digest("hex"),
fakeCodexSha256: createHash("sha256").update(readFileSync(join(root,
"packages/paperclip-runner/runner/target/debug/fake-codex-app-server"))).digest("hex"),
});
const output = resolve(verify, "..");
for (const gate of stockHarnessGates) {
const grade = gradeGate(gate, JSON.parse(readFileSync(join(output, `${gate.id}.json`), "utf8")), 0);
if (!grade.passed) throw new Error(`Missing or failed retained prerequisite assertions: ${gate.id}`);
}
if (!/test runtime_instructions_are_additive_for_codex_on_start_and_resume \.\.\. ok/.test(readFileSync(join(output, "rust.txt"), "utf8")))
throw new Error("Missing passing retained Rust prerequisite.");
console.log(JSON.stringify(report));
return report;
}
const output = args.find(arg => arg.startsWith("--output-dir="))?.slice("--output-dir=".length) ??
join(root, "tests/runner-e2e/results", `stock-harness-preflight-${new Date().toISOString().replaceAll(":", "-")}`);
mkdirSync(output, { recursive: true });
const env = Object.fromEntries([
"PATH", "HOME", "TMPDIR", "TMP", "TEMP", "SYSTEMROOT", "LANG", "LC_ALL",
"CARGO_HOME", "RUSTUP_HOME", "CI", "GITHUB_ACTIONS",
].flatMap(name => process.env[name] === undefined ? [] : [[name, process.env[name]]]));
// A protected cold install disables lifecycle scripts. Use the same ordinary
// SDK dependency builder as server startup, before importing server tests.
const setupRun = spawnSync(process.execPath, [join(root, "scripts/ensure-plugin-build-deps.mjs")], {
cwd: root, env, encoding: "utf8", timeout: 5 * 60_000,
});
writeFileSync(join(output, "setup.txt"), `${setupRun.stdout ?? ""}\n${setupRun.stderr ?? ""}`);
// The exact daemon-frame test uses the real local Rust daemon. Legacy cold
// cells do not download native artifacts, so compile it before TS discovery.
const runnerd = setupRun.status === 0 ? spawnSync("cargo", ["build", "--locked",
...(args.includes("--allow-rust-network") ? [] : ["--offline"]),
"--workspace", "--bins"], {
cwd: join(root, "packages/paperclip-runner/runner"), env, encoding: "utf8", timeout: 10 * 60_000,
}) : null;
writeFileSync(join(output, "runnerd-build.txt"), `${runnerd?.stdout ?? ""}\n${runnerd?.stderr ?? ""}`);
const setup = { passed: setupRun.status === 0 && runnerd?.status === 0,
exitCode: setupRun.status !== 0 ? setupRun.status : runnerd?.status ?? null,
sdkExitCode: setupRun.status, runnerdExitCode: runnerd?.status ?? null };
if (!setup.passed) {
const source = sourceFingerprint();
writeFileSync(join(output, "preflight.json"), JSON.stringify({
schema: "paperclip.stock-harness-preflight.v3", sourceSha: git.stdout?.trim() || null,
sourceFingerprint: source.fingerprint, measuredAt: new Date().toISOString(), providerCalls: 0,
live: "not_run", passed: false, sourceErrors: source.sourceErrors, setup, gates: [],
}, null, 2) + "\n");
process.exitCode = 1;
return;
}
const runnerdBinary = join(root, "packages/paperclip-runner/runner/target/debug",
`paperclip-runnerd${process.platform === "win32" ? ".exe" : ""}`);
setup.runnerdSha256 = createHash("sha256").update(readFileSync(runnerdBinary)).digest("hex");
setup.fakeCodexSha256 = createHash("sha256").update(readFileSync(join(root,
"packages/paperclip-runner/runner/target/debug/fake-codex-app-server"))).digest("hex");
const results = [];
for (const gate of stockHarnessGates) {
console.log(`Checking ${gate.id}: ${gate.name}`);
const file = join(output, `${gate.id}.json`);
const run = spawnSync(process.execPath, [join(root, "node_modules/vitest/vitest.mjs"), "run", ...gate.files,
...(gate.config ? ["--config", gate.config] : []),
...(gate.testPattern ? ["--testNamePattern", gate.testPattern] : []),
"--reporter=default", "--reporter=json", `--outputFile.json=${file}`],
{ cwd: resolve(root, gate.cwd),
env: gate.id === "SH-1" ? { ...env, PAPERCLIP_STOCK_PREFLIGHT_RUNNERD: runnerdBinary } : env,
stdio: "inherit", timeout: 10 * 60_000 });
let report;
try { report = JSON.parse(readFileSync(file, "utf8")); } catch { /* missing evidence fails closed below */ }
results.push(gradeGate(gate, report, run.status));
}
const rust = spawnSync("cargo", ["test", ...(args.includes("--allow-rust-network") ? [] : ["--offline"]), "-p", "paperclip-runner-core", "--test", "codex_provider",
"runtime_instructions_are_additive_for_codex_on_start_and_resume", "--", "--exact"],
{ cwd: join(root, "packages/paperclip-runner/runner"), env, encoding: "utf8", timeout: 10 * 60_000 });
const rustOutput = `${rust.stdout ?? ""}\n${rust.stderr ?? ""}`;
writeFileSync(join(output, "rust.txt"), rustOutput);
results.push({ id: "SH-1-rust", passed: rust.status === 0 && /test runtime_instructions_are_additive_for_codex_on_start_and_resume \.\.\. ok/.test(rustOutput), exitCode: rust.status });
const { fingerprint, sourceErrors } = sourceFingerprint();
const report = { schema: "paperclip.stock-harness-preflight.v3", sourceSha: git.stdout?.trim() || null,
sourceFingerprint: fingerprint, measuredAt: new Date().toISOString(), providerCalls: 0,
live: "not_run", passed: git.status === 0 && sourceErrors.length === 0 && results.every(row => row.passed), sourceErrors, setup, gates: results };
writeFileSync(join(output, "preflight.json"), JSON.stringify(report, null, 2) + "\n");
console.log(`Stock harness prerequisite evidence: ${output}/preflight.json`);
if (!report.passed) process.exitCode = 1;
}
if (process.argv[1] && resolve(process.argv[1]) === resolve(import.meta.filename)) main();