docs(wake): bound the fd-9 lock claim at detector.sh:534 — refused for one interval, not forever
ci/woodpecker/pr/ci Pipeline was successful

rev-974's round-3 CHANGES-REQUIRED (comment 20040) is correct. The sentence I
wrote to make the fix honest over-claimed in the opposite direction:

  "...and no replacement instance can ever acquire it."

That is false. The orphaned sleep holds the last copy of fd 9 only until it
exits, which is bounded by one poll interval (WAKE_DETECTOR_INTERVAL, default
30s at :510-513). After it exits the fd closes and the next start succeeds.
pepper's M3 measured exactly this boundary: refused, then granted.

Corrected to state the bound, and sharpened on one point rev-974 did not have
to spell out: the lock is taken with `flock -n` (:503), so a replacement is
REFUSED AND EXITS rather than queueing. The operational cost is therefore a
restart window in which every supervisor retry fails outright — which is a real
harm and a sufficient reason for the fix, without needing the false claim that
the lock is permanently unreclaimable. A justification that overstates the
failure it prevents invites the reader to discount the fix when they discover
the overstatement.

The neighbouring sentence — "`9>&-` closes ONLY the child's copy; the parent's
lock is unaffected" — was confirmed accurate by three instruments and is kept
VERBATIM.

MECHANICAL PROOF, comment-only (same instrument as the parent proof, so the
numbers are directly comparable):

  noncomment lines   397 -> 397          equal
  stripped sha256    d6ec8993 -> d6ec8993 IDENTICAL
  file lines         576 -> 582          +7/-1, all comment

The stripped SHA is the SAME VALUE recorded in the parent proof, so executable
content is unchanged not merely against the previous commit but against the
content already verified at da0cf001.

VERIFICATION: framework/tools/wake/test-wake-detector.sh rc=0, all 13 invariant
groups, including D4 "single-instance flock (2nd instance refuses)" — the
invariant the corrected wording describes.

NOTE ON THE VERDICT: this commit VOIDS rev-974's verdict at da0cf001 by the
standing verdict-at-head rule. Re-verdict required at the new head before merge;
I am not treating the prior mechanical proof as carrying forward.

Refs #993, #966
This commit is contained in:
Jason Woltje
2026-07-31 07:39:50 -05:00
parent da0cf00189
commit b648079fad
@@ -531,7 +531,13 @@ cmd_run() {
[ "$once" -eq 1 ] && break
# Close the detector lock fd in the sleep child; otherwise an orphaned sleep
# keeps the single-instance flock (fd 9, taken at exec 9> above) alive after
# the detector parent dies, and no replacement instance can ever acquire it.
# the detector parent dies. The lock is non-blocking (`flock -n`, above), so
# for as long as that sleep survives a replacement instance is REFUSED and
# exits rather than queueing. The window is BOUNDED by one poll interval
# (WAKE_DETECTOR_INTERVAL, default 30s): when the orphaned sleep exits the
# last copy of the fd closes and the next start succeeds. The cost is a
# restart window in which every supervisor retry fails — not a lock that
# nothing can ever reclaim.
# `9>&-` closes ONLY the child's copy — the parent's lock is unaffected.
sleep "$interval" 9>&-
done