#!/usr/bin/env bash
# tests/creds-matrix/run-tests.sh -- harness for scripts/creds-matrix.py (D-137 tier 1).
#
# One case per invariant, each named for the REAL defect it encodes. Fixtures are
# mutations of a minimal valid artifact set, and every case asserts BOTH the exit code and
# an output regex -- a check that fires with the wrong message is a check nobody can act on.
#
# Gauntlet contract (scripts/run-tests-all.sh:22 registers by glob and shows only tail -1):
# the LAST line self-describes, and failure detail lines lead with a bare FAIL so the
# runner's `^\s*(FAIL|\[XX\]|MISS|LEAK|COUNT)` excerpt matches them.
set -uo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
CHECK="$REPO/scripts/creds-matrix.py"
TMPROOT="${TMPDIR:-/tmp}/creds-matrix-tests.$$"
mkdir -p "$TMPROOT"
trap 'rm -rf "$TMPROOT"' EXIT

P=0; F=0
ok() { P=$((P+1)); echo "  [ok]   $1"; }
no() { F=$((F+1)); echo "FAIL   $1"; }

# ---- minimal VALID fixture set -------------------------------------------------------
mkenv() {
  local d; d="$(mktemp -d "$TMPROOT/env.XXXXXX")"
  mkdir -p "$d/creds-manifests"
  cat > "$d/matrix.tsv" <<'EOF'
alpha-key   per-site  vr1-office1  jumphost  alpha_ed25519      ssh   service  consolidated  stage2  operator-terminal  -  n-alpha
alpha-key   per-site  vr1-office1  jumphost  alpha_ed25519.pub  none  service  consolidated  stage2  operator-terminal  -  n-alpha
EOF
  cat > "$d/creds-manifests/vr1-office1.manifest" <<'EOF'
alpha_ed25519      600  local
alpha_ed25519.pub  644  local
EOF
  cat > "$d/notes.md" <<'EOF'
## n-alpha
The alpha service keypair.
EOF
  echo "$d"
}
run() {  # run <env-dir> [extra args...] -> sets RC, writes $1/out
  local d="$1"; shift
  # Derive the stage declaration from THIS fixture's matrix, all reached. A fixed default
  # cannot work: the declaration must be exhaustive over the tokens used AND carry no
  # unused token, so it has to track the fixture. Cases that need a `pending` stage write
  # their own file before calling run().
  [ -f "$d/stages" ] || awk '!/^#/ && NF==12 {print $9" reached"}' "$d/matrix.tsv" \
      | sort -u > "$d/stages"
  # V2's SEC register, fixture-scoped. Empty unless the case writes one -- WITHOUT this the
  # tier-3 cases read the REAL docs/security-ledger.md (because --repo must point at the real
  # repo for S4 mint-ref resolution), which is why V2 had no harness cases at all.
  [ -f "$d/ledger.md" ] || : > "$d/ledger.md"
  # Derive the host-identity binding from THIS fixture's locations, same reasoning as stages:
  # it must be exhaustive over the roles that use a `local` target and carry no unused role,
  # so it has to track the fixture. WITHOUT this the fixtures fell through to the REAL repo's
  # binding (--repo must point at the real repo for S4 mint-ref resolution) -- the identical
  # trap that left V2 with no cases at all. Fixture host is a fixed token; cases testing the
  # wrong-host path pass their own --this-host.
  [ -f "$d/host-identity" ] || awk '!/^#/ && NF==4 && $3=="local" {print $1" fixturehost"}' \
      "$d/locations" | sort -u > "$d/host-identity"
  python3 "$CHECK" --matrix "$d/matrix.tsv" --manifest-dir "$d/creds-manifests" \
                   --notes "$d/notes.md" --repo "$REPO" --stages "$d/stages" \
                   --locations "$d/locations" --ledger "$d/ledger.md" \
                   --host-identity "$d/host-identity" --this-host "${THIS_HOST:-fixturehost}" \
                   "$@" > "$d/out" 2>&1
  RC=$?
}
# A tier-2 fixture: a declared LOCAL location holding the baseline keypair.
mkloc() {  # mkloc <env-dir>
  local d="$1"; mkdir -p "$d/store"
  printf 'x\n' > "$d/store/alpha_ed25519";     chmod 600 "$d/store/alpha_ed25519"
  printf 'x\n' > "$d/store/alpha_ed25519.pub"; chmod 644 "$d/store/alpha_ed25519.pub"
  printf 'jumphost  vr1-office1  local  %s/store/*\n' "$d" > "$d/locations"
}
row() { printf '%s\n' "$1" >> "$2/matrix.tsv"; }
note() { printf '\n## %s\nfixture note.\n' "$1" >> "$2/notes.md"; }

echo "=== creds-matrix harness ==="

# T1 -- the baseline must actually PASS, or every other case proves nothing.
D=$(mkenv); run "$D"
[ "$RC" = 0 ] && grep -q '^PASS: creds-matrix' "$D/out" \
  && ok "T1 a consistent matrix/manifest/notes set -> PASS, exit 0" \
  || { no "T1 the valid baseline must pass (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T2 -- a field containing whitespace silently shifts every later column.
D=$(mkenv); row 'beta pass  singleton  vr1-office1  jumphost  beta.txt  api  service  consolidated  stage2  operator-terminal  -  -' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q 'field(s), want 12' "$D/out" \
  && ok "T2 a row with a whitespace-bearing field -> field-count FAIL" \
  || { no "T2 whitespace in a field must be caught (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T3 -- bare `dc0` is the drifting form that has meant two different things per DC.
D=$(mkenv); row 'beta  per-DC  dc0  jumphost  beta.txt  api  service  off-manifest-known  stage3  operator-terminal  -  -' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q 'not region-qualified' "$D/out" \
  && ok "T3 a bare 'dc0' site-key -> REJECTED (region-qualified discipline)" \
  || { no "T3 bare dc0 must be rejected (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T4 -- an invented custody token would silently escape every custody-conditioned rule.
D=$(mkenv); row 'beta  singleton  vr1-office1  jumphost  beta.txt  api  service  probably-fine  stage2  operator-terminal  -  -' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q "custody 'probably-fine' invalid" "$D/out" \
  && ok "T4 an unknown custody token -> enum FAIL" \
  || { no "T4 custody enum must be closed (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T5 -- the SAME identity declared twice at one location is a real duplication error.
D=$(mkenv); row 'alpha-key  per-site  vr1-office1  jumphost  alpha_ed25519  ssh  service  consolidated  stage2  operator-terminal  -  n-alpha' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q 'duplicate (id,site,host-role,filename)' "$D/out" \
  && ok "T5 one identity declared twice at one location -> duplicate FAIL" \
  || { no "T5 same-identity duplication must be caught (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T6 -- REGRESSION: two DIFFERENT identities may share one file (the Vault init artifact
# holds both the unseal shares and the root token). Keying dedup on location alone
# false-failed this.
D=$(mkenv)
row 'vault-shares  singleton  -  jumphost  vault-init  none  service  off-manifest-known  vr0-phase02  operator-terminal  -  -' "$D"
row 'vault-root    singleton  -  jumphost  vault-init  none  service  off-manifest-known  vr0-phase02  operator-terminal  -  -' "$D"
run "$D"
[ "$RC" = 0 ] && grep -q '^PASS: creds-matrix' "$D/out" && ! grep -q 'FAIL.*duplicate' "$D/out" \
  && ok "T6 two identities sharing one file -> allowed (Vault init regression)" \
  || { no "T6 distinct identities may share a file (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T7 -- SEC-021's class: the register expects a credential the manifest never declared.
D=$(mkenv); row 'gamma  per-site  vr1-office1  jumphost  gamma.txt  api  service  consolidated  stage2  operator-terminal  SEC-021  -' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q "EXPECTED-BUT-ABSENT: 'gamma.txt'" "$D/out" \
  && ok "T7 expected by the matrix, absent from the manifest -> EXPECTED-BUT-ABSENT (SEC-021 class)" \
  || { no "T7 expected-but-absent must fire (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T8 -- the undeclared-secret class SEC-009/-020 kept missing, now from the other side.
D=$(mkenv); printf 'delta.txt  600  local\n' >> "$D/creds-manifests/vr1-office1.manifest"
run "$D"
[ "$RC" = 1 ] && grep -q "UNEXPECTED-EXTRA: manifest declares 'delta.txt'" "$D/out" \
  && ok "T8 declared in the manifest, unknown to the matrix -> UNEXPECTED-EXTRA" \
  || { no "T8 unexpected-extra must fire (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T9 -- THE FALSE GREEN. An empty matrix examines no site, so a site-scoped loop finds
# nothing undeclared and reports CLEAN while every declared credential sits unregistered.
# Same defect netbox/sandbox-fidelity-check.py:131-143 records: an upper bound
# masquerading as an assertion. The site set must be the UNION of both sides.
D=$(mkenv); printf '# every row removed\n' > "$D/matrix.tsv"
run "$D"
[ "$RC" = 1 ] && grep -q 'UNEXPECTED-EXTRA' "$D/out" \
  && ok "T9 an EMPTY matrix against populated manifests -> FAILS (the false-green regression)" \
  || { no "T9 an empty matrix must not read clean (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T10 -- a mint-ref that points past EOF is stale provenance, the drift class tier 3 exists for.
D=$(mkenv); row 'beta  singleton  vr1-office1  headend  beta.txt  api  service  off-manifest-known  stage2  script:scripts/creds-audit.sh:99999  -  -' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q 'points past EOF' "$D/out" \
  && ok "T10 a mint-ref line past EOF -> stale-provenance FAIL" \
  || { no "T10 out-of-range mint-ref must be caught (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T11 -- a mint-ref naming a file that no longer exists (the rename/retire class).
D=$(mkenv); row 'beta  singleton  vr1-office1  headend  beta.txt  api  service  off-manifest-known  stage2  script:scripts/no-such-script.sh:1  -  -' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q 'mint-ref target does not exist' "$D/out" \
  && ok "T11 a mint-ref to a deleted file -> FAIL" \
  || { no "T11 missing mint-ref target must be caught (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T12 -- research FINDING 1: ssh-keygen has ZERO hits repo-wide, so 12 declared secrets
# have no mint command. Those rows are the DEBT the register exists to surface -- they
# must be ADMITTED and reported, never treated as parse errors.
D=$(mkenv); run "$D"
[ "$RC" = 0 ] && grep -q 'provenance debt: 2 row(s) are mint-ref=operator-terminal' "$D/out" \
  && ok "T12 operator-terminal rows -> admitted and reported as debt, not parse errors" \
  || { no "T12 operator-terminal must be admitted + counted (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T13 -- a per-DC credential existing at one DC only (the SEC-021 divergence shape).
D=$(mkenv)
row 'dc0-power  per-DC  vr1-dc0  jumphost  vr1-dc0-power_ed25519  ssh  service  consolidated  stage3  operator-terminal  -  -' "$D"
mkdir -p "$D/creds-manifests"; printf 'vr1-dc0-power_ed25519  600  local\n' > "$D/creds-manifests/vr1-dc0.manifest"
run "$D"
[ "$RC" = 1 ] && grep -q 'S5 ASYMMETRY' "$D/out" \
  && ok "T13 a per-DC credential at one DC only -> ASYMMETRY FAIL (SEC-021 shape)" \
  || { no "T13 per-DC asymmetry must fire (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T14 -- narrowing to one DC cannot silently 'pass' a check that needs both.
D=$(mkenv); run "$D" --dc vr1-dc1
[ "$RC" = 0 ] && grep -q 'symmetry needs BOTH DCs, so the check is SKIPPED' "$D/out" \
  && ok "T14 --dc narrows -> symmetry SKIPPED explicitly, never silently passed" \
  || { no "T14 --dc must skip symmetry out loud (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T15 -- D-137 ruling 5: ONE IDENTITY SERVES ONE PRINCIPAL TYPE. This is the live SEC-020
# conflation (`admin` is both the human GUI login and the automation identity).
D=$(mkenv)
row 'shared-ident  singleton  vr1-office1  headend  shared.pass    gui  human    off-manifest-known  stage2  operator-terminal  -  -' "$D"
row 'shared-ident  singleton  vr1-office1  headend  shared.apikey  api  service  off-manifest-known  stage2  operator-terminal  -  -' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q 'IDENTITY CONFLATION' "$D/out" \
  && ok "T15 one identity serving human AND service -> ruling-5 CONFLATION FAIL" \
  || { no "T15 ruling-5 invariant must fire (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T16 -- a non-credential index file (principal '-') must not fault the ruling-5 check.
D=$(mkenv)
row 'folder-readme  per-site  vr1-office1  jumphost  README  none  -  consolidated  -  -  -  -' "$D"
printf 'README  600  local\n' >> "$D/creds-manifests/vr1-office1.manifest"
run "$D"
[ "$RC" = 0 ] && ! grep -q 'IDENTITY CONFLATION' "$D/out" \
  && ok "T16 a principal='-' index file -> skipped by ruling 5, not faulted" \
  || { no "T16 principal '-' must be skipped (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T17 -- a notes-ref with no entry means ruling-2 governance prose was silently dropped.
D=$(mkenv); row 'beta  singleton  vr1-office1  headend  beta.txt  api  service  off-manifest-known  stage2  operator-terminal  -  n-nonexistent' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q "notes-ref 'n-nonexistent' has no" "$D/out" \
  && ok "T17 a dangling notes-ref -> FAIL (ruling-2 prose must not be droppable)" \
  || { no "T17 dangling notes-ref must be caught (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T18 -- an orphan note is prose that survived the row it documented (the other direction).
D=$(mkenv); note 'n-orphan' "$D"
run "$D"
[ "$RC" = 1 ] && grep -q "orphan note 'n-orphan'" "$D/out" \
  && ok "T18 a note referenced by no row -> orphan FAIL" \
  || { no "T18 orphan notes must be caught (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T19 -- the mode column is DERIVED (.pub -> 644, else 600), so a hand-edited manifest
# mode is drift. This is what makes manifests generated artifacts per ruling 2.
D=$(mkenv); sed -i 's/^alpha_ed25519.pub  644/alpha_ed25519.pub  600/' "$D/creds-manifests/vr1-office1.manifest"
run "$D"
[ "$RC" = 1 ] && grep -qE 'FIELD DRIFT|render drift' "$D/out" \
  && ok "T19 a hand-edited manifest mode -> render/field DRIFT FAIL" \
  || { no "T19 mode drift must be caught (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T20 -- rows with no host artifact (filename '-') must skip existence-shaped checks
# rather than fault: the juju-<dc> superuser password is stored NOWHERE by ruling.
D=$(mkenv)
row 'nowhere-user  singleton  vr1-office1  -  -  api  service  off-manifest-known  stage5  operator-terminal  -  -' "$D"
run "$D"
[ "$RC" = 0 ] \
  && ok "T20 a filename='-' row (credential with no host artifact) -> skipped, not faulted" \
  || { no "T20 no-artifact rows must be skipped (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T21 -- a tier that did not run must announce itself. A silent pass is the exact
# false-green this design exists to prevent. Tier 2 is built, so --all RUNS it and only
# tier 3 self-announces; a default run announces tier 2 as NOT RUN.
D=$(mkenv); mkloc "$D"; run "$D" --all
[ "$RC" = 0 ] && grep -q 'tier 2 (EXISTENCE)' "$D/out" \
             && grep -q 'tier 3 (VALIDITY)' "$D/out" \
  && ok "T21a --all runs every built tier (2 and 3) -> no silent skip" \
  || { no "T21a --all tier reporting (rc=$RC)"; sed 's/^/       /' "$D/out"; }
D=$(mkenv); mkloc "$D"; run "$D"
[ "$RC" = 0 ] && grep -q 'tier 2 (EXISTENCE) NOT RUN' "$D/out" \
  && ok "T21b a default run announces tier 2 as NOT RUN -> never a silent skip" \
  || { no "T21b default run must announce tier 2 (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T22 -- METADATA ONLY. The checker must never gain a content-reading verb; it inspects
# existence, mode, and declared provenance. Mirrors tests/creds-audit/run-tests.sh:71-73.
grep -qE '\b(cat|head|tail|less|more|od|xxd|hexdump)\b' "$CHECK" \
  && no "T22 the checker contains a content-reading verb -- it must inspect metadata only" \
  || ok "T22 metadata-only: no cat/head/tail/od/xxd in the checker"

# T23 -- --render must emit a GENERATED banner, so nobody hand-edits a derived manifest.
D=$(mkenv); run "$D" --render
[ "$RC" = 0 ] && grep -q 'GENERATED from creds-matrix.tsv' "$D/out" \
             && grep -qE 'alpha_ed25519\.pub +644 +local' "$D/out" \
  && ok "T23 --render emits a GENERATED banner and the derived modes" \
  || { no "T23 --render output shape (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T24 -- the REAL tree must be RED, and red for the RIGHT CLASSES. Compared against a
# checked-in baseline of finding CLASSES rather than literal strings: the old form grepped
# for "IDENTITY CONFLATION", so REMEDIATING that defect -- the fix the plan, the notes and
# the changelog all prescribe -- would have deleted the string and turned the GAUNTLET red
# for whoever did the right thing. The cheapest response at that moment is to edit the
# test, which is exactly when the "do not go green by deleting rows" guard stops existing.
# With a baseline, remediation REQUIRES updating expected-findings.txt in the same commit:
# a recorded decision instead of a mystery breakage.
python3 "$CHECK" 2>&1 | python3 "$HERE/classify-findings.py" > "$TMPROOT/actual.txt"; RRC=${PIPESTATUS[0]}
if diff -u "$HERE/expected-findings.txt" "$TMPROOT/actual.txt" > "$TMPROOT/diff.txt" 2>&1; then
  [ "$RRC" = 1 ] \
    && ok "T24 the real tree is RED with exactly the baselined finding classes" \
    || no "T24 the real tree must exit 1 (rc=$RRC)"
else
  no "T24 finding CLASSES drifted from tests/creds-matrix/expected-findings.txt -- if you"
  echo "       FIXED a defect, update the baseline in the SAME commit; if a NEW class"
  echo "       appeared, that is a regression. Diff (expected vs actual):"
  sed 's/^/       /' "$TMPROOT/diff.txt"
fi


# ---- tier 2 (EXISTENCE) ---------------------------------------------------------------

# T25 -- the tier-2 baseline: everything expected is present at its declared location.
D=$(mkenv); mkloc "$D"; run "$D" --tier2
[ "$RC" = 0 ] && grep -q 'E1/E3 existence' "$D/out" \
  && ok "T25 every expected artifact present at a declared location -> tier 2 clean" \
  || { no "T25 tier-2 baseline must pass (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T26 -- the register's whole reason for existing: a credential that SHOULD be there and
# is not. Absence is invisible to a discovery sweep; only an expected-state register sees it.
D=$(mkenv); mkloc "$D"; rm "$D/store/alpha_ed25519"; run "$D" --tier2
[ "$RC" = 1 ] && grep -q "E1 EXPECTED-BUT-ABSENT: alpha-key 'alpha_ed25519'" "$D/out" \
  && ok "T26 an expected credential missing from disk -> E1 EXPECTED-BUT-ABSENT" \
  || { no "T26 absence must be detectable (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T27 -- a credential whose mint-stage has not been reached is DEFERRED, not failed --
# and the caller declares that, never the script (GA-R1: no status claim lives in code).
D=$(mkenv); mkloc "$D"; rm "$D/store/alpha_ed25519"
printf 'stage2 pending\n' > "$D/stages"; run "$D" --tier2
[ "$RC" = 0 ] && grep -q 'deferred as not-yet-minted' "$D/out" \
  && ok "T27 a stage declared 'pending' defers its credential instead of failing it" \
  || { no "T27 pending deferral (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T28 -- the SEC-020/-022 class from the other side: a real secret sitting at a declared
# location that no matrix row accounts for.
D=$(mkenv); mkloc "$D"; printf 'x\n' > "$D/store/stray.apikey"; run "$D" --tier2
[ "$RC" = 1 ] && grep -q "E3 UNDECLARED: 'stray.apikey'" "$D/out" \
  && ok "T28 an undeclared secret at a declared location -> E3 UNDECLARED" \
  || { no "T28 undeclared detection (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T29 -- a group-readable private key is an exposure, not a cosmetic difference.
D=$(mkenv); mkloc "$D"; chmod 640 "$D/store/alpha_ed25519"; run "$D" --tier2
[ "$RC" = 1 ] && grep -q 'E2 MODE' "$D/out" \
  && ok "T29 a private key at the wrong mode -> E2 MODE FAIL" \
  || { no "T29 mode enforcement (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T30 -- ruling 3 is absolute: with no declared-location list there is no bound, so
# discovery must REFUSE to run rather than fall back to walking anything.
D=$(mkenv); mkloc "$D"; rm "$D/locations"; run "$D" --tier2
[ "$RC" = 1 ] && grep -q 'no declared-location list' "$D/out" \
  && ok "T30 a missing locations list -> tier 2 REFUSES to run (ruling 3 is absolute)" \
  || { no "T30 must refuse unbounded discovery (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T31 -- remote locations are opt-in, and their omission must be VISIBLE. A local-only
# run that quietly ignored the headend would reproduce the SEC-022 blind spot exactly.
D=$(mkenv); mkloc "$D"
printf 'headend  vr1-office1  some-host  /root/secrets/*\n' >> "$D/locations"
run "$D" --tier2
[ "$RC" = 0 ] && grep -q 'remote location(s) SKIPPED' "$D/out" \
  && ok "T31 remote locations omitted without --remote -> announced, not silently dropped" \
  || { no "T31 remote omission must be visible (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T32 -- an unreachable host must SKIP explicitly. Treating 'could not look' as 'nothing
# there' is the false-green that makes a credential audit worthless.
D=$(mkenv); mkloc "$D"
printf 'headend  vr1-office1  no-such-host.invalid  /root/secrets/*\n' >> "$D/locations"
run "$D" --tier2 --remote
[ "$RC" = 0 ] && grep -q 'UNREACHABLE' "$D/out" \
             && grep -q 'never a pass' "$D/out" \
  && ok "T32 an unreachable host -> explicit UNREACHABLE skip, never counted as clean" \
  || { no "T32 unreachable must skip loudly (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T33 -- FALSE-GREEN REGRESSION: a declared location that EXISTS but cannot be listed.
# A shell glob over an unreadable directory does not expand, so a naive probe finds no
# files and reports "nothing undeclared here" about a directory it never opened. The
# region secrets dir (SEC-020) is exactly such a directory.
D=$(mkenv); mkloc "$D"; mkdir -p "$D/locked"; printf 'x\n' > "$D/locked/hidden.key"
chmod 000 "$D/locked"
printf 'jumphost  vr1-office1  local  %s/locked/*\n' "$D" >> "$D/locations"
run "$D" --tier2
chmod 755 "$D/locked"
[ "$RC" = 1 ] && grep -q 'E0 UNREADABLE' "$D/out" \
  && ok "T33 an unreadable declared location -> UNREADABLE FAIL, never an empty listing" \
  || { no "T33 unreadable must not read as empty (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T34 -- a declared location that simply does not exist yet is a SKIP, not a failure.
D=$(mkenv); mkloc "$D"
printf 'jumphost  vr1-office1  local  %s/not-created-yet/*\n' "$D" >> "$D/locations"
run "$D" --tier2
[ "$RC" = 0 ] && grep -q 'does not exist -- SKIPPED' "$D/out" \
  && ok "T34 a not-yet-created declared location -> explicit SKIP, not a failure" \
  || { no "T34 absent location handling (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# ---- committee-derived regressions (2026-07-26) ---------------------------------------

# T36 -- THE SITE COLLISION. Two sites, one role, the SAME basename: dc1 has the file,
# dc0 does not. Keyed on role alone, dc1's copy satisfied dc0's row and the absence was
# invisible -- which is exactly how SEC-021's dc0 opnsense-api.txt passed a green sweep.
# The old fixtures declared only ONE location per role, so this was structurally
# unreachable by the test set.
D=$(mkenv); mkloc "$D"
mkdir -p "$D/dc0store" "$D/dc1store"
printf 'x\n' > "$D/dc1store/shared.txt"; chmod 600 "$D/dc1store/shared.txt"
printf 'jumphost  vr1-dc0  local  %s/dc0store/*\n' "$D" >> "$D/locations"
printf 'jumphost  vr1-dc1  local  %s/dc1store/*\n' "$D" >> "$D/locations"
row 'shared-cred  per-DC  vr1-dc0  jumphost  shared.txt  api  service  off-manifest-known  stage3  operator-terminal  -  -' "$D"
row 'shared-cred  per-DC  vr1-dc1  jumphost  shared.txt  api  service  off-manifest-known  stage3  operator-terminal  -  -' "$D"
run "$D" --tier2
[ "$RC" = 1 ] && grep -q "E1 EXPECTED-BUT-ABSENT: shared-cred 'shared.txt' expected at jumphost/vr1-dc0" "$D/out" \
  && ok "T36 same basename, two sites, absent at one -> E1 fires for THAT site (site-collision)" \
  || { no "T36 site-scoped existence must fire (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T37 -- ruling 3's bound is ABSOLUTE, so an EMPTY list must refuse exactly like a missing
# one. Previously an empty file parsed to [] (not None), skipped the refusal, and then
# affirmed existence over zero probed locations.
D=$(mkenv); mkloc "$D"; : > "$D/locations"; run "$D" --tier2
[ "$RC" = 1 ] && grep -q 'is EMPTY' "$D/out" \
  && ok "T37 an EMPTY declared-location list -> REFUSES, never affirms over zero locations" \
  || { no "T37 empty locations must refuse (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T38 -- a register with no rows asserts nothing, but every check reported clean over the
# empty set and the run exited 0.
D=$(mkenv); printf '# all rows removed\n' > "$D/matrix.tsv"; run "$D"
[ "$RC" = 1 ] && grep -q 'S1 FLOOR' "$D/out" \
  && ok "T38 a ZERO-row matrix -> FLOOR failure, not a clean run" \
  || { no "T38 empty matrix must not be clean (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T39 -- mode enforcement used to apply only to custody=consolidated, leaving 43 of 77
# rows -- including the SEC-020 region dir and the SEC-022 shadow stores -- unchecked.
D=$(mkenv); mkloc "$D"
printf 'x\n' > "$D/store/exposed_ed25519"; chmod 644 "$D/store/exposed_ed25519"
row 'exposed  singleton  vr1-office1  jumphost  exposed_ed25519  ssh  service  off-manifest-known  stage2  operator-terminal  -  -' "$D"
run "$D" --tier2
[ "$RC" = 1 ] && grep -q 'E2 WORLD-READABLE' "$D/out" \
  && ok "T39 a world-readable secret at NON-consolidated custody -> caught" \
  || { no "T39 world-readable secrets must be caught at any custody (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T40 -- S5's SECOND direction. T13 only ever populated the dc0 side, so deleting the
# dc1->dc0 comparison left the harness fully green: an upper bound masquerading as an
# assertion, in the check whose entire job is symmetry.
D=$(mkenv)
row 'dc1-only  per-DC  vr1-dc1  jumphost  vr1-dc1-only_ed25519  ssh  service  off-manifest-known  stage3  operator-terminal  -  -' "$D"
printf 'vr1-dc1-only_ed25519  600  local\n' > "$D/creds-manifests/vr1-dc1.manifest"
run "$D"
[ "$RC" = 1 ] && grep -q 'S5 ASYMMETRY: vr1-dc1' "$D/out" \
  && ok "T40 a dc1-ONLY per-DC credential -> ASYMMETRY fires on the second direction too" \
  || { no "T40 S5 must assert BOTH directions (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T41 -- literal-file locations previously skipped the absent/unreadable determination
# entirely, so an unreadable directory read as "fully probed, nothing there".
D=$(mkenv); mkloc "$D"; mkdir -p "$D/lockedf"; printf 'x\n' > "$D/lockedf/k.key"; chmod 000 "$D/lockedf"
printf 'jumphost  vr1-office1  local  %s/lockedf/k.key\n' "$D" >> "$D/locations"
run "$D" --tier2; chmod 755 "$D/lockedf"
[ "$RC" = 1 ] && grep -q 'E0 UNREADABLE' "$D/out" \
  && ok "T41 an unreadable dir behind a LITERAL-file location -> UNREADABLE, not silent ok" \
  || { no "T41 literal paths need state detection too (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T42 -- the declared path reaches a remote shell, so its charset is a security boundary:
# ruling 3 calls the bound absolute, and an unvalidated `;` would execute on the host --
# under sudo when --privileged is passed.
D=$(mkenv); mkloc "$D"
printf 'jumphost  vr1-office1  local  /tmp/x;id\n' >> "$D/locations"
run "$D" --tier2
[ "$RC" = 1 ] && grep -q 'shell metacharacter' "$D/out" \
  && ok "T42 a shell metacharacter in a declared path -> REJECTED before any probe" \
  || { no "T42 unsafe declared paths must be rejected (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T43 -- a row whose (role, site) has no declared location can never be verified. It used
# to `continue` with no counter and no [ok], contradicting the module's own contract that
# nothing passes silently.
D=$(mkenv); mkloc "$D"
row 'orphan-scope  singleton  vr1-office1  unit  thing.txt  api  service  off-manifest-known  stage6  operator-terminal  -  -' "$D"
run "$D" --tier2
[ "$RC" = 1 ] && grep -q 'E4 UNCHECKABLE' "$D/out" \
  && ok "T43 a row with no declared location for its scope -> disclosed, not silently skipped" \
  || { no "T43 uncheckable rows must be disclosed (rc=$RC)"; sed 's/^/       /' "$D/out"; }


# T44 -- per-tenant/per-node rows name an instance they cannot know at authoring time.
# Exact matching made the five tenant rows unmatchable, so the first onboard would have
# produced five false absences AND five false undeclared findings on the SAME real files.
D=$(mkenv); mkloc "$D"
printf 'x\n' > "$D/store/acme-handover.txt"; chmod 600 "$D/store/acme-handover.txt"
row 'tenant-thing  per-tenant  vr1-office1  jumphost  <client>-handover.txt  api  service  off-manifest-known  tenant-onboard  operator-terminal  -  -' "$D"
run "$D" --tier2
[ "$RC" = 0 ] && grep -q 'E1/E3 existence' "$D/out" \
  && ok "T44 a <client> instance placeholder matches the real basename (no false absent+undeclared pair)" \
  || { no "T44 instance templating must match (rc=$RC)"; sed 's/^/       /' "$D/out"; }


# ---- tier 3 (VALIDITY) ------------------------------------------------------------------

# T45 -- the same artifact copied to two scopes, byte-identical -> clean.
mk3() {  # mk3 <env-dir> <bytesA> <bytesB>
  local d="$1"; mkdir -p "$d/s1" "$d/s2"
  printf '%s\n' "$2" > "$d/s1/twin.key"; chmod 600 "$d/s1/twin.key"
  printf '%s\n' "$3" > "$d/s2/twin.key"; chmod 600 "$d/s2/twin.key"
  printf 'jumphost  vr1-dc0  local  %s/s1/*\n' "$d" >> "$d/locations"
  printf 'jumphost  vr1-dc1  local  %s/s2/*\n' "$d" >> "$d/locations"
  row 'twin  per-DC  vr1-dc0  jumphost  twin.key  ssh  service  off-manifest-known  stage3  operator-terminal  -  -' "$d"
  row 'twin  per-DC  vr1-dc1  jumphost  twin.key  ssh  service  off-manifest-known  stage3  operator-terminal  -  -' "$d"
}
D=$(mkenv); mkloc "$D"; mk3 "$D" same same; run "$D" --tier2 --tier3
grep -q 'V1 provenance: all copies byte-identical' "$D/out" \
  && ok "T45 identical copies of one artifact -> V1 clean" \
  || { no "T45 identical copies should verify"; sed 's/^/       /' "$D/out"; }

# T46 -- THE POINT OF TIER 3. SEC-020: "any future rotation MUST update both copies in one
# operation or the region copy becomes a stale trap." That warning was unchecked prose from
# the day it was written; the sha256 equality behind it was a ONE-TIME manual act.
D=$(mkenv); mkloc "$D"; mk3 "$D" original rotated; run "$D" --tier2 --tier3
[ "$RC" = 1 ] && grep -q 'V1 PROVENANCE DRIFT' "$D/out" && grep -q 'STALE TRAP' "$D/out" \
  && ok "T46 one copy rotated and the other not -> V1 PROVENANCE DRIFT (the SEC-020 trap)" \
  || { no "T46 drift must be caught (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T47 -- REGRESSION for a false positive this check shipped with in draft: an identity's
# DISTINCT artifacts (a password, its API key, its CLI profile) are not byte-copies of each
# other. Keying digests by scope let them overwrite one another and report drift between
# unrelated files. Verified by hand against the live hosts: the digests actually matched.
D=$(mkenv); mkloc "$D"; mkdir -p "$D/multi"
printf 'pw\n'  > "$D/multi/thing.pass";   chmod 600 "$D/multi/thing.pass"
printf 'key\n' > "$D/multi/thing.apikey"; chmod 600 "$D/multi/thing.apikey"
printf 'jumphost  vr1-office1  local  %s/multi/*\n' "$D" >> "$D/locations"
row 'one-ident  singleton  vr1-office1  jumphost  thing.pass    gui  human    off-manifest-known  stage2  operator-terminal  -  -' "$D"
row 'one-ident  singleton  vr1-office1  jumphost  thing.apikey  api  human    off-manifest-known  stage2  operator-terminal  -  -' "$D"
run "$D" --tier2 --tier3
! grep -q 'V1 PROVENANCE DRIFT' "$D/out" \
  && ok "T47 distinct artifacts of ONE identity are not compared (draft false positive)" \
  || { no "T47 distinct artifacts must not be diffed"; sed 's/^/       /' "$D/out"; }

# T48 -- REGRESSION for the second draft false positive: the same credential in a DIFFERENT
# SERIALIZATION. The netbox token is a raw file on the VM and an assembled env file on the
# jumphost; they are the same secret and are NOT byte-identical by design.
D=$(mkenv); mkloc "$D"; mkdir -p "$D/raw" "$D/wrapped"
printf 'tok\n'            > "$D/raw/api.token";  chmod 600 "$D/raw/api.token"
printf 'URL=x\nTOK=tok\n' > "$D/wrapped/w.env";  chmod 600 "$D/wrapped/w.env"
printf 'netbox    vr1-office1  local  %s/raw/*\n' "$D" >> "$D/locations"
printf 'jumphost  vr1-office1  local  %s/wrapped/*\n' "$D" >> "$D/locations"
row 'wrapped-tok  singleton  vr1-office1  netbox    api.token  api  service  source-of-record  stage2  operator-terminal  -  -' "$D"
row 'wrapped-tok  singleton  vr1-office1  jumphost  w.env      api  service  consolidated      stage2  operator-terminal  -  -' "$D"
printf 'w.env  600  local\n' >> "$D/creds-manifests/vr1-office1.manifest"
run "$D" --tier2 --tier3
! grep -q 'V1 PROVENANCE DRIFT' "$D/out" \
  && ok "T48 same credential, different SERIALIZATION -> not compared (draft false positive)" \
  || { no "T48 re-serialized copies must not be diffed"; sed 's/^/       /' "$D/out"; }

# T49 -- but a copy DECLARED byte-identical under a different name IS compared. This is
# SEC-020's exact shape (admin.pass on the region, maas-admin-password on the jumphost)
# and it is the case tier 3 exists for, so it must not fall into T48's skip.
D=$(mkenv); mkloc "$D"; mkdir -p "$D/src" "$D/cpy"
printf 'v1\n' > "$D/src/orig.pass"; chmod 600 "$D/src/orig.pass"
printf 'v2\n' > "$D/cpy/work.pass"; chmod 600 "$D/cpy/work.pass"
printf 'netbox    vr1-office1  local  %s/src/*\n' "$D" >> "$D/locations"
printf 'jumphost  vr1-office1  local  %s/cpy/*\n' "$D" >> "$D/locations"
row 'declared-twin  singleton  vr1-office1  netbox    orig.pass  gui  human  source-of-record  stage2  operator-terminal  -  -' "$D"
row 'declared-twin  singleton  vr1-office1  jumphost  work.pass  gui  human  verbatim-copy     stage2  operator-terminal  -  -' "$D"
printf 'work.pass  600  local\n' >> "$D/creds-manifests/vr1-office1.manifest"
run "$D" --tier2 --tier3
[ "$RC" = 1 ] && grep -q 'V1 PROVENANCE DRIFT' "$D/out" \
  && ok "T49 a DECLARED verbatim copy under a different name IS compared (SEC-020 shape)" \
  || { no "T49 verbatim-copy pairs must be compared (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T50 -- a tier that did not run must announce itself, like tiers 2 and 3 elsewhere.
D=$(mkenv); mkloc "$D"; run "$D" --tier2
grep -q 'tier 3 (VALIDITY) NOT RUN' "$D/out" \
  && ok "T50 tier 3 omitted -> announced, never a silent skip" \
  || { no "T50 tier 3 must self-announce"; sed 's/^/       /' "$D/out"; }

# T51 -- DIGESTS ONLY, NEVER CONTENT. The plan's hard constraint. sha256sum is the mandated
# mechanism and is allowed; what must never appear is a verb that brings bytes back. This
# extends T22's list, which would not have caught a content transfer added alongside a
# legitimate digest call.
grep -q 'sha256sum' "$CHECK" \
  && ok "T51a the provenance path uses sha256sum (digest, not content)" \
  || no "T51a tier 3 must compare digests"
grep -qE '\b(scp|rsync|base64|dd)\b' "$CHECK" \
  && no "T51b the checker gained a content-TRANSFER verb -- digests only" \
  || ok "T51b no content-transfer verb anywhere in the checker"


# ---- stage declaration (D-137 tier 2 gate input) ---------------------------------------

# T52 -- without a stage declaration the checker cannot tell MISSING from NOT-YET-MINTED,
# and it must refuse rather than guess. Guessing either way is wrong in a dangerous
# direction: assume-reached floods the run with false absences, assume-pending silently
# defers real ones.
D=$(mkenv); mkloc "$D"; printf 'stage2 reached\n' > "$D/stages"; rm "$D/stages"
python3 "$CHECK" --matrix "$D/matrix.tsv" --manifest-dir "$D/creds-manifests" \
  --notes "$D/notes.md" --repo "$REPO" --locations "$D/locations" \
  --stages "$D/nope" --tier2 > "$D/out" 2>&1; RC=$?
[ "$RC" = 1 ] && grep -q 'no stage declaration' "$D/out" \
  && ok "T52 a missing stage declaration -> tier 2 REFUSES, never guesses" \
  || { no "T52 missing stage decl must refuse (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T53 -- EXHAUSTIVENESS. A mint-stage the declaration does not classify is a hard FAIL, not
# a default. This is the both-bounds discipline applied to the gate's own status input.
D=$(mkenv); mkloc "$D"; printf 'tenant-onboard pending\n' > "$D/stages"; run "$D" --tier2
[ "$RC" = 1 ] && grep -q "mint-stage 'stage2' is used in the matrix but NOT classified" "$D/out" \
  && ok "T53 an unclassified mint-stage -> FAIL (no silent default in either direction)" \
  || { no "T53 unclassified stages must fail (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T54 -- the other bound: a classified stage no row uses is a STALE declaration. This is
# how `stage4` was caught -- Stage 4 is open and executing but mints no credential.
D=$(mkenv); mkloc "$D"; printf 'stage2 reached\ntenant-onboard pending\nstage9 reached\n' > "$D/stages"
run "$D" --tier2
[ "$RC" = 1 ] && grep -q "'stage9' is classified but used by no matrix row" "$D/out" \
  && ok "T54 a classified-but-unused stage -> stale-declaration FAIL (both bounds)" \
  || { no "T54 stale stage declarations must fail (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T55 -- `pending` defers a missing credential; `reached` reports it. This is the whole
# point of the file, and it must swing BOTH ways on the same fixture.
D=$(mkenv); mkloc "$D"; rm "$D/store/alpha_ed25519"
printf 'stage2 pending\n' > "$D/stages"; run "$D" --tier2
PEND=$RC
printf 'stage2 reached\n' > "$D/stages"; run "$D" --tier2
[ "$PEND" = 0 ] && [ "$RC" = 1 ] && grep -q 'E1 EXPECTED-BUT-ABSENT' "$D/out" \
  && ok "T55 the same absent credential defers under 'pending' and FAILS under 'reached'" \
  || { no "T55 stage reach must drive the verdict (pending=$PEND reached=$RC)"; sed 's/^/       /' "$D/out"; }


# ---- V2 (tier 3, declared state) -----------------------------------------------------
# V2 shipped with ZERO cases: --repo had to point at the real repo, so there was no fixture
# path to the SEC register. T56-T59 exist because of that gap, not despite it.
secrow() {  # secrow <env-dir> <disposition-cell>
  printf '| SEC-900 | 2026-01-01 | fixture token is BURNED and must be revoked + reissued | fixture | operator | %s |\n' \
    "$2" > "$1/ledger.md"
}
# A consolidated row that cites the fixture SEC row.
v2env() { local d; d=$(mkenv); mkloc "$d"
  row 'zeta-token  singleton  vr1-office1  jumphost  zeta.txt  api  service  consolidated  stage2  operator-terminal  SEC-900  n-alpha' "$d"
  printf 'zeta.txt  600  local\n' >> "$d/creds-manifests/vr1-office1.manifest"
  printf 'x\n' > "$d/store/zeta.txt"; chmod 600 "$d/store/zeta.txt"
  echo "$d"; }

# T56 -- the original invariant: dead in the ledger, healthy in the register -> FAIL.
D=$(v2env); secrow "$D" 'OPEN -- rotate at v1 close'
run "$D" --tier2 --tier3
[ "$RC" = 1 ] && grep -q 'V2 DECLARED-DEAD BUT REGISTERED HEALTHY.*zeta-token' "$D/out" \
  && ok "T56 declared-dead + custody=consolidated -> V2 FAIL (the founding invariant)" \
  || { no "T56 a burned credential registered healthy must fail (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T57 -- SEC-006's real shape: the operator RULED the revocation deferred. Reporting that as
# a defect every run reports the operator's own decision as a bug -- but it must NOT go silent.
D=$(v2env); secrow "$D" 'OPEN -- **DEFERRED by operator ruling 2026-01-02: revoke at completion of this deployment.** Until then the token is live and exposed'
run "$D" --tier2 --tier3
[ "$RC" = 0 ] && grep -q 'V2 ACKNOWLEDGED DEFERRAL.*zeta-token' "$D/out" \
  && grep -q 'still live and still exposed' "$D/out" \
  && ok "T57 a RULED deferral -> acknowledged, exit 0, and the notice still says EXPOSED" \
  || { no "T57 a ruled deferral must be acknowledged, not failed and not silenced (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T58 -- the evasion guard, and the reason the match is narrow: prose that merely DISCUSSES
# deferring, with no operator ruling, must not buy silence. A loose match here would turn V2
# into the false green the register exists to prevent.
D=$(v2env); secrow "$D" 'OPEN -- deferred for now, we will revisit this later'
run "$D" --tier2 --tier3
[ "$RC" = 1 ] && grep -q 'V2 DECLARED-DEAD BUT REGISTERED HEALTHY' "$D/out" \
  && ok "T58 'deferred' WITHOUT an operator ruling -> still FAIL (no silencing by prose)" \
  || { no "T58 unruled deferral prose must not silence V2 (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T59 -- DEFERRED in one table cell and 'operator ruling' in another is NOT a ruled deferral;
# the cell boundary is load-bearing, so the pattern must not span `|`.
D=$(v2env)
printf '| SEC-900 | 2026-01-01 | fixture token is BURNED, DEFERRED | fixture | per an operator ruling | OPEN |\n' > "$D/ledger.md"
run "$D" --tier2 --tier3
[ "$RC" = 1 ] && grep -q 'V2 DECLARED-DEAD BUT REGISTERED HEALTHY' "$D/out" \
  && ok "T59 a deferral claim spanning two ledger cells does NOT count as ruled" \
  || { no "T59 the deferral match must not cross a table cell (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# --- host-identity: `local` is only meaningful ON the host the ROLE denotes ------------
# Measured 2026-07-29: running P5 on the headend probed the JUMPHOST's declared paths against
# the headend's own SEC-022 shadow stores and reported 26 artefact findings as fact. Absence
# asserted over the wrong filesystem is worse than "could not look", so these cases pin it.

# T60 -- the baseline is on the RIGHT host, so the location IS probed and the run is clean.
# Without this case T61 would prove nothing: a gate that never probes always "passes".
D=$(mkenv); mkloc "$D"; run "$D" --tier2
[ "$RC" = 0 ] && ! grep -q 'NOT PROBED' "$D/out" \
  && ok "T60 on the declared host the local location IS probed" \
  || { no "T60 baseline must probe on the right host (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T61 -- the defect itself: on any OTHER host the location must NOT be probed, and absence
# must not be asserted over it.
D=$(mkenv); mkloc "$D"; THIS_HOST=someotherhost run "$D" --tier2
grep -q 'NOT PROBED' "$D/out" && grep -q "this run is on 'someotherhost'" "$D/out" \
  && ok "T61 on a different host the local location is NOT PROBED, absence not asserted" \
  || { no "T61 a wrong-host local probe must be refused, not measured"; sed 's/^/       /' "$D/out"; }

# T62 -- an ABSENT binding file REFUSES. "Could not look" is never "nothing there", and the
# checker must not fall back to assuming it is running on the right host.
D=$(mkenv); mkloc "$D"; run "$D" --tier2 --host-identity "$D/nonexistent"
[ "$RC" = 1 ] && grep -q 'no host-identity declaration' "$D/out" \
  && ok "T62 a missing host-identity declaration REFUSES rather than guessing" \
  || { no "T62 missing host-identity must refuse (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T63 -- a role that uses `local` but is UNBOUND is a hard fail, never a default.
D=$(mkenv); mkloc "$D"; printf '# deliberately empty\n' > "$D/host-identity"; run "$D" --tier2
[ "$RC" = 1 ] && grep -q "uses a \`local\` target .* but is NOT bound" "$D/out" \
  && ok "T63 an unbound role using a local target FAILS, and is not defaulted" \
  || { no "T63 unbound local role must fail (rc=$RC)"; sed 's/^/       /' "$D/out"; }

# T64 -- exhaustive in the other direction too: a bound role that uses no `local` target is a
# stale declaration. Same discipline as stages-reached, and the reason it is checked is that a
# stale binding silently survives the removal of the location it described.
D=$(mkenv); mkloc "$D"
printf 'jumphost fixturehost\nedge     ghosthost\n' > "$D/host-identity"; run "$D" --tier2
[ "$RC" = 1 ] && grep -q "role 'edge' is bound to a host but uses no \`local\` target" "$D/out" \
  && ok "T64 a bound role with no local target is reported as a stale declaration" \
  || { no "T64 stale host binding must fail (rc=$RC)"; sed 's/^/       /' "$D/out"; }

echo
if [ "$F" = 0 ]; then echo "creds-matrix: $P/$P PASS"; exit 0; fi
echo "creds-matrix: FAILURES: $F (passed $P)"; exit 1
