[object Object]

← back to Ticket System

fix(codex-yoloforever): self-heal stale cycle.lock from dead owner pid

d29c0f721d5d2775123b63645e545514774614d9 · 2026-09-14 01:14:13 -0700 · Steve Abrams

A cycle killed by reboot/SIGKILL skips its EXIT trap, leaving cycle.lock/
orphaned. The overlap branch only tested lock-dir existence, never owner-pid
liveness, so every subsequent launchd invocation hit 'overlap prevented' and
returned 0 doing nothing — bricking the loop until a manual unlock (TK-11171;
also the cause of the Sep 3 'cleared inactive lock'). Now the overlap branch
checks kill -0 on the owner: alive => prevent (unchanged); dead/absent =>
reclaim the lock and re-acquire. Proven in the wild: reclaimed real stale lock
pid=55031 and started a real cycle (pid 67025).

TK-11171
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnGVNUxnQfxpU1vfZmZFv3

Files touched

Diff

commit d29c0f721d5d2775123b63645e545514774614d9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Sep 14 01:14:13 2026 -0700

    fix(codex-yoloforever): self-heal stale cycle.lock from dead owner pid
    
    A cycle killed by reboot/SIGKILL skips its EXIT trap, leaving cycle.lock/
    orphaned. The overlap branch only tested lock-dir existence, never owner-pid
    liveness, so every subsequent launchd invocation hit 'overlap prevented' and
    returned 0 doing nothing — bricking the loop until a manual unlock (TK-11171;
    also the cause of the Sep 3 'cleared inactive lock'). Now the overlap branch
    checks kill -0 on the owner: alive => prevent (unchanged); dead/absent =>
    reclaim the lock and re-acquire. Proven in the wild: reclaimed real stale lock
    pid=55031 and started a real cycle (pid 67025).
    
    TK-11171
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01SnGVNUxnQfxpU1vfZmZFv3
---
 scripts/codex-yoloforever.sh | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/scripts/codex-yoloforever.sh b/scripts/codex-yoloforever.sh
index c9ffb3da..f37c0eaf 100755
--- a/scripts/codex-yoloforever.sh
+++ b/scripts/codex-yoloforever.sh
@@ -68,8 +68,19 @@ run_cycle() {
 
   if ! mkdir "$LOCK_DIR" 2>/dev/null; then
     local old_pid="$(cat "$LOCK_DIR/pid" 2>/dev/null || true)"
-    print "$(date -u +%FT%TZ) overlap prevented; lock owner pid=${old_pid:-unknown}" >> "$LOG_FILE"
-    return 0
+    if [[ -n "$old_pid" ]] && kill -0 "$old_pid" 2>/dev/null; then
+      print "$(date -u +%FT%TZ) overlap prevented; lock owner pid=${old_pid}" >> "$LOG_FILE"
+      return 0
+    fi
+    # Stale lock: owner pid is dead/absent (e.g. a reboot/SIGKILL skipped the EXIT
+    # trap that would have rmdir'd the lock). Reclaim instead of bricking the loop.
+    print "$(date -u +%FT%TZ) stale lock reclaimed; dead owner pid=${old_pid:-unknown}" >> "$LOG_FILE"
+    rm -f "$LOCK_DIR/pid" "$LOCK_DIR/started-at" 2>/dev/null || true
+    rmdir "$LOCK_DIR" 2>/dev/null || true
+    if ! mkdir "$LOCK_DIR" 2>/dev/null; then
+      print "$(date -u +%FT%TZ) overlap prevented; lock reacquire race, owner pid=$(cat "$LOCK_DIR/pid" 2>/dev/null || echo unknown)" >> "$LOG_FILE"
+      return 0
+    fi
   fi
 
   print $$ > "$LOCK_DIR/pid"

← a2d1a0c1 TK-11678: regression test proving TK-11660's claim guard clo  ·  back to Ticket System  ·  auto-data-snapshot: 2026-09-14T01:27:28 (4 data files) — dat 59fda0c5 →