wait-on: delete WAIT_FROM; reused log paths are forbidden instead
Truncating writers (`>`, Unity -logFile) rewrite below any offset, so an offset turned a stale success into a silent hang. The option, the scan branch, its docs and its fixtures are gone; the header states the fresh-log-path rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
de35a72fc3
commit
d9e45c8740
3 changed files with 8 additions and 34 deletions
|
|
@ -9,7 +9,7 @@ gets a Soul pass before merge.
|
|||
| script | purpose | consumer | named by |
|
||||
|---|---|---|---|
|
||||
| `tools/stopgap/ygg-verify.sh` | Pushes an exact revision to a mirror on Yggdrasil and runs one command in a capped, niced container (`RUN_ON_HOST=1`: run a host-side command in the same slot, clone and watchdog instead; the watchdog kills every container carrying the job label). `SUMMARY=<log>` keeps the full log and prints a bounded summary (verdict, counts, ZERO TESTS RAN, failing tests); `wait <log>` blocks at most 270 s and returns the summary once; call it with a Bash tool `timeout` of at least 280000 ms. Dies with the stopgap when Idunn's verify transaction lands. | Hands, Soul, Imagination probes | `references/briefs.md` (Hands long jobs and verification, Soul long jobs), `SKILL.md` (Yggdrasil) |
|
||||
| `tools/wait-on.sh` | One general waiter for anything that is not a Yggdrasil job: `pid <pid> [<log>]`, `log <log> <done-regex>` (with `WAIT_PID=<pid>` a dead process with no match is exit 98; `WAIT_FROM=<byte offset>` ignores a reused log's stale lines; an invalid ERE is exit 3), `file <path>`. The `pid` form prints `GONE (no completion evidence)`: prefer `log` whenever the job writes a done line. Same contract as `ygg-verify.sh wait`: blocks at most 270 s, exit 0 done with a tail of at most 60 log lines, `RUNNING <n>s` exit 99, exit 98 gone with no completion evidence; call it with a Bash tool `timeout` of at least 280000 ms. Replaces hand-rolled poll loops and idle `true`/`echo`/`sleep` calls. | Hands, Soul, Imagination probes | `references/briefs.md` (Hands long jobs, Soul long jobs, detached scripts) |
|
||||
| `tools/wait-on.sh` | One general waiter for anything that is not a Yggdrasil job: `pid <pid> [<log>]`, `log <log> <done-regex>` (with `WAIT_PID=<pid>` a dead process with no match is exit 98; an invalid ERE is exit 3), `file <path>`. The `pid` form prints `GONE (no completion evidence)`: prefer `log` whenever the job writes a done line. Same contract as `ygg-verify.sh wait`: blocks at most 270 s, exit 0 done with a tail of at most 60 log lines, `RUNNING <n>s` exit 99, exit 98 gone with no completion evidence; call it with a Bash tool `timeout` of at least 280000 ms. Replaces hand-rolled poll loops and idle `true`/`echo`/`sleep` calls. | Hands, Soul, Imagination probes | `references/briefs.md` (Hands long jobs, Soul long jobs, detached scripts) |
|
||||
| `tools/mutation-table.sh` | One verify job that runs a list of hand mutants (file, before, after, test) against a revision and returns a kill table: CAUGHT with the killing test, SURVIVED, UNVIABLE, TIMEOUT, NOAPPLY, ERROR. Refuses a red or zero-test baseline and a red BUILD; rows print live. Isolation is Docker's: the baselines run once in a container that holds the revision, that container is committed to a throwaway image, and every mutant runs in a fresh `--rm` container from it, so nothing a run writes (tracked, untracked or ignored, such as `target/`) reaches the next mutant; the image is removed when the job ends, killed or not. The shared registry and sccache volumes and the network are not isolated. A file-level load failure (vitest `FAIL <file> [ <file> ]`, jest `FAIL <file>` plus `Test suite failed to run`) reads UNVIABLE. Replaces per-agent runmut/mutrun/strykrun scripts. | Soul, Hands (scoped run) | `references/briefs.md` (Hands verification, Soul reruns) |
|
||||
| `tools/testparse.sh` | Sourced library: failing test names and pass/fail/skip counts from cargo, dotnet, vitest, jest and pytest logs, and test files that failed to load (`LOADFAIL`). Shared by the summary and the kill table. | the two scripts above | not named by a brief; it lives and dies with them |
|
||||
| `tools/test-tools.sh` | Local fixture check of the parsers and the summary over `tools/fixtures/logs` (real cargo and mutation logs, plus hand-written dotnet, vitest, jest, pytest, exit-0-but-failed and no-verdict logs), and of the mutation runner's host driver and container half under a mock docker. Run it before merging a change to the scripts above. | whoever changes those scripts | `SKILL.md` (Shared tools) |
|
||||
|
|
|
|||
|
|
@ -216,18 +216,6 @@ r=$(WM=2 wo log "$T/none.log" 'x'); wchk "log missing, no pid, short cap" 99 "$r
|
|||
( sleep 2; echo ready >"$T/f.txt" ) &
|
||||
r=$(WM=10 wo file "$T/f.txt"); wchk "file appears" 0 "$r"
|
||||
: >"$T/e.txt"; r=$(WM=2 wo file "$T/e.txt"); wchk "file empty is not done" 99 "$r"
|
||||
# F1: a stale done line in a reused log is ignored from WAIT_FROM on; a new one is seen
|
||||
printf 'old run
|
||||
ALL DONE
|
||||
' >"$T/r.log"; off=$(wc -c <"$T/r.log")
|
||||
r=$(WAIT_FROM=$off WM=2 wo log "$T/r.log" 'ALL DONE'); wchk "stale done before WAIT_FROM ignored" 99 "$r"
|
||||
printf 'new run
|
||||
ALL DONE
|
||||
' >>"$T/r.log"
|
||||
r=$(WAIT_FROM=$off WM=4 wo log "$T/r.log" 'ALL DONE'); wchk "new done after WAIT_FROM seen" 0 "$r"
|
||||
printf 'short
|
||||
' >"$T/r2.log"
|
||||
r=$(WAIT_FROM=9999 WM=2 wo log "$T/r2.log" 'short'); wchk "truncated log searched whole" 0 "$r"
|
||||
# F2: the pid form reports GONE, never anything that reads as success
|
||||
sleep 1 & gp=$!; wait $gp
|
||||
r=$(WM=4 wo pid $gp); wchk "pid gone" 0 "$r"
|
||||
|
|
|
|||
|
|
@ -20,20 +20,17 @@
|
|||
# never "succeeded". Prefer `log <log> <regex>` with WAIT_PID whenever the job
|
||||
# writes a done line.
|
||||
#
|
||||
# Reused log paths: `log` accepts WAIT_FROM=<byte offset>; only bytes after it are
|
||||
# searched, so a stale done line from an earlier job on the same path is ignored.
|
||||
# Capture it at launch: `WAIT_FROM=$(wc -c < "$log" 2>/dev/null || echo 0)`. If the
|
||||
# file is now shorter than WAIT_FROM (truncated and rewritten) the whole file is
|
||||
# searched. Reading only from the offset also keeps each 2 s check cheap on a
|
||||
# multi-GB log. Without WAIT_FROM the whole log is searched.
|
||||
# Log paths: every job gets a fresh log path, never one a previous job wrote. The
|
||||
# waiter greps the whole file, so an old done line reads as success at once. An
|
||||
# offset cannot fix it: truncating writers (`>`, Unity -logFile) rewrite below it.
|
||||
#
|
||||
# Known limitations (Soul pass 1, PR #7; not fixed):
|
||||
# F5 WAIT_PID naming a launcher that exits while its child keeps writing gives a
|
||||
# false 98; pass the pid that writes the log, or none.
|
||||
# F6 `file` is done at the first byte; a writer that is still writing is not
|
||||
# waited out. Have the job write the file last (rename into place).
|
||||
# F7 `pid <pid> <log>` prints a tail only; without WAIT_FROM the `log` form
|
||||
# greps the whole log each 2 s, which is slow on multi-GB logs.
|
||||
# F7 `pid <pid> <log>` prints a tail only; the `log` form greps the
|
||||
# whole log each 2 s, which is slow on multi-GB logs.
|
||||
#
|
||||
# PIDs: on Git Bash a Windows PID (PowerShell Start-Process .Id, a task manager
|
||||
# number) is seen by `tasklist`, but `kill -0` does not know it; Git Bash's own
|
||||
|
|
@ -54,17 +51,6 @@ alive() {
|
|||
esac
|
||||
return 1
|
||||
}
|
||||
# scan <log> <re>: does the log (from WAIT_FROM, when set and not past EOF) match?
|
||||
scan() {
|
||||
[ -f "$1" ] || return 1
|
||||
local from=${WAIT_FROM:-0}
|
||||
case "$from" in ''|*[!0-9]*) from=0 ;; esac
|
||||
if [ "$from" -gt 0 ] && [ "$(wc -c <"$1")" -ge "$from" ]; then
|
||||
tail -c +"$((from + 1))" "$1" 2>/dev/null | grep -Eq -- "$2"
|
||||
else
|
||||
grep -Eq -- "$2" "$1" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
tail60() { [ -n "${1:-}" ] && [ -f "$1" ] && tail -n 60 "$1" | cut -c1-400; return 0; }
|
||||
running() { echo "RUNNING $(( $(date +%s) - t0 ))s ($1)"; exit 99; }
|
||||
elapsed() { echo $(( $(date +%s) - t0 )); }
|
||||
|
|
@ -83,10 +69,10 @@ case "$mode" in
|
|||
rc=0; printf '' | grep -Eq -- "$re" 2>/dev/null || rc=$?
|
||||
[ "$rc" -le 1 ] || { echo "wait-on: usage - /$re/ is not a valid extended regex" >&2; exit 3; }
|
||||
while :; do
|
||||
if scan "$log" "$re"; then tail60 "$log"; exit 0; fi
|
||||
if [ -f "$log" ] && grep -Eq -- "$re" "$log" 2>/dev/null; then tail60 "$log"; exit 0; fi
|
||||
if [ -n "$wp" ] && ! alive "$wp"; then
|
||||
sleep 1
|
||||
if scan "$log" "$re"; then tail60 "$log"; exit 0; fi
|
||||
if [ -f "$log" ] && grep -Eq -- "$re" "$log" 2>/dev/null; then tail60 "$log"; exit 0; fi
|
||||
echo "wait-on: DEAD - pid $wp is gone and /$re/ never matched in $log"; tail60 "$log"; exit 98
|
||||
fi
|
||||
if [ -z "$wp" ] && [ ! -f "$log" ] && [ "$(elapsed)" -ge 30 ]; then
|
||||
|
|
|
|||
Loading…
Reference in a new issue