From 99567a65b7c5912d27339a699bc3d315a113d254 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 1 Oct 2026 14:39:47 -0700 Subject: [PATCH] fix(e2e): restore generic survivor candidate identity checks (#162976) The upgrade-survivor candidate identity check surfaced a raw node:fs read error instead of the identity messages when the candidate payload was missing or unreadable (test/scripts/upgrade-survivor-candidate-identity red on main since #160448 narrowed the contract). The generic baseline contract is restored (the captured baseline stays mandatory for cron-owner-doctor; generic scenarios use baselineCommit), and both filesystem error boundaries now produce the contextual identity error with the original cause retained. Refs #160448 --- docs/help/testing-updates-plugins.md | 3 +++ .../upgrade-survivor/worker-cell-package.mjs | 22 ++++++++++++++----- ...pgrade-survivor-candidate-identity.test.ts | 14 ++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/docs/help/testing-updates-plugins.md b/docs/help/testing-updates-plugins.md index 3ad4ad48932f..470d5020151e 100644 --- a/docs/help/testing-updates-plugins.md +++ b/docs/help/testing-updates-plugins.md @@ -215,6 +215,9 @@ commit before the update and compare the installed application payload with the frozen tarball afterward, before candidate probes. This distinguishes different builds with the same version string. npm still owns dependency reification; manual tarball runs without a selected source SHA retain their existing contract. +These generic scenarios do not require a worker-cell baseline identity artifact. +After the update, missing or unreadable tarballs and installed payloads fail with +the corresponding candidate identity diagnostic before any candidate probes run. Useful published-upgrade survivor variants: diff --git a/scripts/e2e/lib/upgrade-survivor/worker-cell-package.mjs b/scripts/e2e/lib/upgrade-survivor/worker-cell-package.mjs index a9a4df38aad5..c66f9e6e5f14 100644 --- a/scripts/e2e/lib/upgrade-survivor/worker-cell-package.mjs +++ b/scripts/e2e/lib/upgrade-survivor/worker-cell-package.mjs @@ -215,12 +215,22 @@ async function main() { writeJson(path.join(artifacts, "candidate-package-identity.json"), expected); } else if (mode === "installed") { const expected = readJson(path.join(artifacts, "candidate-package-identity.json")); - assert.equal( - hash(fs.readFileSync(candidateTarball)), - expected.sha256, - "Candidate tarball changed", - ); - const actual = readWorkerCellPackageIdentity(packageRoot); + let tarballBytes; + try { + tarballBytes = fs.readFileSync(candidateTarball); + } catch (cause) { + throw new Error("Candidate tarball changed: cannot read the frozen tarball", { cause }); + } + assert.equal(hash(tarballBytes), expected.sha256, "Candidate tarball changed"); + let actual; + try { + actual = readWorkerCellPackageIdentity(packageRoot); + } catch (cause) { + throw new Error( + "Installed application payload differs from the frozen tarball: cannot read the installed package", + { cause }, + ); + } assertWorkerCellPackageIdentity(actual, { version: expected.version, buildInfo: expected.buildInfo, diff --git a/test/scripts/upgrade-survivor-candidate-identity.test.ts b/test/scripts/upgrade-survivor-candidate-identity.test.ts index b162cf7618ea..33a6eb19a25a 100644 --- a/test/scripts/upgrade-survivor-candidate-identity.test.ts +++ b/test/scripts/upgrade-survivor-candidate-identity.test.ts @@ -12,6 +12,8 @@ type Change = | "valid" | "stale" | "tarball-changed" + | "tarball-missing" + | "entrypoint-missing" | "wrong-source" | "published" | "missing-baseline"; @@ -110,6 +112,12 @@ update_candidate_for_install_mode() { if [ "$UNIT_CHANGE" = tarball-changed ]; then printf '\\n' >> "$CANDIDATE_SPEC" fi + if [ "$UNIT_CHANGE" = tarball-missing ]; then + rm "$CANDIDATE_SPEC" + fi + if [ "$UNIT_CHANGE" = entrypoint-missing ]; then + rm "$UNIT_ROOT/installed/openclaw.mjs" + fi } phase() { shift @@ -180,6 +188,12 @@ describe.skipIf(process.platform === "win32")( events: [], }, { change: "tarball-changed", error: "Candidate tarball changed", events: ["updater"] }, + { change: "tarball-missing", error: "Candidate tarball changed", events: ["updater"] }, + { + change: "entrypoint-missing", + error: "Installed application payload differs from the frozen tarball", + events: ["updater"], + }, ] as const)("refuses $change at the actual candidate boundary", ({ change, error, events }) => { const observed = runCandidateFlow("base", change); expect(observed.result.status).not.toBe(0);