#!/usr/bin/env bash set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SCRIPT="$REPO_ROOT/scripts/check-scope-walkup-sync.sh" PASS=0 FAIL=0 pass() { echo " PASS: $1"; PASS=$((PASS + 1)); } fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); } FIXTURES=() cleanup() { [[ ${#FIXTURES[@]} -eq 0 ]] || rm -rf "${FIXTURES[@]}"; } trap cleanup EXIT # --- 1. Exits 0 against this repo's own (fixed) scripts --- echo "" echo "--- exits 0 against this repo's real scripts ---" if bash "$SCRIPT" "$REPO_ROOT" > /tmp/check-scope-walkup-sync-clean.out 2>&1; then pass "exits 0 against this repo's real scope walk-up scripts" else fail "exited non-zero against this repo's real (already-fixed) scripts" sed 's/^/ /' /tmp/check-scope-walkup-sync-clean.out fi # --- 2. Exits 0 as a no-op when the kyberforge skills aren't present --- echo "" echo "--- exits 0 (no-op) when the target scripts don't exist ---" FIXTURE_EMPTY="$(mktemp -d)" FIXTURES+=("$FIXTURE_EMPTY") if bash "$SCRIPT" "$FIXTURE_EMPTY" > /dev/null 2>&1; then pass "exits 0 as a no-op when agent-author/agent-audit/skill-author aren't present" else fail "exited non-zero when the kyberforge skills are simply absent" fi # --- 3. Exits 1 against a REPO_ROOT that doesn't exist --- echo "" echo "--- exits 1 when REPO_ROOT does not exist ---" if bash "$SCRIPT" "/nonexistent/path/$(date +%s)-$$" > /dev/null 2>&1; then fail "exited 0 for a nonexistent REPO_ROOT — expected exit 1" else pass "exits non-zero for a nonexistent REPO_ROOT" fi # --- 4. Regression guard: reintroducing the $HOME-collapse bug into # validate.sh's detect_scope must make the check fail. Builds a minimal # REPO_ROOT (just the four scripts, at their real relative paths) so this # doesn't depend on — or risk mutating — the real repo tree. make_minimal_repo_root() { local dir dir="$(mktemp -d)" local na="$dir/plugins/kyberforge/.apm/skills/agent-author/scripts" local ns="$dir/plugins/kyberforge/.apm/skills/skill-author/scripts" local aa="$dir/plugins/kyberforge/.apm/skills/agent-audit/scripts" mkdir -p "$na" "$ns" "$aa" cp "$REPO_ROOT/plugins/kyberforge/.apm/skills/agent-author/scripts/new-agent.sh" "$na/" cp "$REPO_ROOT/plugins/kyberforge/.apm/skills/skill-author/scripts/new-skill.sh" "$ns/" cp "$REPO_ROOT/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate.sh" "$aa/" cp "$REPO_ROOT/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate-provenance.sh" "$aa/" # agent-author's templates are needed by new-agent.sh at runtime. cp -R "$REPO_ROOT/plugins/kyberforge/.apm/skills/agent-author/assets" "$dir/plugins/kyberforge/.apm/skills/agent-author/" cp -R "$REPO_ROOT/plugins/kyberforge/.apm/skills/skill-author/assets" "$dir/plugins/kyberforge/.apm/skills/skill-author/" # validate.sh needs field-inventory.md mkdir -p "$dir/plugins/kyberforge/.apm/skills/agent-audit/references" cp "$REPO_ROOT/plugins/kyberforge/.apm/skills/agent-audit/references/field-inventory.md" \ "$dir/plugins/kyberforge/.apm/skills/agent-audit/references/" echo "$dir" } echo "" echo "--- exits 1 when validate.sh's detect_scope collapses back to the \$HOME-walk-up bug ---" FIXTURE_BUG="$(make_minimal_repo_root)" FIXTURES+=("$FIXTURE_BUG") python3 - "$FIXTURE_BUG/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate.sh" <<'PYTHON' import re, sys path = sys.argv[1] with open(path) as f: content = f.read() # Revert to the pre-fix collapsed logic: both the $HOME-boundary case and the # filesystem-root fallback return 'user', home unconditionally. old = """def detect_scope(start_dir): home = os.path.expanduser('~') original_start = os.path.abspath(start_dir)""" assert old in content, "detect_scope signature not found — validate.sh has changed shape" buggy = '''def detect_scope(start_dir): home = os.path.expanduser('~') current = os.path.abspath(start_dir) while True: apm_yml = os.path.join(current, 'apm.yml') if os.path.isfile(apm_yml) and find_apm_package_root(apm_yml): return 'plugin', current if current == home: return 'user', home if os.path.exists(os.path.join(current, '.git')): return 'project', current parent = os.path.dirname(current) if parent == current: return 'user', home current = parent ''' # Replace the whole function body up to (but not including) the next # top-level `agent_dir = ` assignment that calls it. pattern = re.compile(r"def detect_scope\(start_dir\):\n.*?\n(?=agent_dir = )", re.DOTALL) assert pattern.search(content), "could not isolate detect_scope's full body" content = pattern.sub(buggy + "\n", content) with open(path, 'w') as f: f.write(content) PYTHON if bash "$SCRIPT" "$FIXTURE_BUG" > /tmp/check-scope-walkup-sync-buggy.out 2>&1; then fail "exited 0 against a validate.sh reverted to the \$HOME-collapse bug — expected exit 1" else pass "exits non-zero when validate.sh's detect_scope regresses to the \$HOME-collapse bug" fi echo "" echo "--- exits 1 when validate-provenance.sh's find_plugin_root loses its \$HOME boundary check ---" FIXTURE_BUG2="$(make_minimal_repo_root)" FIXTURES+=("$FIXTURE_BUG2") python3 - "$FIXTURE_BUG2/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate-provenance.sh" <<'PYTHON' import re, sys path = sys.argv[1] with open(path) as f: content = f.read() # Drop the `if current == home: return None` line — reverts to the pre-fix # behavior of never checking a $HOME boundary at all. pattern = re.compile(r"\n *# \$HOME is a non-plugin-scope boundary.*?\n *if current == home:\n *return None\n", re.DOTALL) assert pattern.search(content), "could not find the \\$HOME boundary check to remove" content = pattern.sub("\n", content) with open(path, 'w') as f: f.write(content) PYTHON if bash "$SCRIPT" "$FIXTURE_BUG2" > /tmp/check-scope-walkup-sync-buggy2.out 2>&1; then fail "exited 0 against a validate-provenance.sh with no \$HOME boundary check — expected exit 1" else pass "exits non-zero when validate-provenance.sh's find_plugin_root loses its \$HOME boundary check" fi echo "" echo "Results: $PASS passed, $FAIL failed" [[ $FAIL -eq 0 ]]