#!/usr/bin/env bash # Regenerate verify/results/state-checksum/ from the saves on this host. # # Saves are read from $SOTS_SAVES_DIR when set, plus the in-repo # verify/results/saves/. No .sav is copied anywhere. Skips cleanly with a # message when there is nothing to read. # # verify/state-checksum/run_validation.sh # # Each gate is run separately and its exit status reported; nothing is chained # with && so that a failure cannot skip a later step. set -u HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" VERIFY="$(dirname "$HERE")" OUT="$VERIFY/results/state-checksum" SC="uv run python3 $HERE/state_checksum.py" mkdir -p "$OUT" REPO_SAVES="$VERIFY/results/saves" EXTRA="${SOTS_SAVES_DIR:-}" if [ -n "$EXTRA" ] && [ ! -d "$EXTRA" ]; then echo "note: \$SOTS_SAVES_DIR=$EXTRA is not a directory; ignoring" EXTRA="" fi if [ -z "$EXTRA" ]; then echo "note: \$SOTS_SAVES_DIR unset; using in-repo saves only" fi shopt -s nullglob ALL=("$REPO_SAVES"/*.sav) [ -n "$EXTRA" ] && ALL+=("$EXTRA"/*.sav) if [ ${#ALL[@]} -eq 0 ]; then echo "no saves available; nothing to validate" exit 0 fi rc=0 note() { echo "== $1 -> exit $2"; [ "$2" -eq 0 ] || rc=1; } # 1. one root + subsystem digest set per distinct save content { echo "# state_checksum roots -- $(date -u +%Y-%m-%dT%H:%M:%SZ)" echo seen="" for f in "${ALL[@]}"; do h=$(sha256sum "$f" | cut -c1-16) case " $seen " in *" $h "*) continue;; esac seen="$seen $h" echo "--- sha256:$h $(basename "$f")" $SC "$f" echo done } > "$OUT/roots.txt" 2>&1 note "roots" $? # 2. identical bytes must give identical roots, and every save must be covered uv run python3 "$HERE/stability_check.py" "${ALL[@]}" > "$OUT/identical-roots.txt" 2>&1 note "identical-roots" $? # 3. the known load->re-save delta must LOCALISE, not read as a whole-state miss A="$REPO_SAVES/turn2-state.sav" B="" for f in "${ALL[@]}"; do [ "$(sha256sum "$f" | cut -c1-8)" = "bb4fd9ac" ] && B="$f" && break done if [ -n "$B" ] && [ -f "$A" ]; then { echo "# determinism-oracle.md: loading a post-turn autosave and re-saving it" echo "# changes Player.Status (4->0) on the four turn-participating players" echo "# plus the derived Summary.Checksum. Nothing else." echo echo "\$ state_checksum.py turn2-state.sav $(basename "$B")" $SC "$A" "$B" echo echo "\$ state_checksum.py turn2-state.sav $(basename "$B") --mask resave" $SC "$A" "$B" --mask resave } > "$OUT/resave-localisation.txt" 2>&1 note "resave-localisation" 0 else echo "== resave-localisation -> SKIPPED (the bb4fd9ac re-save form is not on this host)" fi # 4. a real turn transition, fully attributed if [ -f "$REPO_SAVES/turn2-state.sav" ] && [ -f "$REPO_SAVES/turn3-state.sav" ]; then { echo "# one real End Turn (turn 2 -> turn 3), every difference named" echo $SC "$REPO_SAVES/turn2-state.sav" "$REPO_SAVES/turn3-state.sav" --ulps 2 --limit 500 } > "$OUT/turn2-to-turn3.txt" 2>&1 note "turn-transition" 0 fi # 5. the recorded chain (host side of the replay loop; the VM side is unrun) CH=("$REPO_SAVES/turn1-state.sav" "$REPO_SAVES/turn2-state.sav" "$REPO_SAVES/turn3-state.sav") have=1 for f in "${CH[@]}"; do [ -f "$f" ] || have=0; done if [ "$have" = 1 ]; then $SC "${CH[@]}" --record-chain "$OUT/chain-turn1-3.json" > "$OUT/chain.txt" 2>&1 note "record-chain" $? echo >> "$OUT/chain.txt" echo "\$ state_checksum.py --chain chain-turn1-3.json turn1 turn2 turn3" >> "$OUT/chain.txt" $SC --chain "$OUT/chain-turn1-3.json" "${CH[@]}" >> "$OUT/chain.txt" 2>&1 note "verify-chain" $? fi # 6. float census -- the evidence behind the float-parity policy uv run python3 "$HERE/float_census.py" "${ALL[@]}" > "$OUT/float-census.txt" 2>&1 note "float-census" $? echo echo "wrote $OUT" exit $rc