import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const recovery = fs.readFileSync(path.join(repoRoot, "docs/web/RELEASE_RECOVERY.md"), "utf8"); const notes = fs.readFileSync(path.join(repoRoot, "docs/web/RELEASE_NOTES.md"), "utf8"); const app = fs.readFileSync(path.join(repoRoot, "web/app/src/app/App.tsx"), "utf8"); const deployment = fs.readFileSync(path.join(repoRoot, "docs/web/DEPLOYMENT.md"), "utf8"); assert.ok(notes.includes("[Release recovery](RELEASE_RECOVERY.md)"), "release notes do not link release recovery"); for (const required of [ "sha256sum --check SHA256SUMS.txt", "sha256sum --check RC_MANIFEST.json.sha256", "test:rc-manifest", "test:binary-archive", "test:source-archive", "check-ci-report.mjs", "Save Project", "blender-web.blend", "SceneIR revision", "readlink current", "failure before the switch", "failure after the switch", "INDEXEDDB_SCHEMA_DOWNGRADE_UNSUPPORTED", "OPFS_REVERSE_MIGRATION_UNDECLARED", "Recover Project", "Open .blend", "test:operations-rehearsal", ]) { assert.ok(recovery.toLowerCase().includes(required.toLowerCase()), `release recovery omits: ${required}`); } assert.match(app, /anchor\.download = "blender-web\.blend"/); assert.match(app, /await storage\.saveProject\(projectIdRef\.current, revision, data\.slice\(0\)\)/); assert.match(deployment, /Rollback changes only the `current` app\/engine release pointer/); assert.match(deployment, /in-place rollback is\s+`BLOCKED`/); for (const link of ["DEPLOYMENT.md"]) assert.ok(recovery.includes(`](${link})`)); process.stdout.write("rc-release-recovery-ok checksums=2 archives=2 schemaBlocks=2 projectBackup=downloaded-blend\n");