Review on #8442 found the guard itself re-introduced the corruption it exists to prevent, plus one rollout gap. All were right. - A pre-lock server writes no server.lock, only server-runtime.json with its live pid, so the file was blind to the running 0.0.34 the upgrade swaps out. Read that as a held lock and refuse the same way before anything binds, or the auto-update incident still happens once on upgrade day. - Between one starter reading a stale lock and recreating it, the unconditional unlink removed the successor's fresh claim: two starters after a crash each unlinked the other's lock and both proceeded. Reclaim now refreshes the dead file's mtime across several observation rounds and only removes what stays untouched, and the live holder's own heartbeat refreshes inside one round, so a live claim can never be reclaimed from under it. - recordServerLockPort rewrote the lock in place, truncating first; a reader in that window decoded an empty holder and reclaimed a live lock. The update now goes through write-temp-then-rename, and release never removes a lock it cannot decode. - Lock-create errors stopped being coerced into "taken": a permission or disk failure used to surface as "another server is running". Only AlreadyExists reads as contention now, and the exhaustion error keeps the observation count instead of fixed prose. Verified: apps/server suite 246 files passed / 2 skipped, 2829 tests passed / 10 skipped (parent: 245 files, 2815 passed). Typecheck exit 0. The new tests also pin the pre-fix behaviour as failing, not just the new behaviour as passing.
NoahLinckeScout committed on 9/14/2026, 8:27:20 PMparent53dd74b
2 files changedLine totals unavailable