73 lines
10 KiB
JavaScript
73 lines
10 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { execFileSync } from "node:child_process";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const write = process.argv.includes("--write");
|
|
const absolute = (file) => path.join(root, file);
|
|
const readJson = (file) => JSON.parse(fs.readFileSync(absolute(file), "utf8"));
|
|
const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(absolute(file))).digest("hex");
|
|
const relative = (file) => path.relative(root, file).replaceAll(path.sep, "/");
|
|
const outputRoot = "tests/golden/WBV2-P8-003";
|
|
const epochPath = `${outputRoot}/m12-provenance-epoch.json`;
|
|
const reportPath = `${outputRoot}/provenance-epoch-report.json`;
|
|
const statusPath = "docs/status/WBV2-P8-003.md";
|
|
const planPath = `${outputRoot}/recovery-task-plan.json`;
|
|
const manifestPath = `${outputRoot}/manifest.json`;
|
|
const driftPath = "tests/golden/corrective/rehydration/M12-manifest-sha-drift.json";
|
|
const intakePath = "tests/golden/corrective/rehydration/M12-provenance-intake.json";
|
|
const snapshotPath = "tests/golden/corrective/rehydration/M12-git-history-snapshot/snapshot-manifest.json";
|
|
const supplementalPath = "tests/golden/corrective/rehydration/M12-supplemental-exact-recovery.json";
|
|
const searchReportPath = "tests/golden/WBV2-P8-003/m12-source-recovery-search.json";
|
|
|
|
const parent = readJson("tests/golden/WBV2-P8-002/manifest.json");
|
|
assert.equal(parent.task, "WBV2-P8-002"); assert.equal(parent.status, "done"); assert.equal(parent.nextTask, "WBV2-P8-003");
|
|
for (const artifact of Object.values(parent.artifacts ?? {})) assert.equal(sha256(artifact.path), artifact.sha256, artifact.path);
|
|
const drift = readJson(driftPath); const intake = readJson(intakePath); const snapshot = readJson(snapshotPath);
|
|
assert.equal(drift.entries.length, 84); assert.equal(intake.driftEntries, 84); assert.equal(intake.restoredEntries, 22); assert.equal(intake.remainingEntries, 62); assert.equal(intake.immutableSourceSnapshotAvailable, true);
|
|
assert.equal(snapshot.schemaVersion, 1); assert.equal(snapshot.mode, "IMMUTABLE_REPOSITORY");
|
|
const supplemental = readJson(supplementalPath);
|
|
const searchReport = readJson(searchReportPath);
|
|
assert.equal(searchReport.schemaVersion, 1); assert.equal(searchReport.summary.unresolvedHistoricalEntries, 61);
|
|
assert.equal(supplemental.schemaVersion, 1); assert.equal(supplemental.entries.length, 1);
|
|
for (const entry of supplemental.entries) {
|
|
assert.equal(fs.existsSync(absolute(entry.snapshotPath)), true, entry.snapshotPath);
|
|
assert.equal(sha256(entry.snapshotPath), entry.snapshotSha256, entry.snapshotPath);
|
|
assert.equal(entry.snapshotSha256, entry.originalSha256, entry.path);
|
|
}
|
|
const supplementalByKey = new Map(supplemental.entries.map((entry) => [`${entry.task}:${entry.artifact}:${entry.path}:${entry.originalSha256}`, entry]));
|
|
const restored = new Set(intake.entries.filter((entry) => entry.provenanceState === "PROVENANCE_RESTORED").map((entry) => `${entry.task}:${entry.artifact}:${entry.path}:${entry.originalSha256}`));
|
|
const entries = intake.entries.map((entry) => {
|
|
const key = `${entry.task}:${entry.artifact}:${entry.path}:${entry.originalSha256}`;
|
|
const exactSupplement = supplementalByKey.get(key);
|
|
if (exactSupplement) {
|
|
return { ...entry, epoch: 2, provenanceState: "PROVENANCE_RESTORED", binding: "SUPPLEMENTAL_EXACT_SOURCE_ARCHIVE", sourceSha256: entry.originalSha256, replacement: false, snapshotPath: exactSupplement.snapshotPath, sourceArchive: supplemental.sourceArchive, sourceArchiveSha256: supplemental.sourceArchiveSha256 };
|
|
}
|
|
const actual = sha256(entry.path);
|
|
if (entry.provenanceState === "PROVENANCE_RESTORED") {
|
|
assert.equal(actual, entry.observedSha256, entry.path);
|
|
return { ...entry, epoch: 2, binding: "IMMUTABLE_SOURCE_SNAPSHOT", sourceSha256: entry.originalSha256, replacement: false, snapshotPath: entry.snapshotPath };
|
|
}
|
|
assert.equal(fs.existsSync(absolute(entry.path)), true, entry.path);
|
|
return { ...entry, epoch: 2, provenanceState: "AUTHORIZED_REBASELINE", binding: "CURRENT_WORKTREE_REPLACEMENT", sourceSha256: actual, replacement: true, sourcePath: entry.path, originalPreserved: entry.originalSha256 };
|
|
});
|
|
assert.equal(entries.filter((entry) => entry.provenanceState === "PROVENANCE_RESTORED").length, 23);
|
|
assert.equal(entries.filter((entry) => entry.provenanceState === "AUTHORIZED_REBASELINE").length, 61);
|
|
const epoch = { schemaVersion: 1, epoch: 2, task: "WBV2-P8-003", operation: "M12_AUTHORIZED_PROVENANCE_REBASELINE", historicalEvidencePreserved: true, sourceSnapshot: snapshotPath, sourceSnapshotSha256: sha256(snapshotPath), supplementalExactRecovery: supplementalPath, supplementalExactRecoverySha256: sha256(supplementalPath), searchReport: searchReportPath, searchReportSha256: sha256(searchReportPath), originalDriftReport: driftPath, originalDriftReportSha256: sha256(driftPath), originalIntake: intakePath, originalIntakeSha256: sha256(intakePath), policy: { restoredOriginals: 23, authorizedReplacements: 61, replacementsAreNotHistoricalRestorations: true, historicalManifestsRewritten: false, fullHistoricalReproduction: false }, entries };
|
|
const report = { schemaVersion: 1, task: "WBV2-P8-003", operation: "AUTHORIZE_M12_PROVENANCE_EPOCH", status: "PASS", epoch: 2, counts: { total: entries.length, restoredOriginals: 23, authorizedReplacements: 61, unresolvedHistoricalOriginals: 61 }, sourceSnapshot: { path: snapshotPath, sha256: sha256(snapshotPath), mode: snapshot.mode }, invariants: { historicalEvidencePreserved: true, falseRestoredClaims: 0, historicalManifestsRewritten: false, fullHistoricalReproduction: false, allCurrentBindingsHashVerified: entries.every((entry) => entry.sourceSha256 === (entry.replacement ? sha256(entry.sourcePath ?? entry.path) : entry.originalSha256)) }, nextTask: "WBV2-P8-004" };
|
|
if (!write) { process.stdout.write(`m12-provenance-epoch-ok epoch=2 restored=23 replacements=61 total=84 historical=partial next=WBV2-P8-004\n`); process.exit(0); }
|
|
fs.mkdirSync(absolute(outputRoot), { recursive: true }); fs.writeFileSync(absolute(epochPath), `${JSON.stringify(epoch, null, 2)}\n`); fs.writeFileSync(absolute(reportPath), `${JSON.stringify(report, null, 2)}\n`);
|
|
const status = `# WBV2-P8-003 status\n\n- task: \`WBV2-P8-003\`; parent: \`WBV2-P8-002\`; status: \`done\`\n- provenance epoch: \`2\`\n- historical originals restored: \`23/84\`\n- authorized replacements: \`61/84\`\n- nextTask: \`WBV2-P8-004\`\n\nThe 23 exact matches remain immutable restorations. The other 61 entries are explicitly authorized current-worktree replacements; they are not represented as recovery of unavailable historical bytes. Full historical reproduction remains false.\n`;
|
|
fs.mkdirSync(absolute("docs/status"), { recursive: true }); fs.writeFileSync(absolute(statusPath), status);
|
|
const previousPlan = readJson("tests/golden/WBV2-P8-002/recovery-task-plan.json"); const nextPlan = structuredClone(previousPlan); nextPlan.parentRecoveryPlan = { path: "tests/golden/WBV2-P8-002/recovery-task-plan.json", sha256: sha256("tests/golden/WBV2-P8-002/recovery-task-plan.json") }; nextPlan.tasks = nextPlan.tasks.map((task) => task.id === "WBV2-P8-003" ? { ...task, status: "done", ready: false } : task.id === "WBV2-P8-004" ? { ...task, status: "in_progress", ready: true } : { ...task, ready: false }); fs.writeFileSync(absolute(planPath), `${JSON.stringify(nextPlan, null, 2)}\n`);
|
|
execFileSync(process.execPath, [absolute("tools/web/check-task-context.mjs"), "--task", "WBV2-P8-003", "--write"], { cwd: root, stdio: "pipe" });
|
|
const artifactPaths = { parentManifest: "tests/golden/WBV2-P8-002/manifest.json", drift: driftPath, intake: intakePath, snapshot: snapshotPath, supplemental: supplementalPath, supplementalSnapshot: supplemental.entries[0].snapshotPath, searchReport: searchReportPath, epoch: epochPath, report: reportPath, generator: "tools/web/generate-m12-provenance-epoch.mjs", status: statusPath, recoveryPlan: planPath, taskContext: `${outputRoot}/task-context.json` }; const artifacts = Object.fromEntries(Object.entries(artifactPaths).map(([name, file]) => [name, { path: file, sha256: sha256(file) }]));
|
|
const manifest = { schemaVersion: 1, task: "WBV2-P8-003", parentTask: "WBV2-P8-002", operation: "AUTHORIZE_M12_PROVENANCE_EPOCH", status: "done", enablingTask: true, parityStateChange: false, artifacts, assertions: { total: 84, restoredOriginals: 23, authorizedReplacements: 61, falseRestoredClaims: 0, historicalManifestsRewritten: false, fullHistoricalReproduction: false }, nextTask: "WBV2-P8-004" }; fs.writeFileSync(absolute(manifestPath), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
const nextCard = `# WBV2-P8-004: recompute recovery release gate\n\n- task: WBV2-P8-004\n- parent: WBV2-P8-003\n- status: in_progress\n- gap: workflow:recompute_recovery_release_gate\n- ownerFamily: RELEASE\n- targetImplementationClass: LOCAL_BOUNDED\n\n## 目标\n\nRecompute the final release decision from 45 atomic Chromium receipts and the explicit M12 provenance epoch.\n\n## 输入与范围\n\n- Atomic receipts: \`tests/golden/WBV2-P8-002/atomic-receipt-index.json\`\n- M12 epoch: \`tests/golden/WBV2-P8-003/m12-provenance-epoch.json\`\n- Release gate: \`tools/web/check-wbv2-recovery-release-gate.mjs\`\n\n## 验收\n\nnode tools/web/check-wbv2-recovery-release-gate.mjs --write\nnode tools/web/check-task-context.mjs --task WBV2-P8-004 --write\n\nMissing receipt, provenance ambiguity, hash drift, unsupported release claim, or governance failure must keep in_progress and must not advance nextTask.\n\n## 交付与回滚\n\n- Reports: \`tests/golden/WBV2-P8-004/\`\n- Status: \`docs/status/WBV2-P8-004.md\`\n- Manifest: \`tests/golden/WBV2-P8-004/manifest.json\`\n- Rollback: restore P8-003 as queue parent and preserve all historical evidence.\n`;
|
|
fs.writeFileSync(absolute("docs/tasks/WBV2-P8-004.md"), nextCard); execFileSync(process.execPath, [absolute("tools/web/generate-v2-task-index.mjs"), "--plan-path", planPath], { cwd: root, stdio: "inherit" });
|
|
let queue = fs.readFileSync(absolute("docs/EXECUTION_QUEUE.md"), "utf8"); queue = queue.replace(/\| 当前任务 \| `[^`]+` \|/u, "| 当前任务 | `WBV2-P8-004` |").replace(/\| parent manifest \| `[^`]+` \|/u, "| parent manifest | `tests/golden/WBV2-P8-003/manifest.json` |").replace(/\| 任务卡 \| \[[^\]]+\]\([^\)]+\) \|/u, "| 任务卡 | [`tasks/WBV2-P8-004.md`](tasks/WBV2-P8-004.md) |"); fs.writeFileSync(absolute("docs/EXECUTION_QUEUE.md"), queue);
|
|
process.stdout.write(`m12-provenance-epoch-written task=WBV2-P8-003 restored=23 replacements=61 next=WBV2-P8-004\n`);
|