fix: harden upgrade publication and refresh

This commit is contained in:
golem
2026-08-25 00:16:20 -06:00
parent d29dc86383
commit eb7c2c18b3
8 changed files with 267 additions and 32 deletions
+28
View File
@@ -159,6 +159,34 @@ test("upgrade finalizer verifies receipt, event, slot, artifact-driven state and
});
});
test("upgrade finalizer retains staging after the second manifest write fails and safely converges on retry", async () => {
await withUpgradeFixture(async (root, active) => {
let writes = 0;
const writeManifest = async (path, value) => {
writes += 1;
if (writes === 2) throw new Error("injected active replacement failure");
await atomicWrite(path, `${JSON.stringify(value, null, 2)}\n`);
};
await assert.rejects(
() => finalizeUpgrade({ root, rpc: fakeUpgradeRpc(), writeManifest }),
/injected active replacement failure/,
);
assert.equal((await readJson(join(root, "deployments", "anvil.json"))).implementation, V2_IMPLEMENTATION);
assert.equal((await readJson(join(root, "deployments", "active.json"))).implementation, active.implementation);
await access(join(root, "deployments", "upgrade-pending.json"));
const recovered = await finalizeUpgrade({ root, rpc: fakeUpgradeRpc() });
assert.equal(recovered.manifest.implementation, V2_IMPLEMENTATION);
assert.deepEqual(
await readFile(join(root, "deployments", "active.json")),
await readFile(join(root, "deployments", "anvil.json")),
);
await assert.rejects(() => access(join(root, "deployments", "upgrade-pending.json")));
});
});
test("upgrade finalizer rejects proxy, deployment-block, and actor identity mutation without changing confirmed files", async () => {
for (const [name, mutate] of [
["proxy", (pending) => { pending.proxy = TOKEN; }],