#!/usr/bin/env bash # revision-preflight.sh — prove the copy a contract is about to execute is the # copy that is merged. # # Usage: # revision-preflight.sh [options] # # Options: # --ref Ref to compare against (default: origin/master) # --no-fetch Do not refresh the ref first (see FRESHNESS below) # --quiet Print nothing on success # -h, --help Show this help # # Exit codes: # 0 the executing script byte-matches : # 1 MISMATCH, or the revision could not be resolved (see FAIL CLOSED) # # FRESHNESS # A guard is only as good as the ref it compares against. On 2026-09-25 a # stale local origin/master made an ancestry check on this fleet report # "unlanded work" for a branch that had in fact merged, and it would equally # have passed a stale script as current. So by default this guard FETCHES the # remote before comparing. With --no-fetch it compares against whatever the # local ref points at and says so out loud; it never silently assumes # freshness. # # FAIL CLOSED # An unresolvable path or ref is a FAILURE, never a warning. "Cannot verify" # is precisely the state a stale or hand-edited copy produces, so treating it # as success would defeat the guard. The original draft of this script did # exactly that: it resolved the master revision with # `git show origin/master:$(basename "$SCRIPT")`, which drops the scripts/ # prefix, queries the repo root, fails, and exited 0 — passing a script that # exists in no revision at all. # # WHICH CLONE # Pass the clone the contract is actually executing from. See # docs/contract-execution-pinning.md for which clone each contract pins. set -euo pipefail REF="origin/master" FETCH=1 QUIET=0 usage() { sed -n '2,45p' "$0" | sed 's/^# \{0,1\}//' } while [[ $# -gt 0 ]]; do case "$1" in --ref) [[ $# -ge 2 ]] || { echo "revision-preflight: --ref needs a value" >&2; exit 1; } REF="$2"; shift 2 ;; --no-fetch) FETCH=0; shift ;; --quiet) QUIET=1; shift ;; -h|--help) usage; exit 0 ;; --) shift; break ;; -*) echo "revision-preflight: unknown option: $1" >&2; exit 1 ;; *) break ;; esac done if [[ $# -lt 2 ]]; then usage >&2 exit 1 fi SCRIPT="$1" CLONE="$2" say() { [[ $QUIET -eq 1 ]] || echo "$@" >&2; } fail() { echo "❌ revision-preflight: $*" >&2; exit 1; } # ── 1. inputs must exist ────────────────────────────────────────────────────── [[ -f "$SCRIPT" ]] || fail "executing script not found: $SCRIPT" [[ -d "$CLONE" ]] || fail "clone path is not a directory: $CLONE" git -C "$CLONE" rev-parse --git-dir >/dev/null 2>&1 \ || fail "not a git clone: $CLONE" # ── 2. resolve the repo-relative path (the original defect) ─────────────────── CLONE_ABS=$(cd "$CLONE" && pwd) SCRIPT_ABS=$(cd "$(dirname "$SCRIPT")" && pwd)/$(basename "$SCRIPT") case "$SCRIPT_ABS" in "$CLONE_ABS"/*) REL="${SCRIPT_ABS#"$CLONE_ABS"/}" ;; *) fail "script is outside the clone: $SCRIPT_ABS is not under $CLONE_ABS" ;; esac # ── 3. refresh the ref so staleness cannot mask a stale script ──────────────── if [[ $FETCH -eq 1 ]]; then REMOTE="${REF%%/*}" [[ "$REMOTE" == "$REF" ]] && REMOTE="origin" if ! git -C "$CLONE" fetch --quiet "$REMOTE" 2>/dev/null; then fail "cannot fetch '$REMOTE' in $CLONE — refusing to verify against a possibly stale '$REF'. Re-run with network access, or pass --no-fetch to compare against the local ref deliberately." fi else say "⚠️ revision-preflight: --no-fetch — comparing against the LOCAL '$REF'; freshness is assumed, not verified" fi # ── 4. resolve the merged revision; unresolvable is a failure ──────────────── git -C "$CLONE" rev-parse --verify --quiet "$REF" >/dev/null \ || fail "ref '$REF' does not resolve in $CLONE" REF_COMMIT=$(git -C "$CLONE" rev-parse --short "$REF") TMPFILE=$(mktemp) trap 'rm -f "$TMPFILE"' EXIT if ! git -C "$CLONE" show "$REF:$REL" > "$TMPFILE" 2>/dev/null; then fail "'$REL' does not exist in $REF ($REF_COMMIT) — cannot verify $SCRIPT. A path that is absent from $REF can never be a merged copy." fi # ── 5. compare ─────────────────────────────────────────────────────────────── EXEC_SHA=$(sha256sum "$SCRIPT" | cut -d' ' -f1) MERGED_SHA=$(sha256sum "$TMPFILE" | cut -d' ' -f1) if [[ "$EXEC_SHA" != "$MERGED_SHA" ]]; then { echo "❌ revision-preflight: MISMATCH — refusing to report from this copy" echo " script: $SCRIPT_ABS" echo " clone: $CLONE_ABS" echo " executed: $EXEC_SHA" echo " merged: $MERGED_SHA ($REF:$REL @ $REF_COMMIT)" } >&2 exit 1 fi say "✅ revision-preflight: $REL matches $REF @ $REF_COMMIT ($EXEC_SHA)" exit 0