#!/usr/bin/env bash # Regression test for the three STRUCTURAL claims the ADR-0020 gate family makes # about itself. None of them was pinned anywhere before this file, and each one # fails silently — which is the whole reason they need a test rather than a # comment: # # 1. "ONE resolver, embedded VERBATIM in three scripts." The block between the # BEGIN/END markers is copied, not imported, because a cache-installed # plugin's scripts cannot read files outside their own plugin directory. # Nothing but this file asserts the three copies are still identical, and a # one-line edit to a single copy is invisible: every constant-agreement # assertion in tests/test-skill-size-check.sh still passes, because the # CONSTANTS are not what drifted. # 2. Both interpreter preflights, in all three scripts. python3 and PyYAML are # declared HARD dependencies precisely so a missing one cannot turn into a # vacuous pass, and the two are checked separately so the message names the # thing to install rather than the wrong one. # 3. `verbose: true` on the skill-size-check hook. It is the ENTIRE delivery # mechanism for the SUGGESTION tier: pre-commit prints nothing at all for a # passing hook, and a SUGGESTION deliberately does not fail, so dropping # one word from the config silences the tier ADR-0020 depends on while # every test and every hook still reports green. set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" HOOK="$REPO_ROOT/scripts/skill-size-check.sh" SKILL_VALIDATE="$REPO_ROOT/plugins/kyberforge/.apm/skills/skill-audit/scripts/validate.sh" AGENT_VALIDATE="$REPO_ROOT/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate.sh" PASS=0 FAIL=0 pass() { echo " PASS: $1"; PASS=$((PASS + 1)); } fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); } TMPDIR_T="$(mktemp -d)" trap 'rm -rf "$TMPDIR_T"' EXIT BEGIN_MARKER='# ===== BEGIN ADR-0020 SHARED BOUNDARY RESOLVER =====' END_MARKER='# ===== END ADR-0020 SHARED BOUNDARY RESOLVER =====' # --------------------------------------------------------------------------- # 1. The shared resolver block is byte-identical in all three scripts # --------------------------------------------------------------------------- echo "" echo "--- the ADR-0020 shared resolver block is byte-identical in all three scripts ---" # Marker discipline first. An unbalanced or duplicated marker pair makes the # extraction below silently measure the wrong span — a sed range that never # closes swallows the rest of the file, and one that opens twice concatenates # two spans. Both would still compare "equal" if all three were mangled the # same way, so the shape is asserted before the contents. MARKERS_OK=true for f in "$HOOK" "$SKILL_VALIDATE" "$AGENT_VALIDATE"; do if [[ ! -f "$f" ]]; then fail "script not found: $f" MARKERS_OK=false continue fi b="$(grep -cFx "$BEGIN_MARKER" "$f" || true)" e="$(grep -cFx "$END_MARKER" "$f" || true)" if [[ "$b" == "1" && "$e" == "1" ]]; then pass "${f#"$REPO_ROOT/"} carries exactly one BEGIN and one END marker" else fail "${f#"$REPO_ROOT/"} has $b BEGIN and $e END markers, expected 1 and 1" MARKERS_OK=false fi done if ! $MARKERS_OK; then fail "skipping the byte-identity comparison — the marker pairs are not well-formed, so any extraction would measure the wrong span" else HASHES=() LINECOUNTS=() for f in "$HOOK" "$SKILL_VALIDATE" "$AGENT_VALIDATE"; do out="$TMPDIR_T/block-$(echo "$f" | md5sum | cut -c1-8).txt" sed -n "/^${BEGIN_MARKER}\$/,/^${END_MARKER}\$/p" "$f" > "$out" HASHES+=("$(md5sum < "$out" | cut -d' ' -f1)") LINECOUNTS+=("$(wc -l < "$out" | tr -d ' ')") done if [[ "${HASHES[0]}" == "${HASHES[1]}" && "${HASHES[1]}" == "${HASHES[2]}" ]]; then pass "all three copies hash to ${HASHES[0]} (${LINECOUNTS[0]} lines) — agreement by construction, not by coincidence" else fail "the shared resolver has DRIFTED: skill-size-check=${HASHES[0]} (${LINECOUNTS[0]} lines), skill-audit=${HASHES[1]} (${LINECOUNTS[1]} lines), agent-audit=${HASHES[2]} (${LINECOUNTS[2]} lines). Edit one copy, then paste it over the other two." fi # A block that has been emptied out would hash equal in all three and pass the # comparison above while enforcing nothing. The resolver is ~570 lines; 100 is # a floor low enough never to need maintenance and high enough that a gutted # block cannot sneak past. if [[ "${LINECOUNTS[0]}" -gt 100 ]]; then pass "the extracted block is ${LINECOUNTS[0]} lines — the comparison is over real content, not an empty span" else fail "the extracted shared block is only ${LINECOUNTS[0]} lines — three identical empty spans would compare equal and assert nothing" fi fi # --------------------------------------------------------------------------- # 2. Both interpreter preflights, in all three scripts # --------------------------------------------------------------------------- # The two are checked separately on purpose: `python3 -c 'import yaml'` fails # identically whether python3 is missing or PyYAML is, and naming the wrong one # sends the reader to install the wrong thing. REAL_PYTHON="$(command -v python3)" # Absolute path, deliberately. The no-python3 fixture below replaces PATH # wholesale, so a bare `bash` (or `/usr/bin/env bash`) would be resolved against # that stripped PATH and die with "No such file or directory" before the script # under test ever starts -- a 127 that looks like the preflight firing. BASH_BIN="$(command -v bash)" # A PATH that genuinely has no python3 on it. Built by symlinking the handful of # binaries the three scripts touch before their own preflight rather than by # hiding python3 from a full PATH, because there is no portable way to subtract # one entry from a directory. `bash` is invoked by absolute path below so the # interpreter itself does not have to be on this PATH. NOPY_BIN="$TMPDIR_T/nopython-bin" mkdir -p "$NOPY_BIN" for b in awk cat cut dirname basename grep sed pwd rm mkdir tr; do src="$(command -v "$b" 2>/dev/null || true)" [[ -n "$src" ]] && ln -sf "$src" "$NOPY_BIN/$b" done # A python3 that runs but cannot import yaml. A shim on PATH re-execs the real # interpreter with a PYTHONPATH entry holding a `yaml` module that raises on # import; PYTHONPATH precedes site-packages on sys.path, so it shadows a real # PyYAML install without touching it. SHADOW="$TMPDIR_T/shadow" mkdir -p "$SHADOW" printf 'raise ImportError("PyYAML deliberately unavailable in this fixture")\n' \ > "$SHADOW/yaml.py" NOYAML_BIN="$TMPDIR_T/noyaml-bin" mkdir -p "$NOYAML_BIN" cat > "$NOYAML_BIN/python3" </dev/null; then fail "the no-PyYAML shim does not actually shadow PyYAML — every PyYAML assertion below would be vacuous" else pass "fixture check: the no-PyYAML shim makes 'import yaml' fail while python3 still runs" fi if PATH="$NOPY_BIN" command -v python3 > /dev/null 2>&1; then fail "the no-python3 PATH still resolves python3 — every python3 assertion below would be vacuous" else pass "fixture check: the no-python3 PATH resolves no python3" fi # A minimal, entirely clean subject for each script. The preflight must fire # before any measurement, so the subject's own content is irrelevant — which is # exactly what makes a clean one the right choice: nothing else can produce the # non-zero exit these cases assert. SUBJECT_SKILL_DIR="$TMPDIR_T/subject/my-skill" mkdir -p "$SUBJECT_SKILL_DIR" cat > "$SUBJECT_SKILL_DIR/SKILL.md" <<'EOF' --- name: my-skill description: A short valid description. Do not use for anything else. --- Do the thing. EOF SUBJECT_AGENT_ROOT="$TMPDIR_T/subject-agent" mkdir -p "$SUBJECT_AGENT_ROOT/.apm/agents" cat > "$SUBJECT_AGENT_ROOT/apm.yml" <<'EOF' name: test-package version: 0.1.0 type: skill EOF cat > "$SUBJECT_AGENT_ROOT/.apm/agents/my-agent.agent.md" <<'EOF' --- name: my-agent description: A short valid description. Do not use for anything else. --- You are a test agent. When invoked, do the thing. EOF # probe_preflight