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
This commit is contained in:
Peter Steinberger 2026-10-01 14:39:47 -07:00 • committed by GitHub
parent 4418ca069b
commit 99567a65b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 33 additions and 6 deletions

View file

@ -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:

View file

@ -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,

View file

@ -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);