carlos / platform Public

zdd plan 3 final fix: WIP mutate.sh integrity and scratch isolation (F3/F4, partial, untested; fixer cut off by a usage limit)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 31bf28b3529b983b93d129252d9f06bbf96b7885 parent a6ba2a0
1 file changed, +45 −22
  • internal/activator/testdata/swapsim/mutate.sh +45 −22
diff --git a/internal/activator/testdata/swapsim/mutate.sh b/internal/activator/testdata/swapsim/mutate.sh
index 0506a68a9..5011d2e10 100755
--- a/internal/activator/testdata/swapsim/mutate.sh
+++ b/internal/activator/testdata/swapsim/mutate.sh
@@ -21,19 +21,26 @@
# after three failing seeds, so K is at most 3), "liveness only at K
# seed(s)", "passed", "TIMEOUT", "PANIC", or "NOT RUN" (no PASS or FAIL
# line for TestSwapInvariants).
-# witness: FAIL (an assertion failed), PASS, TIMEOUT or PANIC.
+# witness: FAIL (an assertion failed), PASS, TIMEOUT, PANIC or NOT RUN.
+# Execution integrity is recorded apart from detection. Every run the
+# mutant needs (the sweep and each designated witness) must build, start,
+# and end in its own PASS or FAIL line. A run that panicked, timed out, never
+# ran, or did not build fails the mutant's gate even when another run caught
+# the mutation; that kill is still printed, as a diagnostic.
# Verdicts:
-# CAUGHT a designated witness failed by assertion, or the sweep
-# reported an I1-I3 violation
-# TIMEOUT not caught, and a run timed out
-# PANIC not caught, and a run panicked
+# CAUGHT every run executed intact, and a designated witness failed by
+# assertion or the sweep reported an I1-I3 violation
+# BROKEN a run panicked, timed out or never ran (the detail names it);
+# "caught" in the detail is the diagnostic kill, not a pass
# LIVENESS not caught; the sweep's only failure is a Swap that never
# returned
# MISSED everything passed under the mutation
# ERROR the marker is not on exactly one line, the substitution did
-# not apply, or the mutant did not build
-# Exit 1 on anything but CAUGHT. Scratch: $TMPDIR/zdd3-mut. The tree copy is
-# removed on exit; the logs stay in $TMPDIR/zdd3-mut/logs. Remove them after.
+# not apply, or the mutant did not build or set up
+# Exit 1 on anything but CAUGHT. Scratch: a fresh directory per run, made
+# with mktemp -d under $TMPDIR (named zdd3-mut.XXXXXX) and printed first.
+# Its tree copy is removed on exit; its logs stay. Remove that directory
+# after. The script never touches any other path under $TMPDIR.
set -u
if [ "$(nice)" -lt 19 ] && [ -z "${SWAPSIM_RENICED:-}" ]; then
SWAPSIM_RENICED=1 exec nice -n 19 "$0" "$@"
@@ -46,13 +53,15 @@ shift $(($# > 0 ? 1 : 0))
ONLY=" $* "
export GOMAXPROCS=2
ROOT=$(git -C "$(dirname "$0")" rev-parse --show-toplevel) || exit 2
-SCRATCH=${TMPDIR:?}/zdd3-mut
+# This run's own scratch: never a fixed name, so two runs sharing TMPDIR
+# cannot delete each other's trees or logs.
+SCRATCH=$(mktemp -d "${TMPDIR:?}/zdd3-mut.XXXXXX") || exit 2
+echo "scratch: $SCRATCH"
A=internal/activator
# The default build cache is read-only inside a sandbox.
if [ -z "${GOCACHE:-}" ] && [ ! -w "$(go env GOCACHE)" ]; then
export GOCACHE=$TMPDIR/gocache
fi
-rm -rf "$SCRATCH"
mkdir -p "$SCRATCH/tree" "$SCRATCH/logs" "$SCRATCH/gowork" || exit 2
trap 'rm -rf "$SCRATCH/tree" "$SCRATCH/gowork"' EXIT
export GOTMPDIR=$SCRATCH/gowork
@@ -110,14 +119,22 @@ while IFS='|' read -r name file marker witnesses sub; do
continue
fi
- # The sweep, on its own.
+ # The sweep, on its own. Its integrity is judged apart from what it
+ # found: a crash or a missing result line breaks the run even when the
+ # log also reports a violation.
slog="$SCRATCH/logs/$name.sweep.log"
runtest "$slog" 20m '^TestSwapInvariants$' SWAP_SIM_SEEDS="$SEEDS"
built=1
grep -q '\[build failed\]\|\[setup failed\]' "$slog" && built=0
inv=$(grep -c 'invariant violated at seed' "$slog")
live=$(grep -c 'liveness failed at seed' "$slog")
+ broken=""
scrash=$(crashed "$slog")
+ if [ -n "$scrash" ]; then
+ broken="$broken sweep=$scrash"
+ elif ! grep -qE -- '--- (PASS|FAIL): TestSwapInvariants' "$slog"; then
+ broken="$broken sweep=NOT-RUN"
+ fi
if [ "$inv" -gt 0 ]; then
sweep="violated at $inv seed(s)"
elif [ -n "$scrash" ]; then
@@ -130,10 +147,10 @@ while IFS='|' read -r name file marker witnesses sub; do
sweep=passed
fi
- # Each designated witness, on its own.
+ # Each designated witness, on its own. A witness that crashed or never
+ # reported is broken even if its log also holds a FAIL line.
wres=""
wcaught=0
- crash=$scrash
for w in ${witnesses//,/ }; do
wlog="$SCRATCH/logs/$name.$w.log"
runtest "$wlog" 5m "^$w\$"
@@ -141,10 +158,7 @@ while IFS='|' read -r name file marker witnesses sub; do
wcrash=$(crashed "$wlog")
if [ -n "$wcrash" ]; then
r=$wcrash
- # A timeout outranks a panic in the verdict.
- if [ "$wcrash" = TIMEOUT ] || [ -z "$crash" ]; then
- crash=$wcrash
- fi
+ broken="$broken $w=$wcrash"
elif grep -q -- "--- FAIL: $w " "$wlog"; then
r=FAIL
wcaught=1
@@ -152,20 +166,29 @@ while IFS='|' read -r name file marker witnesses sub; do
r=PASS
else
r="NOT RUN"
+ broken="$broken $w=NOT-RUN"
fi
wres="$wres $w=$r"
done
cp "$ROOT/$A/$file" "$src"
+ caught=0
+ if [ $wcaught -eq 1 ] || [ "$inv" -gt 0 ]; then
+ caught=1
+ fi
detail="sweep $sweep; witnesses${wres}"
if [ $built -eq 0 ]; then
- echo "ERROR $name: the mutant did not build; see $SCRATCH/logs/$name.*"
+ echo "ERROR $name: the mutant did not build or set up; see $SCRATCH/logs/$name.*"
fail=1
- elif [ $wcaught -eq 1 ] || [ "$inv" -gt 0 ]; then
- echo "CAUGHT $name: $detail"
- elif [ -n "$crash" ]; then
- printf '%-9s %s: %s; see %s\n' "$crash" "$name" "$detail" "$SCRATCH/logs/$name.*"
+ elif [ -n "$broken" ]; then
+ kill="not caught"
+ if [ $caught -eq 1 ]; then
+ kill="caught (diagnostic only)"
+ fi
+ printf 'BROKEN %s: execution integrity failed:%s; %s; %s; see %s\n' "$name" "$broken" "$kill" "$detail" "$SCRATCH/logs/$name.*"
fail=1
+ elif [ $caught -eq 1 ]; then
+ echo "CAUGHT $name: $detail"
elif [ "$live" -gt 0 ]; then
echo "LIVENESS $name: $detail"
fail=1