From 7c421832b2594f6932aa35869f083e1843c0fab4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 19:13:43 +0000 Subject: [PATCH] fix(pm): serve a verify-lock queue whose holds outlast one foreground turn The wait budget cannot be raised: it is spent inside one foreground agent turn, which the harness kills at ~600s, so a larger number buys an unreportable SIGTERM rather than a longer wait. What spans several turns is the slot, and two things stopped it working: - its ageing bound ran from the ORIGINAL arrival, so a caller queued behind two ~19-minute holds lost its place for the offence of the queue being long. The age now runs from when the place was last put down, which is the question the bound was always asking. - it was named only in the exit-99 message, one whole budget after the place it would have kept was lost. A queueing caller is now told at second zero. Exclusion, FIFO order, the cap and the budget are unchanged. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69 --- scripts/pm/os-verify-lock.sh | 276 ++++++++++++++++++++++++++++++++--- 1 file changed, 254 insertions(+), 22 deletions(-) diff --git a/scripts/pm/os-verify-lock.sh b/scripts/pm/os-verify-lock.sh index 23ca638a3c..dc5fe5ea90 100755 --- a/scripts/pm/os-verify-lock.sh +++ b/scripts/pm/os-verify-lock.sh @@ -243,6 +243,24 @@ set -uo pipefail # single foreground agent call (harness ceiling: 10 minutes). Everything above # this is unrepresentable through this entry point — that is the point of the # entry point, so it is a constant and not an option. +# +# ⛔ THIS IS NOT THE NUMBER TO RAISE, AND THE REASON IS ARITHMETIC RATHER THAN +# TASTE. It was filed as one (#12538): holds on this lock were measured at 1164s +# while the budget is 540s, so "raise the budget above the p95 legitimate hold" +# reads as the obvious repair. It cannot be done. The budget is spent INSIDE one +# foreground agent turn, and that turn is killed at ~600s by a ceiling nothing in +# this file can move. A budget of, say, 1200s does not buy 1200s of waiting; it +# buys a SIGTERM at 600s — no VERDICT line, no ledger record, no NOT MEASURED +# note, the caller left with the one outcome this whole file exists to prevent +# (see the bash 3.2 block above: 20 seconds of spinning and ZERO verdicts). The +# cap at 540s is what keeps the refusal REPORTABLE, with ~60s of headroom for the +# verdict and the ledger append. Raising it converts an honest exit 99 into an +# unreportable exit 143. +# +# So a wait longer than one turn is not a longer BUDGET. It is a place kept +# ACROSS turns — the slot mechanism further down, whose ageing bound is the +# number that actually has to cover a long hold. The card's arithmetic is real; +# it just indicts SLOT_MAX_AGE_S, not this constant. readonly HARD_CAP_S=540 readonly DEFAULT_WAIT_S=540 @@ -275,10 +293,12 @@ HOLDER_FILE="${LOCK_FILE}.holder" LEDGER_FILE="${OS_VERIFY_LOCK_LEDGER:-${LOCK_FILE}.ledger}" readonly TICKET_MAX_AGE_S=$((HARD_CAP_S + 300)) -# How long a PARKED slot keeps the arrival stamp it is holding a place with. -# Three budgets: long enough that an agent doing lock-free work between -# attempts still finds its place, short enough that the priority a slot carries -# over later arrivals is bounded and declared rather than indefinite. +# How long a PARKED slot keeps the arrival stamp it is holding a place with, +# measured from when it was LAST RELINQUISHED rather than from the original +# arrival — see the ageing paragraph in the slots block below for why the clock +# moved. Long enough that an agent doing lock-free work between attempts still +# finds its place; short enough that a place nobody is coming back for stops +# carrying priority over later arrivals. readonly SLOT_MAX_AGE_S=$((HARD_CAP_S * 3)) # Past this the ledger stops growing. A record is ~150 bytes, so this is tens @@ -448,9 +468,18 @@ queue_usable() { # ticket file: "