import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const queuePath = path.join(root, "docs/EXECUTION_QUEUE.md"); const reviewPath = path.join(root, "tests/golden/corrective/C6-003/owner-review.json"); const dryRunPath = path.join(root, "tests/golden/corrective/C6-003/queue-reactivation-dry-run.json"); const writePreviewPath = path.join(root, "tests/golden/corrective/C6-003/queue-reactivation-write-preview.json"); const currentManifestPath = path.join(root, "tests/golden/M16-GAP-00276/manifest.json"); const currentStatusPath = path.join(root, "docs/status/M16-GAP-00276.md"); const completionPath = path.join(root, "tests/golden/M15-03A/completed-gap-tasks.json"); const nextPlanPath = path.join(root, "tests/golden/M15-03A/next-task-plan.json"); const catalogPath = path.join(root, "tests/golden/M15-03A/task-catalog.jsonl"); const indexPath = path.join(root, "tests/golden/M15-03A/task-index.json"); const evidenceRoot = path.join(root, "tests/golden/corrective/C6-003"); const relative = (file) => path.relative(root, file).replaceAll(path.sep, "/"); const sha256 = (bytes) => crypto.createHash("sha256").update(bytes).digest("hex"); const readJson = (file) => JSON.parse(fs.readFileSync(file, "utf8")); const writeJson = (file, value) => fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`); const write = process.argv.includes("--write"); if (!write) throw new Error("queue reactivation is write-only after review; pass --write explicitly"); const review = readJson(reviewPath); assert.equal(review.task, "C6-003"); assert.equal(review.status, "APPROVED"); assert.equal(review.authorization?.approved, true); assert.equal(review.decision?.allowQueueMutation, true); assert.equal(review.decision?.targetTask, "M16-GAP-00277"); const dryRunBytes = fs.readFileSync(dryRunPath); const writePreviewBytes = fs.readFileSync(writePreviewPath); assert.equal(sha256(dryRunBytes), review.reviewedEvidence.dryRun.sha256); assert.equal(sha256(writePreviewBytes), review.reviewedEvidence.writePreview.sha256); assert.deepEqual(dryRunBytes, writePreviewBytes, "dry-run and write-preview bytes differ"); const dryRun = JSON.parse(dryRunBytes); assert.equal(dryRun.status, "BLOCKED_OWNER_REVIEW"); assert.equal(dryRun.queueMutation, false); assert.equal(dryRun.legacyQueue.currentTask, "M16-GAP-00276"); assert.equal(dryRun.legacyQueue.parentManifest, "tests/golden/M16-GAP-00275/manifest.json"); const currentManifest = readJson(currentManifestPath); assert.equal(currentManifest.task, "M16-GAP-00276"); assert.equal(currentManifest.status, "done", "complete the focused legacy task before queue write"); assert.equal(currentManifest.nextTask, "M16-GAP-00277"); assert.match(fs.readFileSync(currentStatusPath, "utf8"), /^status:\s*done\s*$/mu); assert.ok(readJson(completionPath).includes("operator:asset.clear_single"), "completion registry is not closed"); const nextPlan = readJson(nextPlanPath); assert.equal(nextPlan.firstTask, "M16-GAP-00277"); assert.equal(nextPlan.tasks.find((entry) => entry.id === "M16-GAP-00276")?.state, "completed"); assert.equal(nextPlan.tasks.find((entry) => entry.id === "M16-GAP-00277")?.state, "active"); const taskIndex = readJson(indexPath); assert.equal(taskIndex.activeTask, "M16-GAP-00277"); const catalogRows = fs.readFileSync(catalogPath, "utf8").trimEnd().split("\n").map((line) => JSON.parse(line)); assert.equal(catalogRows.find((entry) => entry.id === "M16-GAP-00276")?.state, "completed"); assert.equal(catalogRows.find((entry) => entry.id === "M16-GAP-00277")?.state, "active"); const queueBefore = fs.readFileSync(queuePath); assert.equal(sha256(queueBefore), review.reviewedEvidence.protectedHashes["docs/EXECUTION_QUEUE.md"]); const parentManifestPath = path.join(root, "tests/golden/M16-GAP-00275/manifest.json"); assert.equal(sha256(fs.readFileSync(parentManifestPath)), review.reviewedEvidence.protectedHashes["tests/golden/M16-GAP-00275/manifest.json"]); const queueSource = queueBefore.toString("utf8"); assert.match(queueSource, /\|\s*当前任务\s*\|\s*`M16-GAP-00276`/u); assert.match(queueSource, /\|\s*parent manifest\s*\|\s*`tests\/golden\/M16-GAP-00275\/manifest\.json`/u); const queueAfter = queueSource .replace("| 当前任务 | `M16-GAP-00276`", "| 当前任务 | `M16-GAP-00277`") .replace("| parent manifest | `tests/golden/M16-GAP-00275/manifest.json`", "| parent manifest | `tests/golden/M16-GAP-00276/manifest.json`") .replace("| 任务卡 | [`tasks/M16-GAP-00276.md`](tasks/M16-GAP-00276.md)", "| 任务卡 | [`tasks/M16-GAP-00277.md`](tasks/M16-GAP-00277.md)") .replace("| 专项验收 | `npm --prefix web run test:generated-gap -- --task M16-GAP-00276`", "| 专项验收 | `npm --prefix web run test:generated-gap -- --task M16-GAP-00277`"); assert.notEqual(queueAfter, queueSource); assert.match(queueAfter, /\|\s*当前任务\s*\|\s*`M16-GAP-00277`/u); assert.match(queueAfter, /\|\s*parent manifest\s*\|\s*`tests\/golden\/M16-GAP-00276\/manifest\.json`/u); const beforePath = path.join(evidenceRoot, "queue-before.md"); const afterPath = path.join(evidenceRoot, "queue-after.md"); fs.writeFileSync(beforePath, queueBefore); fs.writeFileSync(afterPath, queueAfter); const temporary = `${queuePath}.c6-003-${process.pid}.tmp`; try { fs.writeFileSync(temporary, queueAfter, { mode: fs.statSync(queuePath).mode }); fs.renameSync(temporary, queuePath); } finally { if (fs.existsSync(temporary)) fs.rmSync(temporary); } const observedAfter = fs.readFileSync(queuePath); assert.deepEqual(observedAfter, Buffer.from(queueAfter)); const receipt = { schemaVersion: 1, task: "C6-003", operation: "CORRECTIVE_QUEUE_REACTIVATION_WRITE", status: "APPLIED", queueMutation: true, authorization: { review: relative(reviewPath), approved: true }, before: { path: relative(beforePath), sha256: sha256(queueBefore), currentTask: "M16-GAP-00276", parentManifest: "tests/golden/M16-GAP-00275/manifest.json" }, after: { path: relative(afterPath), sha256: sha256(observedAfter), currentTask: "M16-GAP-00277", parentManifest: "tests/golden/M16-GAP-00276/manifest.json" }, changedFields: ["currentTask", "parentManifest", "taskCard", "focusedCommand"], rollback: { action: "restore before.path atomically", source: relative(beforePath) }, }; writeJson(path.join(evidenceRoot, "queue-reactivation-write-receipt.json"), receipt); process.stdout.write(`queue-reactivation-ok task=C6-003 queue=M16-GAP-00277 before=${receipt.before.sha256} after=${receipt.after.sha256}\n`);