diff --git a/deployment/receipts/HOLOLAKE-DEVELOPMENT-AUTO-FINALIZER-20260805.json b/deployment/receipts/HOLOLAKE-DEVELOPMENT-AUTO-FINALIZER-20260805.json index d9c2b47..66d9e28 100644 --- a/deployment/receipts/HOLOLAKE-DEVELOPMENT-AUTO-FINALIZER-20260805.json +++ b/deployment/receipts/HOLOLAKE-DEVELOPMENT-AUTO-FINALIZER-20260805.json @@ -30,8 +30,8 @@ "any failed gate leaves the development lane unfinished" ], "source_sha256": { - "finalizer": "a1f51515cc3c175f83a487f499ddd26ec13aec17d063d4416efe714780a02517", - "tests": "39e70be546c54c1e315bd5d8447947b79fb1a0b67416cc2acd0f0a7027075bd0" + "finalizer": "7216ec32244d4b5c6956908b8f72a6c3cd207cddcc4f527ca90272e9de1a672d", + "tests": "9e9297d2ec0b72aadd54c0781d537d986afd8d384ef739b1aac9c1714dfb2f57" }, "runtime_sha256": { "bootstrap": "cbf5be03e5156a11f3ae11d0baf2f9daa2c8b4e58c8a405de22bcadff024c282", @@ -39,7 +39,7 @@ "publish_policy": "10bc53c439479c67f5b59685a29e221e0db2baab48ceb26c8369ca75c1427330" }, "verification": { - "finalizer_node_tests": "PASS_100_9_OF_9", + "finalizer_node_tests": "PASS_100_10_OF_10", "continuity_guard_self_test": "PASS_100", "source_and_runtime_syntax": "PASS_100", "environment_audit": { diff --git a/server-tools/lake-lamp-continuity-guard/finalize-development.mjs b/server-tools/lake-lamp-continuity-guard/finalize-development.mjs index a8ed24f..e72176b 100644 --- a/server-tools/lake-lamp-continuity-guard/finalize-development.mjs +++ b/server-tools/lake-lamp-continuity-guard/finalize-development.mjs @@ -138,18 +138,23 @@ export function isJsonRecordName(name) { } function run(file, args, options = {}) { - return execFileSync(file, args, { + const output = execFileSync(file, args, { cwd: options.cwd, encoding: "utf8", stdio: options.stdio || ["ignore", "pipe", "pipe"], maxBuffer: 16 * 1024 * 1024, - }).trim(); + }); + return options.trim === false ? output : output.trim(); } function git(worktree, args) { return run("/usr/bin/git", ["-C", worktree, ...args]); } +function gitRaw(worktree, args) { + return run("/usr/bin/git", ["-C", worktree, ...args], { trim: false }); +} + function guard(guardScript, args) { const output = run(process.execPath, [guardScript, ...args]); return output ? JSON.parse(output) : null; @@ -159,7 +164,7 @@ function readJson(filePath) { return JSON.parse(fs.readFileSync(filePath, "utf8")); } -function sha256(value) { +export function sha256(value) { return crypto.createHash("sha256").update(value).digest("hex"); } @@ -214,7 +219,7 @@ function verifyWorktree(worktree, receipts) { const branch = normalizeBranch(git(root, ["branch", "--show-current"])); const receiptEvidence = receipts.map((receipt) => { const relative = normalizeReceipt(receipt); - const content = git(root, ["show", `HEAD:${relative}`]); + const content = gitRaw(root, ["show", `HEAD:${relative}`]); if (!content) throw new Error(`RECEIPT_NOT_COMMITTED:${relative}`); return { path: relative, @@ -321,7 +326,7 @@ function pushAndReadBack(worktree, repository, branch, head, receipts) { if (cloneHead !== head) throw new Error(`FRESH_CLONE_HEAD_MISMATCH:${cloneHead}`); git(clone, ["fsck", "--full"]); for (const receipt of receipts) { - const remoteContent = git(clone, ["show", `HEAD:${receipt.path}`]); + const remoteContent = gitRaw(clone, ["show", `HEAD:${receipt.path}`]); if (sha256(Buffer.from(remoteContent)) !== receipt.sha256) { throw new Error(`REMOTE_RECEIPT_MISMATCH:${receipt.path}`); } diff --git a/server-tools/lake-lamp-continuity-guard/finalize-development.test.mjs b/server-tools/lake-lamp-continuity-guard/finalize-development.test.mjs index 484995f..0e67632 100644 --- a/server-tools/lake-lamp-continuity-guard/finalize-development.test.mjs +++ b/server-tools/lake-lamp-continuity-guard/finalize-development.test.mjs @@ -11,6 +11,7 @@ import { parseArgs, safeCacheRelative, selfTest, + sha256, } from "./finalize-development.mjs"; test("the finalizer accepts only the registered Fifth Domain code channel", () => { @@ -83,3 +84,7 @@ test("macOS AppleDouble sidecars are never parsed as JSON records", () => { assert.equal(isJsonRecordName("._lease.json"), false); assert.equal(isJsonRecordName("lease.txt"), false); }); + +test("receipt hashes preserve exact trailing bytes", () => { + assert.notEqual(sha256(Buffer.from("{}")), sha256(Buffer.from("{}\n"))); +});