The M1/M2 lock fix (4903ee04) was asymmetric: acquireStateLock got a 60s deadman ceiling (recovers a lock once its age crosses an absolute bound ABOVE the wait budget) but withPlanningLock did not. The .lock body carries no startTime, so _planningHolderVerifiedLive can only check pid liveness — it cannot detect pid reuse. A false-alive holder (original holder crashed, pid recycled by an unrelated live process) would therefore make withPlanningLock throw on every call with no self-heal until the reused pid happens to die. Mirror acquireStateLock: in the EEXIST branch, steal a verified-live holder anyway once the lock ages past deadmanCeilingMs (60000 > lockTimeout 10000). mtime age is measured from lock creation, so a stuck lock self-heals on a subsequent call. Adds a clock+probe-seam regression test (false-alive holder past the ceiling IS stolen). planning-workspace 13/13, clock-seam 34/34, locking suites green; lint clean. Claude-Session: https://claude.ai/code/session_01R88n7Q54bAaVHFkDbbH1yz
13 KiB
13 KiB