#!/usr/bin/env bash # Run a game-binary command under a freshly created isolated HOME so it can # never touch the real save, then prove the real save directory is untouched. # # Motivation (2026-07-11): `HOME=x printf ... | ./binary` scopes the override # to printf, not the binary — a mis-scoped observed run truncated a real # playthrough save in place. This wrapper exports the isolated HOME for the # child process itself and fails loudly if the real # `dirs::data_dir()/misaligned/` changed in any way. # # Usage: # tools/observed-run.sh [args...] # Example: # printf 'wait 1\nquit\n' | tools/observed-run.sh ./target/debug/misaligned --agent --seed 1 # # The sandbox HOME is retained after the run (its path is printed) so the # sandboxed save can be inspected as evidence. set -euo pipefail if [ "$#" -lt 1 ]; then echo "usage: tools/observed-run.sh [args...]" >&2 exit 2 fi real_home="$HOME" case "$(uname -s)" in Darwin) real_data_dir="$real_home/Library/Application Support/misaligned" ;; *) real_data_dir="${XDG_DATA_HOME:-$real_home/.local/share}/misaligned" ;; esac # One line per entry under the real save directory: path, size/mtime, and a # content hash for files. Directory mtimes are included so a file created and # deleted during the run still trips the comparison. Empty if the directory # does not exist. snapshot() { [ -e "$real_data_dir" ] || return 0 find "$real_data_dir" -print | LC_ALL=C sort | while IFS= read -r entry; do meta=$(stat -f '%z %m' "$entry" 2>/dev/null || stat -c '%s %Y' "$entry") if [ -f "$entry" ]; then hash=$(shasum -a 256 "$entry" | awk '{print $1}') else hash=dir fi printf '%s|%s|%s\n' "$entry" "$meta" "$hash" done } before=$(snapshot) sandbox=$(mktemp -d "${TMPDIR:-/tmp}/misaligned-observed-run.XXXXXX") echo "observed-run: sandbox HOME: $sandbox" >&2 status=0 HOME="$sandbox" XDG_DATA_HOME="$sandbox/.local/share" "$@" || status=$? after=$(snapshot) if [ "$before" != "$after" ]; then echo "observed-run: FAIL — the real save directory changed: $real_data_dir" >&2 diff <(printf '%s\n' "$before") <(printf '%s\n' "$after") >&2 || true echo "observed-run: sandbox retained at $sandbox" >&2 exit 1 fi echo "observed-run: real save directory untouched: $real_data_dir" >&2 echo "observed-run: sandbox retained at $sandbox" >&2 exit "$status"