feat: P6 commit-addressed Evidence materialization acceptance runner

This commit is contained in:
2026-08-13 12:41:11 +02:00
parent f09ab2b8c6
commit 124891bbfe
4 changed files with 1614 additions and 0 deletions
File diff suppressed because it is too large Load Diff
+158
View File
@@ -0,0 +1,158 @@
import assert from "node:assert/strict";
import { mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
import {
CHECK_IDS,
canonicalIntegrationBase,
cleanupOwnedRun,
createOwnedRun,
readAndValidateOwnership,
runIntegration,
validateReport,
validateRunRoot,
} from "./p6-acceptance.mjs";
const roots = [];
async function fakeRepository() {
const root = await mkdtemp(join(tmpdir(), "p6-acceptance-repo-"));
roots.push(root);
await mkdir(join(root, ".artifacts", "p6-integration"), { recursive: true });
await mkdir(join(root, ".artifacts", "p2-integration"), { recursive: true });
await mkdir(join(root, ".artifacts", "p1-integration"), { recursive: true });
await mkdir(join(root, ".artifacts", "manual-acceptance", "p11"), { recursive: true });
return root;
}
test.afterEach(async () => {
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
});
test("run roots are only canonical direct p6 integration children", async () => {
const repositoryRoot = await fakeRepository();
const base = canonicalIntegrationBase(repositoryRoot);
const id = `p6-${"a".repeat(32)}`;
assert.equal(validateRunRoot(repositoryRoot, join(base, id), id), join(base, id));
for (const candidate of [
base,
join(repositoryRoot, ".artifacts", "manual-acceptance", "p11"),
join(repositoryRoot, ".artifacts", "p1-integration", id),
join(repositoryRoot, ".artifacts", "p2-integration", id),
join(base, id, "nested"),
join(base, "foreign"),
]) {
assert.throws(() => validateRunRoot(repositoryRoot, candidate, id));
}
assert.throws(() => validateRunRoot(repositoryRoot, join(base, `p6-${"A".repeat(32)}`), `p6-${"A".repeat(32)}`));
});
test("cleanup refuses p1, p2, p11, manual, sibling, and wrong-nonce roots", async () => {
const repositoryRoot = await fakeRepository();
const run = await createOwnedRun({ repositoryRoot });
await readAndValidateOwnership({ repositoryRoot, runRoot: run.root, expectedNonce: run.nonce });
for (const bad of [
join(repositoryRoot, ".artifacts", "p1-integration", `p1-${"b".repeat(32)}`),
join(repositoryRoot, ".artifacts", "p2-integration", `p2-${"c".repeat(32)}`),
join(repositoryRoot, ".artifacts", "manual-acceptance", "p11"),
join(canonicalIntegrationBase(repositoryRoot), `p6-${"d".repeat(32)}`),
]) {
await assert.rejects(cleanupOwnedRun({ repositoryRoot, runRoot: bad, expectedNonce: run.nonce }));
}
await assert.rejects(cleanupOwnedRun({ repositoryRoot, runRoot: run.root, expectedNonce: "0".repeat(64) }));
});
test("cleanup removes exactly one owned p6 root", async () => {
const repositoryRoot = await fakeRepository();
const run = await createOwnedRun({ repositoryRoot });
const sibling = join(canonicalIntegrationBase(repositoryRoot), `p6-${"e".repeat(32)}`);
await mkdir(sibling);
await writeFile(join(sibling, "sentinel"), "foreign");
await cleanupOwnedRun({ repositoryRoot, runRoot: run.root, expectedNonce: run.nonce });
await assert.rejects(readFile(join(run.root, "ownership.json")));
assert.equal(await readFile(join(sibling, "sentinel"), "utf8"), "foreign");
});
function resultFor(id) {
return {
id,
status: "PASS",
startedAt: "2026-08-12T00:00:00.000Z",
finishedAt: "2026-08-12T00:00:01.000Z",
commands: ["node"],
artifacts: [{ path: `logs/${id}.json`, sha256: "a".repeat(64) }],
};
}
test("report validation requires exact p6 identity, check order, and unique artifacts", () => {
const report = {
schemaVersion: 1,
runId: `p6-${"f".repeat(32)}`,
startedAt: "2026-08-12T00:00:00.000Z",
finishedAt: "2026-08-12T00:00:10.000Z",
command: "p6-acceptance integration --keep",
overall: "PASS",
checks: CHECK_IDS.map(resultFor),
};
assert.doesNotThrow(() => validateReport(report));
const invalid = structuredClone(report);
invalid.runId = `p2-${"f".repeat(32)}`;
assert.throws(() => validateReport(invalid));
const duplicate = structuredClone(report);
duplicate.checks[1].artifacts[0].path = duplicate.checks[0].artifacts[0].path;
assert.throws(() => validateReport(duplicate), /duplicated/);
const reordered = structuredClone(report);
reordered.checks.reverse();
reordered.overall = "FAIL";
assert.throws(() => validateReport(reordered));
});
test("public wrapper uses a strict empty environment", async () => {
const wrapper = await readFile(join(dirname(fileURLToPath(import.meta.url)), "..", "..", "scripts", "p6-acceptance.sh"), "utf8");
assert.match(wrapper, /safe_env=\(\/usr\/bin\/env -i/);
assert.doesNotMatch(wrapper, /LANG|LC_ALL|TZ/);
assert.doesNotMatch(wrapper, /P6_ACCEPTANCE_FAIL_AT/);
});
test("synthetic integration cleans up successful non-kept runs", async () => {
const repositoryRoot = await fakeRepository();
const result = await runIntegration({ repositoryRoot, keep: false, env: { P6_ACCEPTANCE_SYNTHETIC: "1" } });
assert.equal(result.exitCode, 0);
assert.equal(result.retained, false);
await assert.rejects(readFile(join(result.runRoot, "ownership.json")));
});
test("synthetic integration retains kept runs with bounded reports", async () => {
const repositoryRoot = await fakeRepository();
const result = await runIntegration({ repositoryRoot, keep: true, env: { P6_ACCEPTANCE_SYNTHETIC: "1" } });
assert.equal(result.exitCode, 0);
assert.equal(result.retained, true);
const report = JSON.parse(await readFile(join(result.runRoot, "report.json"), "utf8"));
assert.equal(report.overall, "PASS");
const reportMd = await readFile(join(result.runRoot, "report.md"), "utf8");
assert.match(reportMd, /P6 automated integration: PASS/);
assert.match(reportMd, /P6 manual acceptance: PENDING/);
const reportJsonStat = await stat(join(result.runRoot, "report.json"));
const reportMdStat = await stat(join(result.runRoot, "report.md"));
assert.ok(reportJsonStat.size <= 64 * 1024, `report.json too large: ${reportJsonStat.size}`);
assert.ok(reportMdStat.size <= 32 * 1024, `report.md too large: ${reportMdStat.size}`);
});
test("synthetic injected failure retains the owned run and records a single failed report", async () => {
const repositoryRoot = await fakeRepository();
const result = await runIntegration({
repositoryRoot,
keep: false,
env: { P6_ACCEPTANCE_SYNTHETIC: "1", P6_ACCEPTANCE_FAIL_AT: CHECK_IDS[2] },
});
assert.equal(result.exitCode, 1);
assert.equal(result.retained, true);
const report = JSON.parse(await readFile(join(result.runRoot, "report.json"), "utf8"));
assert.equal(report.overall, "FAIL");
const failed = report.checks.find((check) => check.id === CHECK_IDS[2]);
assert.equal(failed.status, "FAIL");
const roots = await readFile(join(result.runRoot, "ownership.json"), "utf8");
assert.match(roots, /p6-acceptance/);
});
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env -S -i PATH=/usr/bin:/bin /bin/bash
set -euo pipefail
script_path=${BASH_SOURCE[0]}
script_dir=${script_path%/*}
[[ "$script_dir" != "$script_path" ]] || script_dir=.
repo_root="$(cd -P -- "$script_dir/.." && pwd)"
if [[ $# -lt 1 || "$1" != "integration" || $# -gt 2 || ( $# -eq 2 && "$2" != "--keep" ) ]]; then
printf 'usage: %s integration [--keep]
' "$0" >&2
exit 2
fi
canonical_file() {
local path=$1 target parent leaf
[[ "$path" = /* ]] || return 1
while [[ -L "$path" ]]; do
target=$(/usr/bin/readlink "$path") || return 1
if [[ "$target" = /* ]]; then path=$target; else path="${path%/*}/$target"; fi
done
parent=${path%/*}; leaf=${path##*/}
parent=$(cd -P -- "$parent" && pwd) || return 1
printf '%s/%s
' "$parent" "$leaf"
}
node_path= npm_path= toolchain_prefix=
for pair in "/usr/bin/node|/usr/bin/npm|/usr" "/opt/homebrew/bin/node|/opt/homebrew/bin/npm|/opt/homebrew" "/usr/local/bin/node|/usr/local/bin/npm|/usr/local"; do
node_candidate=${pair%%|*}; remainder=${pair#*|}; npm_candidate=${remainder%%|*}; prefix=${remainder##*|}
[[ -e "$node_candidate" && -e "$npm_candidate" ]] || continue
resolved_node=$(canonical_file "$node_candidate") || continue
resolved_npm=$(canonical_file "$npm_candidate") || continue
[[ -f "$resolved_node" && ! -L "$resolved_node" && -x "$resolved_node" ]] || continue
[[ -f "$resolved_npm" && ! -L "$resolved_npm" ]] || continue
[[ "${resolved_npm##*/}" = "npm-cli.js" ]] || continue
node_path=$resolved_node; npm_path=$resolved_npm; toolchain_prefix=$prefix
break
done
[[ -n "$node_path" && -n "$npm_path" && -n "$toolchain_prefix" ]] || {
printf 'trusted fixed Node/npm toolchain is unavailable
' >&2
exit 127
}
wrapper_root=$(/usr/bin/mktemp -d /tmp/thoth-p6-wrapper.XXXXXXXX)
trap '/bin/rm -rf -- "$wrapper_root"' EXIT HUP INT TERM
/bin/mkdir -m 700 "$wrapper_root/home" "$wrapper_root/tmp"
owned_path="${node_path%/*}:/usr/bin:/bin"
build_env=(/usr/bin/env -i "PATH=$owned_path" "HOME=$wrapper_root/home" "TMPDIR=$wrapper_root/tmp")
/bin/rm -rf -- "$repo_root/backend/dist"
"${build_env[@]}" "$node_path" "$npm_path" --prefix "$repo_root/backend" run build
p6_real_home=$(/bin/bash -lc 'printf "%s" ~' 2>/dev/null || true)
safe_env=(/usr/bin/env -i "PATH=$owned_path" "HOME=$wrapper_root/home" "TMPDIR=$wrapper_root/tmp"
"P6_REAL_HOME=${p6_real_home:-}" "THT_BIN=$repo_root/harness/.venv/bin/tht" "P6_ACCEPTANCE_NODE_PATH=$node_path" "P6_ACCEPTANCE_NPM_PATH=$npm_path")
set +e
"${safe_env[@]}" "$node_path" "$repo_root/backend/scripts/p6-acceptance.mjs" "$@"
status=$?
set -e
exit "$status"
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
bash -n "$repo_root/scripts/p6-acceptance.sh" "$repo_root/scripts/test-p6-acceptance.sh"
node --check "$repo_root/backend/scripts/p6-acceptance.mjs"
node --check "$repo_root/backend/scripts/p6-acceptance.test.mjs"
npm --prefix "$repo_root/backend" run build
node --test "$repo_root/backend/scripts/p6-acceptance.test.mjs"