#!/bin/bash
# =============================================================================
# provision-rig.sh — THE SEPARATE MUTATING PHASE (doyle W4C2QFLE)
#
# WHAT IT IS FOR. The LAN-bootstrap set gate refuses a fresh home BEFORE any firewall
# admission is attempted: set_gate (lanhost.rs:184-213) wants a staged SET, verified under
# VerifyPolicy::production, whose host-triple artifact_sha256 equals the running daemon's own
# executable, at a version the home records as Applied. On failure start_at_inner returns
# LanRefused (serveverb.rs:260) and report_lan_admission — the only thing that reaches
# request_lan_firewall -> bootstrap_firewall::reconcile — is never called (:247). So a D2 run
# against an empty home can never create the pair it exists to measure.
#
# THIS SCRIPT MAKES ALL FOUR CONJUNCTS TRUE HONESTLY. It removes, relaxes and reorders NOTHING:
# no product edit, no gate bypass, no test seam on a production path. The home simply carries
# what the gate asks for, and the product verifies those bytes itself, twice — at apply and
# again at the gate.
#
# RULED APPROACH B (doyle W4C2QFLE): the exact repaired artifact under isolated, rig-local
# debug trust. VerifyPolicy::production loads identity_dir()/release-keys.json
# (release.rs:359-364) and identity_dir() is spt_home()/identity (perch.rs:245) — HOME-SCOPED —
# and load_from starts from BUILTIN_RELEASE_KEYS with FILE KEYS ADDING to it (release.rs:377+).
# So this home, and only this home, trusts one extra rig-scoped release key. The fleet home
# cannot pick it up: that is a property of the product, not a convention anyone must remember.
#
# WHAT IT IS NOT. It is not preparation. It MUTATES: it creates a home, places staged bytes,
# runs the product's own apply and may leave a daemon running. It therefore has its OWN grant
# boundary, and the D2 driver NEVER calls it — fp-driver-d2-r2.sh only VERIFIES and CONSUMES
# what this produced (S0b), because a run that provisioned its own subject could not tell a
# prepared home from one it repaired to make a check pass.
#
# WHAT IT NEVER DOES.
#   - It never writes the applied record. The Applied state must be GENUINE: the product's own
#     apply produces it, and this script observes the outcome and refuses if it did not land.
#   - It never applies FROM the preserved worktree subject or the fleet installation. `update
#     apply` replaces the executable it is running, so either would be overwritten by the very
#     step that makes the gate passable. The rig executable is a COPY, and the copy is what
#     makes the apply disposable.
#   - It never clears, reuses or repairs a destination. Exclusive creation, and a failed
#     attempt is PRESERVED — a failed attempt is often the only evidence that a defect was real.
#   - It never hand-writes the applied record, and it does not use the maintainer verb that
#     would ('xtask debug-mark-applied', which records Applied for a hand-staged set). doyle
#     ruled OCTAXWYQ: a supported command that WRITES Applied still does not demonstrate the
#     transition this rig exists to exercise. The genuine apply/promotion stays.
#
# THE ONE CLAIM THIS REVISION WITHDRAWS. The predecessor said it never handles a signing
# secret, because it consumed an already-signed set. The supported staging verb SIGNS: xtask's
# debug_signing_identity() reads SPT_DEBUG_RELEASE_SEED from the environment. So the seed must
# be present in THIS script's environment, and that is stated here rather than hidden. What the
# script does with it is bounded and measurable:
#   - the seed is read ONCE at preflight into a NON-EXPORTED shell variable and IMMEDIATELY
#     unset from the environment, so no child inherits it by default (doyle OCTAXWYQ);
#   - IT IS NEVER EXPORTED, not even briefly. doyle WD47ADZV: a temporary export in this shell
#     would hand the seed to every child launched during that interval, the timeout and
#     registration helpers included. It is passed as a COMMAND-PREFIX ASSIGNMENT on the signing
#     command, which places it in the environment of that command alone;
#   - the FLOOR, stated because it cannot be lowered: xtask reads its seed FROM THE ENVIRONMENT,
#     so the signing process must have it, and the timeout that bounds that process is its
#     parent and carries it for the same interval. Two processes, both of them the signing call
#     itself. No other command is launched in that interval;
#   - it is NEVER an argument: an argument is readable from the process table by every other
#     process on the box. It is never printed, never written to evidence, never archived;
#   - the retained variable is unset immediately after the signing call, and the ABSENCE of both
#     is asserted before the apply. That assertion is a floor, not a proof of earlier
#     containment (doyle WD47ADZV): it shows nothing from the apply onward can inherit the seed,
#     and says nothing about the interval above it. The containment above it is structural — no
#     export ever happened — not measured here.
#
# NOTHING IS RUN BY AUTHORING THIS FILE. Every pin below is empty and every empty pin is a
# refusal to start.
# =============================================================================
set -u

# ---- pins (all empty: an empty pin is a refusal, never a default) ------------
RIG_ROOT=''            # the run-specific root to CREATE. Must not exist. Its parent must.
SUBJECT_EXE=''         # the artifact to copy in — the exact repaired binary
SUBJECT_SHA=''         # its pinned sha256; a copy that does not hash to this is another subject
# SET_FILE and SET_SHA ARE GONE. On the supported route no signed set pre-exists as a file to
# copy: 'xtask debug-rollout' MINTS and signs one from the artifact it is given. There is
# nothing to hash and nothing to place, so a pin for it would be a pin for a file this script
# never sees.
SET_VERSION=''         # --version for the staging verb. PINNED rather than left to the verb's
                       # own monotonic sequence, so the staged version is stated here and a
                       # mismatch downstream is a refusal rather than a discovery
XTASK_BIN=''           # the PREBUILT maintainer xtask executable that stages. A prebuilt binary
                       # and not 'cargo run -p xtask' on purpose: a cargo invocation inside
                       # provisioning would build into some tree's target/ and take a build-cache
                       # pool (AGENTS.md, releases#103) as a side effect of provisioning
XTASK_SHA=''           # its pinned sha256. These bytes decide what gets staged and signed, so
                       # they are pinned like every other subject in this run
HOST_TRIPLE=''         # e.g. x86_64-pc-windows-msvc — the TARGET the staging verb is given as
                       # --artifact <triple>=<path>, and the triple the set gate looks its own
                       # platform up by. It is no longer a filename this script spells: the
                       # product's own staging chooses the on-disk name
TRUST_KEY_ID=''        # the rig-scoped release key id
TRUST_KEY_HEX=''       # its PUBLIC verifying key hex. A private key here is a refusal (below).
# A CONFIGURED EXCLUSION, resolved by doyle 23YE7ZXG — and explicitly NOT a fresh canonical
# measurement of this box's install location. It is here so the fleet binary can be refused BY
# NAME; if the real install moves, this pin is stale and the refusal it powers is weaker, so it
# is re-resolved rather than trusted on age.
FLEET_EXE='C:/Users/decid/AppData/Local/spt-core/bin/spt.exe'
WORKTREE_EXE=''        # the preserved worktree subject's path, likewise
STAGE_BUDGET_S=120     # the two maintainer verbs' bound. No cargo runs inside them (XTASK_BIN
                       # is prebuilt), so this is a tool invocation, not a build
APPLY_BUDGET_S=300     # the apply/promotion lifecycle's bound
KILL_GRACE_S=5         # timeout -k escalation; every bound subtracts it, as in the driver
POLL_S=5

STATE=''               # set as each phase completes; written to rollout-state.json
say() { echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $*"; }
refuse() { # WHY — a refusal to start is not a failed run
  say "PROVISION_REFUSED: $1"
  [ -n "$RIG_ROOT" ] && [ -d "$RIG_ROOT" ] && rollout_state "REFUSED" "$1"
  exit 3
}
fail() { # WHY — something ran and did not land. NOTHING IS CLEANED UP.
  say "PROVISION_FAILED: $1"
  rollout_state "FAILED" "$1"
  say "  the rig is LEFT EXACTLY AS IT IS at $RIG_ROOT. It is not cleared, not repaired and not"
  say "  reused: provision a new one. What is here is this attempt's evidence."
  exit 4
}
# THE RIG'S OWN SEQUENCE STATE, AT ITS OWN PATH (doyle W4C2QFLE). It is NOT the product's
# applied record and must never be read as one: this says which phase of provisioning was
# reached, and applied-state.json says what the product did.
rollout_state() { # PHASE DETAIL
  [ -n "$RIG_ROOT" ] || return 0
  printf '{"rig_id":"%s","phase":"%s","detail":"%s","utc":"%s"}\n' \
    "${RIG_ID:-unset}" "$1" "$2" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
    > "$RIG_ROOT/rollout-state.json" 2>/dev/null
}
# One bound, one shape, same reasoning as the driver's bounded(): the escalation interval is
# INSIDE the deadline, and an expiry is not a proof that a native Windows child stopped.
bounded() { # LABEL DEADLINE OUT ERR -- CMD...
  local label="$1" dl="$2" out="$3" err="$4"; shift 4
  [ "$1" = '--' ] && shift || { say "HARNESS_BUG bounded $label without --"; return 90; }
  local left; left=$(( dl - $(date +%s) - KILL_GRACE_S ))
  if [ "$left" -lt 1 ]; then
    say "BOUND_NOT_STARTED $label — nothing remains after the ${KILL_GRACE_S}s escalation reserve"
    return 125
  fi
  say "BOUND $label ${left}s"
  timeout -k "$KILL_GRACE_S" "$left" "$@" > "$out" 2> "$err"
  local rc=$?
  [ "$rc" -eq 124 ] || [ "$rc" -eq 137 ] && say "BOUND_EXPIRED $label (killer rc=$rc) — the WAIT ended; what stopped is a separate question this script does not answer by assumption"
  return $rc
}
canon() { # PATH -> a comparable spelling, or empty
  ( cd "$(dirname "$1")" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$(basename "$1")" ) | tr 'A-Z' 'a-z' | tr '\\' '/'
}

# ---- 0. every pin, before anything is created -------------------------------
for p in RIG_ROOT SUBJECT_EXE SUBJECT_SHA SET_VERSION XTASK_BIN XTASK_SHA HOST_TRIPLE \
         TRUST_KEY_ID TRUST_KEY_HEX FLEET_EXE WORKTREE_EXE; do
  eval "v=\$$p"
  [ -n "$v" ] || refuse "$p is unpinned. An empty pin is this script stating it has no authorized input, not a default to fill in."
done
[ -f "$SUBJECT_EXE" ] || refuse "the subject $SUBJECT_EXE does not exist"
[ -f "$XTASK_BIN" ]   || refuse "the staging tool $XTASK_BIN does not exist"
# A PRIVATE KEY IS NEVER AN INPUT HERE. The public verifying key is 64 hex characters; anything
# else, and anything that names a secret, is refused before it can be written anywhere.
case "$TRUST_KEY_HEX" in
  *[!0-9a-fA-F]* | '') refuse "TRUST_KEY_HEX is not hex" ;;
esac
[ "${#TRUST_KEY_HEX}" -eq 64 ] || refuse "TRUST_KEY_HEX is ${#TRUST_KEY_HEX} characters; a verifying key is 64. A signing secret is NOT an input to this script and must never reach this file, a transcript, the repository or an archive."
# THE SIGNING SEED: read once, then removed from the environment. Everything after this line
# runs WITHOUT it, and the staging call re-supplies it for its own subprocess only.
SIGN_SEED="${SPT_DEBUG_RELEASE_SEED:-}"
unset SPT_DEBUG_RELEASE_SEED
[ -n "$SIGN_SEED" ] || refuse "SPT_DEBUG_RELEASE_SEED is not in the environment. The supported staging verb signs, so provisioning cannot stage without it. It is an ENVIRONMENT input on purpose: a pin would put a secret in a file, and an argument would put it in the process table."
case "$SIGN_SEED" in
  *[!0-9a-fA-F]*) refuse "SPT_DEBUG_RELEASE_SEED is not hex. Its VALUE is not reported here, and no refusal in this script ever prints it." ;;
esac
[ "${#SIGN_SEED}" -eq 64 ] || refuse "SPT_DEBUG_RELEASE_SEED is ${#SIGN_SEED} characters; the seed xtask derives an identity from is 32 bytes of hex. The value is not reported."
say "SIGNING_SEED present in the environment, 64 hex characters, VALUE NOT REPORTED. It is now unset from this script's environment and held in a NON-EXPORTED variable; it is never exported, and reaches only the signing command's own environment."
for a in "$@"; do
  [ "$a" = "$SIGN_SEED" ] && refuse "a secret value was passed as an argument to this script. An argument is readable from the process table by every other process on this box."
done
s=$(sha256sum "$SUBJECT_EXE" | cut -d' ' -f1)
[ "$s" = "$SUBJECT_SHA" ] || refuse "the subject hashes to $s and the pin says $SUBJECT_SHA: different bytes are a different subject"
s=$(sha256sum "$XTASK_BIN" | cut -d' ' -f1)
[ "$s" = "$XTASK_SHA" ] || refuse "the staging tool hashes to $s and the pin says $XTASK_SHA: different bytes are a different tool, and this tool signs"
case "$SET_VERSION" in
  '' | *[!0-9]*) refuse "SET_VERSION is not a plain integer. The staging verb's --version is a u64 counter, and a version this script cannot state as a number it cannot pin." ;;
esac

# ---- 1. exclusive creation (doyle W4C2QFLE: the RIG enforces this, not the product)
# mkdir either creates or fails because it exists. A test-then-create leaves a window in which
# two attempts both read 'absent', and 'refuse an existing destination' is the standing rule.
[ -d "$(dirname "$RIG_ROOT")" ] || refuse "the rig root's parent does not exist: $(dirname "$RIG_ROOT")"
mkdir "$RIG_ROOT" 2>/dev/null || refuse "$RIG_ROOT already exists. It is NOT cleared and NOT reused: its contents are some attempt's evidence. Provision a new root."
RIG_ID="rig-$(date -u +%Y%m%dT%H%M%SZ)-$$"
rollout_state CREATED "$RIG_ROOT"
say "RIG_CREATED $RIG_ROOT id=$RIG_ID"
# ONLY what this script itself owns. identity/ and releases/artifacts/ are NOT pre-created:
# the supported verbs create their own (debug-pin does create_dir_all on identity, and
# stage_update_set on releases/artifacts), so leaving them absent keeps 'the verb made this'
# observable instead of pre-staging a shape that would look the same either way.
mkdir -p "$RIG_ROOT/bin" "$RIG_ROOT/home" "$RIG_ROOT/evidence" \
  || fail "could not create the rig layout"
H="$RIG_ROOT/home"
RIG_EXE="$RIG_ROOT/bin/spt.exe"
EVID="$RIG_ROOT/evidence"

# ---- 2. the executable COPY, and the two paths that must never be applied from
cp -p "$SUBJECT_EXE" "$RIG_EXE" || fail "could not copy the subject into the rig"
s=$(sha256sum "$RIG_EXE" | cut -d' ' -f1)
[ "$s" = "$SUBJECT_SHA" ] || fail "the copy hashes to $s, not $SUBJECT_SHA"
RIG_CANON=$(canon "$RIG_EXE"); fc=$(canon "$FLEET_EXE"); wc_=$(canon "$WORKTREE_EXE")
[ -n "$RIG_CANON" ] || fail "could not canonicalise the rig executable path"
[ "$RIG_CANON" = "$fc" ]  && fail "the rig executable resolves to the FLEET installation; apply replaces the executable it runs and would overwrite it"
[ "$RIG_CANON" = "$wc_" ] && fail "the rig executable resolves to the preserved worktree subject; apply would overwrite the evidence this run is about"
say "RIG_EXE $RIG_EXE sha256=$s (a copy; the worktree subject and the fleet install are untouched and are never applied from)"
rollout_state EXE_PLACED "$s"

# ---- 3. the home's trust root, written by the SUPPORTED VERB -----------------
# 'xtask debug-pin --home' is the documented maintainer route (docs/DEBUG-ROLLOUT.md, one-time
# lab setup). It ADDS the key to identity/release-keys.json, leaves any existing keys alone,
# writes an empty revoked list only when none exists, and sets the home's channel to debug.
# A revoked entry would void a builtin key id and that IS a weakening; the verb adds.
# The predecessor hand-wrote this file. It no longer does: a second convention for a file the
# product's own tooling already writes is exactly the drift doyle rejected.
TRUST_FILE="$H/identity/release-keys.json"
[ -e "$TRUST_FILE" ] && fail "a trust root already exists at $TRUST_FILE inside a root this script just created exclusively; it is not overwritten"
STAGE_DEADLINE=$(( $(date +%s) + STAGE_BUDGET_S ))
bounded debug_pin "$STAGE_DEADLINE" "$EVID/debug-pin.out" "$EVID/debug-pin.err" -- \
  env SPT_HOME="$H" "$XTASK_BIN" debug-pin --home "$H" --key-id "$TRUST_KEY_ID" --public-key "$TRUST_KEY_HEX"
prc=$?
say "DEBUG_PIN native_exit=$prc (preserved verbatim in evidence/debug-pin.out and debug-pin.err)"
[ "$prc" -eq 0 ] || fail "debug-pin did not return zero (exit $prc). Nothing is hand-written in its place."
[ -f "$TRUST_FILE" ] || fail "debug-pin returned zero and $TRUST_FILE does not exist: the verb's exit is not the state of the evidence"
grep -q "$TRUST_KEY_ID" "$TRUST_FILE" || fail "the trust root does not name key_id=$TRUST_KEY_ID"
cp -p "$TRUST_FILE" "$EVID/release-keys.json"
say "TRUST_ROOT $TRUST_FILE names key_id=$TRUST_KEY_ID, written by debug-pin, and revokes nothing (builtin keys keep their standing)"
rollout_state TRUST_WRITTEN "$TRUST_KEY_ID"

# ---- 4. STAGING, THROUGH THE SUPPORTED MAINTAINER VERB ----------------------
# THE CORRECTION (doyle 23YE7ZXG). The predecessor claimed the product has no local-stage verb
# and placed release.json + artifacts/<triple>.bin into the release cache's layout itself. That
# claim was WRONG and the placement is gone. 'spt update' — the PUBLIC surface — indeed has no
# local-stage subcommand, but the MAINTAINER surface does: 'xtask debug-rollout --stage-dir'
# (docs/DEBUG-ROLLOUT.md), which signs a set over the artifact it is given, verifies it, and
# stages it through ReleaseCache::stage_update_set. That function writes exactly what this
# script used to write by hand — the signed record, artifacts/<platform>.bin, and the removal
# of a stale single-artifact stamp — atomically, and it is the product's own code.
#
# So there is no cache-layout coupling in this script any more. It does not know the file names.
#
# WHAT THIS CALL DOES THAT THE PLACEMENT DID NOT: it SIGNS. The seed is exported for this one
# subprocess and unset on the next line; it is never an argument, and --key-id names the key
# only. Everything after this line runs without the seed in its environment, and that absence
# is asserted rather than assumed.
#
# --artifact pins the host triple to the RIG's copy, so the signed digest is the copy's digest.
# --state keeps the verb's debug sequence inside the rig: its default is a repo target/ path,
# and provisioning has no business writing into a build tree.
# --build-current is deliberately NOT passed: it would run cargo and take a build-cache pool.
# THE SIGNING CALL IS NOT ROUTED THROUGH bounded(). bounded() is a shell function, and a
# variable assignment prefixed to a function call lives in THIS shell's environment for the
# duration of the call — which is the temporary export doyle WD47ADZV ruled out. The bound is
# therefore taken by hand, in bounded()'s own shape and arithmetic, so that the assignment can
# be prefixed to the SIMPLE COMMAND and reach nothing else.
STAGE_DEADLINE=$(( $(date +%s) + STAGE_BUDGET_S ))
stage_left=$(( STAGE_DEADLINE - $(date +%s) - KILL_GRACE_S ))
if [ "$stage_left" -lt 1 ]; then
  say "BOUND_NOT_STARTED debug_rollout — nothing remains after the ${KILL_GRACE_S}s escalation reserve"
  fail "the staging bound had no room left to start in. Nothing was signed and nothing was staged."
fi
say "BOUND debug_rollout ${stage_left}s"
SPT_DEBUG_RELEASE_SEED="$SIGN_SEED" SPT_HOME="$H" \
  timeout -k "$KILL_GRACE_S" "$stage_left" \
    "$XTASK_BIN" debug-rollout \
      --stage-dir "$H/releases" \
      --state "$RIG_ROOT/debug-rollout-state.json" \
      --version "$SET_VERSION" \
      --key-id "$TRUST_KEY_ID" \
      --artifact "$HOST_TRIPLE=$RIG_EXE" \
    > "$EVID/stage.out" 2> "$EVID/stage.err"
src=$?
unset SIGN_SEED
[ "$src" -eq 124 ] || [ "$src" -eq 137 ] && say "BOUND_EXPIRED debug_rollout (killer rc=$src) — the WAIT ended; what stopped is a separate question this script does not answer by assumption"
say "DEBUG_ROLLOUT native_exit=$src (preserved verbatim in evidence/stage.out and stage.err)"
rollout_state STAGE_RETURNED "$src"
[ "$src" -eq 0 ] || fail "the staging verb did not return zero (exit $src). Nothing is placed by hand in its place: a hand placement is the convention this revision exists to remove."
# THE EXIT IS NOT THE STATE OF THE EVIDENCE. The staged set is observed, by the verb's own
# stdout and on disk, and the version it reports is compared with the pin.
grep -q "DEBUG_ROLLOUT_STAGED version=$SET_VERSION" "$EVID/stage.out" \
  || fail "the staging verb returned zero and its output does not report version=$SET_VERSION: [$(tr -d '\r' < "$EVID/stage.out" | tail -3 | tr '\n' ' ')]"
[ -d "$H/releases" ] || fail "the staging verb returned zero and $H/releases does not exist"
( cd "$H/releases" && find . -type f -print0 | xargs -0 sha256sum ) > "$EVID/staged-files.sha256" 2>/dev/null \
  || fail "could not record what was staged"
[ -s "$EVID/staged-files.sha256" ] || fail "nothing was staged under $H/releases: an empty listing is not a staged set"
say "STAGED version=$SET_VERSION by debug-rollout; every staged file is hashed in evidence/staged-files.sha256. This script named no cache file and asserts nothing about the bytes' validity: the product re-verifies them at apply and again at the set gate, under this home's own policy."
rollout_state STAGED "$SET_VERSION"

# ---- 5. the apply/promotion lifecycle, BOUNDED and OBSERVED, never forced ----
# apply is not one step: it records AppliedPending, swaps the executable, and promotion to
# Applied happens when the next generation comes up healthy (applyhost.rs:280,
# brainproc.rs:702), with RolledBack the other outcome. set_gate refuses AppliedPending
# explicitly, so 'it applied' is not the question — 'did it PROMOTE' is.
# doyle OCTAXWYQ / WD47ADZV: the seed reaches the signing command's environment and nothing
# else. This pair of checks is a FLOOR, not a proof of earlier containment — it establishes that
# nothing from the apply onward can inherit the seed, and says nothing about the interval above
# it. That interval is contained STRUCTURALLY, by never exporting at all, which is a property of
# the code above rather than a reading taken here.
[ -z "${SPT_DEBUG_RELEASE_SEED:-}" ] || fail "the signing seed is in the environment at the apply. It must not be inherited by apply, daemon or capture processes."
[ -z "${SIGN_SEED:-}" ] || fail "the signing seed is still held in a shell variable at the apply"
say "SEED_SCOPE_FLOOR verified: neither the environment variable nor the retained variable exists before the apply, so no process from here on can inherit the seed. This is a floor: it does not measure the interval above it, which is contained by never exporting rather than by this reading."
APPLY_DEADLINE=$(( $(date +%s) + APPLY_BUDGET_S ))
bounded update_apply "$APPLY_DEADLINE" "$EVID/apply.out" "$EVID/apply.err" -- \
  env SPT_HOME="$H" "$RIG_EXE" update apply
arc=$?
say "UPDATE_APPLY native_exit=$arc (preserved verbatim in evidence/apply.out and apply.err)"
rollout_state APPLY_RETURNED "$arc"
[ "$arc" -eq 0 ] || fail "the apply did not return zero (exit $arc). Nothing is retried and nothing is hand-written: an applied record written to make a gate pass is the class of workaround that manufactures a green."

# THE PROMOTION IS OBSERVED. The product's own applied-state record is READ — never written —
# and polled to the deadline. Two channels, as the driver does with the setup's exit: the
# apply's output is the primary evidence and this record corroborates it; a disagreement is
# REPORTED, never resolved by preference.
APPLIED_STATE="$H/releases/applied-state.json"
while [ "$(date +%s)" -lt "$APPLY_DEADLINE" ]; do
  if [ -f "$APPLIED_STATE" ] && grep -q '"Applied"' "$APPLIED_STATE" 2>/dev/null; then break; fi
  sleep "$POLL_S"
done
[ -f "$APPLIED_STATE" ] || fail "no applied-state record exists after the apply: the promotion cannot be shown to have happened, and this script will not write one"
cp -p "$APPLIED_STATE" "$EVID/applied-state.json"
cat "$EVID/apply.out" "$EVID/apply.err" "$EVID/applied-state.json" > "$EVID/applied-evidence.txt" 2>/dev/null
grep -q '"Applied"' "$APPLIED_STATE" || fail "the applied state is [$(cat "$APPLIED_STATE")] — AppliedPending is a trial in flight and RolledBack ran a different binary; set_gate refuses both BY NAME (lanhost.rs:206-211). PENDING is the honest outcome here."
grep -q "$SET_VERSION" "$APPLIED_STATE" || fail "the applied state does not name version $SET_VERSION"
say "APPLIED_GENUINE version=$SET_VERSION — produced by the product's own apply, observed here, written by nothing in this script"
rollout_state APPLIED "$SET_VERSION"

# ---- 6. what provisioning verifies: BYTES, and the ACTUAL supervisor/brain paths
# (doyle W4C2QFLE: binder equality is a POST-SETUP check and is NOT here — there is no
# listener yet, so there is nothing to compare a binder against.)
s=$(sha256sum "$RIG_EXE" | cut -d' ' -f1)
[ "$s" = "$SUBJECT_SHA" ] || fail "AFTER the apply the rig executable hashes to $s, not $SUBJECT_SHA. The swap put different bytes at the path this run is about."
say "BYTES_VERIFIED post-apply sha256=$s"
# The processes, if the apply left any. NOT started here, and an absent daemon is recorded as
# absent rather than started to make the check answerable.
PIDF="$H/daemon.pid"
if [ -f "$PIDF" ]; then
  sp=$(tr -d ' \r\n' < "$PIDF")
  SPT_D2_P="$sp" timeout -k 2 20 pwsh -NoProfile -Command '
    $p = [int]$env:SPT_D2_P
    try { $all = @(Get-CimInstance Win32_Process -ErrorAction Stop) } catch { "PROC=UNREADABLE"; exit 0 }
    $sup = @($all | Where-Object { $_.ProcessId -eq $p })
    if ($sup.Count -eq 0) { "PROC=SUPERVISOR_NOT_IN_TABLE pid=$p"; exit 0 }
    "PROC role=supervisor pid=$p exe=[$($sup[0].ExecutablePath)]"
    foreach ($k in @($all | Where-Object { $_.ParentProcessId -eq $p })) {
      "PROC role=brain pid=$($k.ProcessId) exe=[$($k.ExecutablePath)]"
    }
  ' > "$EVID/processes.txt" 2>&1
  say "PROCESSES recorded in evidence/processes.txt"
  # EVERY recorded path must be the rig executable. A supervisor or brain running some other
  # binary means the home is not being served by the subject, whatever the bytes at the path say.
  while IFS= read -r line; do
    case "$line" in
      PROC\ role=*)
        ep=$(echo "$line" | sed -n 's/.*exe=\[\(.*\)\]$/\1/p')
        [ -n "$ep" ] || { say "  a process row carries no executable path: $line"; continue; }
        [ "$(canon "$ep")" = "$RIG_CANON" ] || fail "a running process for this home is $ep, not the rig executable $RIG_EXE"
        ;;
      PROC=UNREADABLE|PROC=SUPERVISOR_NOT_IN_TABLE*)
        fail "the running processes for this home could not be attributed ($line). UNREADABLE is never CLEAR." ;;
    esac
  done < "$EVID/processes.txt"
  say "PATHS_VERIFIED every supervisor/brain row for this home resolves to the rig executable"
else
  say "PROCESSES none: $PIDF is absent, so the apply left no daemon for this home. Recorded as ABSENT — nothing is started here to make this answerable."
fi
rollout_state VERIFIED "$SET_VERSION"

# ---- 7. the provenance record the driver's S0b reads ------------------------
# This is what makes 'intentionally provisioned' a MEASURABLE property instead of an assertion.
# The driver re-measures the bytes itself and does not take this file's word for them.
printf '{\n  "rig_id": "%s",\n  "rig_exe": "%s",\n  "rig_exe_sha256": "%s",\n  "home": "%s",\n  "applied_version": "%s",\n  "trust_key_id": "%s",\n  "applied_evidence": "%s",\n  "provisioned_utc": "%s"\n}\n' \
  "$RIG_ID" "$RIG_EXE" "$SUBJECT_SHA" "$H" "$SET_VERSION" "$TRUST_KEY_ID" "$EVID/applied-evidence.txt" \
  "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$H/provenance.json" || fail "could not write the provenance record"
say "PROVENANCE $H/provenance.json written"
rollout_state PROVISIONED "$SET_VERSION"

# ---- 8. residue, stated rather than swept -----------------------------------
say "RESIDUE, all of it inside $RIG_ROOT and none of it removed here:"
say "  $H                      the home: the rig trust key, the staged set, the product's applied record"
say "  $RIG_EXE                the disposable copy"
ls -1 "$RIG_ROOT/bin" 2>/dev/null | grep -i 'old' | while IFS= read -r o; do
  say "  $RIG_ROOT/bin/$o        LEFT BY THE APPLY's swap — evidence that it swapped; hashed below, never auto-removed"
done
( cd "$RIG_ROOT" && find . -type f -print0 | xargs -0 sha256sum ) > "$RIG_ROOT/RIG-MANIFEST.sha256" 2>/dev/null \
  && say "  RIG-MANIFEST.sha256      every file in the rig, hashed, so teardown can name what it destroys"
say "TEARDOWN IS A SEPARATE, MEASURED STEP and is not performed here. The trust key file is named"
say "  explicitly in the manifest so its destruction is a measured act rather than an implication."
say "  THE SIGNING SEED was an ENVIRONMENT input to this run, for the staging subprocess alone. It"
say "  is in no file this script wrote, in no argument, and in no line of this transcript; it was"
say "  unset before the apply and that absence was asserted, not assumed. Its disposal in the"
say "  environment it came from is specified apart and is not this script's act."
say "PROVISIONED id=$RIG_ID root=$RIG_ROOT — pin RIG_ROOT in the driver; the driver consumes this rig ONCE."
exit 0
