import { createHash } from "node:crypto"; import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { basename, resolve } from "node:path"; import { validateReleaseCandidateRecord } from "./lib/release-candidate.mjs"; const runId = process.argv[2]; if (!runId) throw new Error("Usage: node scripts/record-wp7-01-manual-review.mjs "); const runDirectory = resolve("artifacts", "tdd", runId); const recordPath = resolve(runDirectory, "release-candidate.json"); const evidencePath = resolve(runDirectory, "evidence.json"); if (!existsSync(recordPath) || !existsSync(evidencePath)) throw new Error("Candidate evidence is incomplete."); const record = validateReleaseCandidateRecord(JSON.parse(readFileSync(recordPath, "utf8"))); const evidence = JSON.parse(readFileSync(evidencePath, "utf8")); if (evidence.status !== "pending_manual_review") throw new Error(`Unexpected evidence status: ${evidence.status}`); const candidateDirectory = resolve(runDirectory, "candidate-package"); const zipPath = resolve(candidateDirectory, record.candidate_package.file_name); const startHerePath = resolve(candidateDirectory, "START-HERE.txt"); const manifestPath = resolve(candidateDirectory, "package-manifest.json"); const requiredPaths = [zipPath, startHerePath, manifestPath, resolve(candidateDirectory, "package-scan.json"), resolve(candidateDirectory, "process-tree.json")]; if (requiredPaths.some((path) => !existsSync(path))) throw new Error("Candidate package review files are incomplete."); const zipHash = createHash("sha256").update(readFileSync(zipPath)).digest("hex").toUpperCase(); if (zipHash !== record.candidate_package.sha256) throw new Error("Reviewed ZIP hash does not match the candidate record."); const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); if (manifest.zip_sha256 !== zipHash || manifest.fixed_port !== record.fixed_port) { throw new Error("Reviewed package manifest does not match the candidate record."); } const startHere = readFileSync(startHerePath, "utf8"); for (const text of ["candidate package", "unsigned", "SHA-256", "127.0.0.1:43121", "not a final P0-A release"]) { if (!startHere.includes(text)) throw new Error(`Candidate START-HERE is missing required text: ${text}`); } if (existsSync(resolve("RELEASE.json"))) throw new Error("A final repository RELEASE.json was written prematurely."); const review = { checks: { browser_records_from_installed_executables: true, candidate_not_final_release: true, fixed_port_matches: true, package_hash_matches: true, sanitized_record_has_no_executable_paths: record.browsers.every((browser) => !("path" in browser) && !("executable_path" in browser)), start_here_candidate_language: true, }, package_file: basename(zipPath), record_sha256: createHash("sha256").update(readFileSync(recordPath)).digest("hex").toUpperCase(), reviewed_at: new Date().toISOString(), reviewer: "codex", schema_version: "1.0", status: "passed", }; writeFileSync(resolve(runDirectory, "manual-review.json"), `${JSON.stringify(review, null, 2)}\n`); writeFileSync(evidencePath, `${JSON.stringify({ ...evidence, manual_review: "manual-review.json", status: "passed" }, null, 2)}\n`); console.log(JSON.stringify(review, null, 2));