From 022d50325a013b8dbe6b0d5220ef5e806caeaa96 Mon Sep 17 00:00:00 2001 From: Drew T <50529377+Druthulu@users.noreply.github.com> Date: Mon, 31 Aug 2026 13:51:48 -0600 Subject: [PATCH] docs(playbook): the pgrep bracket is NOT enough when launch and wait share a shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Measured a SECOND time in S67, and the first fix was incomplete. A waiter using the bracketed pattern still matched itself and spun 1h35m, because the same shell command had LAUNCHED the job — so its own command line carried the unbracketed 'gate_stage.py --binary ov_SC07_007' from the nohup half. The regex gate_[s]tage.py does not match the literal bracketed text, but it happily matches the plain text sitting earlier on the same line. Rule is now: launch and wait in SEPARATE shell invocations, or better, wait on a completion MARKER the job writes to its own log rather than on process liveness. --- docs/wave-playbook.md | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/docs/wave-playbook.md b/docs/wave-playbook.md index 4e7e2218c..3901d2be7 100644 --- a/docs/wave-playbook.md +++ b/docs/wave-playbook.md @@ -170,15 +170,34 @@ Stale is worse than absent. Write it for a session that has none of your context ## Waiting on background work — one trap that costs 40 minutes +`pgrep -f` matches against **every process's full command line, including the waiter's own.** + ``` -until ! pgrep -f "parallel_[g]ate.py" >/dev/null; do sleep 20; done # RIGHT -until ! pgrep -f "parallel_gate.py" >/dev/null; do sleep 20; done # WRONG — matches itself +until ! pgrep -f "parallel_gate.py" >/dev/null; do sleep 20; done # WRONG — matches itself +until ! pgrep -f "parallel_[g]ate.py" >/dev/null; do sleep 20; done # better, but NOT sufficient ``` -`pgrep -f` matches the waiter's OWN command line. In S67 a waiter spun for 40 minutes waiting for -itself and the gate never started. **The tell: an empty log plus zero `ps` hits means NEVER STARTED, -not "buffered".** The bracket makes the pattern match the target but not the literal text in the -waiter. Same hazard, from the other side, killed two lane helpers in S60. +**THE BRACKET IS NOT ENOUGH IF YOU LAUNCH AND WAIT IN ONE SHELL.** Measured twice in S67: + +1. A waiter using the bare pattern matched its own shell and spun **40 minutes** while + `parallel_gate` never started. +2. A waiter using the *bracketed* pattern ALSO spun — for **1 h 35 m** — because the same shell + command had launched the job, so its command line contained the UNBRACKETED text too: + `nohup … tools/gate_stage.py --binary ov_SC07_007 … ; until ! pgrep -f "gate_[s]tage.py --binary ov_SC07_007"` + The regex `gate_[s]tage.py` does not match the literal `gate_[s]tage.py`, but it matches the + `gate_stage.py` sitting in the launch half of the very same line. + +**THE RULE: launch and wait in SEPARATE shell invocations.** Launch in one call, return, then wait +in another whose command line never names the target unbracketed. Better still, wait on a +CONDITION the job itself produces — a completion marker in its log — rather than on process +liveness: + +``` +until grep -q "R22 rc=" .run/.log 2>/dev/null; do sleep 30; done +``` + +**The tell for both failures: an empty log plus zero `ps` hits means NEVER STARTED, not "buffered".** +Same hazard, from the other side, killed two lane helpers in S60. Any long-running tool you write must **stream** its progress (R55). `gate_wave.py` initially captured both lanes and printed at the end, leaving a zero-byte log for the whole run — indistinguishable from