diff --git a/kitchen/patches/D2.md b/kitchen/patches/D2.md new file mode 100644 index 0000000..a904c68 --- /dev/null +++ b/kitchen/patches/D2.md @@ -0,0 +1,20 @@ +# D2 · `@deltoid-fence` · IP fence smoke + +**When:** 2026-09-05T22:10:10Z +**Scope:** enforce `docs/IP-FENCE.md` via local smoke (no push). + +## Done + +1. Added `scripts/smoke/ip_fence_check.sh` — scans `scripts/`, optional `game/`, `assets/`, kitchen ACTIVITY/outbox/PERSONAS/PLAYBOOK/STATUS/README for Magikoopa|Kamek|\bMario\b|Bowser|OpenKrush (case-insensitive). Allowlists fence docs + README + this patch note. Skips `.git`, `kitchen/raw`, binary media globs. Prints `FENCE PASS` / `FENCE FAIL` + matches; exit 0/1. +2. Added `scripts/smoke/README.md` with fence section (smoke dir was empty; merge-safe if D1 lands later). +3. This summary + ACTIVITY / AGENT_LOG lines. +4. Script run once in session (see AGENT_LOG). + +## Not done + +- No rewrite of host name `magikoopa` in `REPORT-pitchwell.txt` (outside scan set). +- No push. + +## Note + +ACTIVITY voice names the fence without listing banned tokens (first draft tripped the scanner). diff --git a/scripts/smoke/ip_fence_check.sh b/scripts/smoke/ip_fence_check.sh new file mode 100755 index 0000000..5d1d290 --- /dev/null +++ b/scripts/smoke/ip_fence_check.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# IP-FENCE smoke: fail on Nintendo / OpenKrush tokens outside allowlisted docs. +# See docs/IP-FENCE.md. Exit 0 → FENCE PASS; non-zero → FENCE FAIL. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$ROOT" + +# Case-insensitive; word-ish Mario. OpenKrush = forbidden packs. +PATTERN='Magikoopa|Kamek|\bMario\b|Bowser|OpenKrush' + +# Paths to scan (tracked-ish product / kitchen surfaces). Skip .git + kitchen/raw. +SCAN_PATHS=() +[[ -d scripts ]] && SCAN_PATHS+=(scripts) +[[ -d game ]] && SCAN_PATHS+=(game) +[[ -d assets ]] && SCAN_PATHS+=(assets) +[[ -f kitchen/ACTIVITY.md ]] && SCAN_PATHS+=(kitchen/ACTIVITY.md) +[[ -d kitchen/outbox ]] && SCAN_PATHS+=(kitchen/outbox) +# kitchen prose that must stay original (not raw model dumps) +for f in kitchen/PERSONAS.md kitchen/PLAYBOOK.md kitchen/STATUS.md kitchen/README.md; do + [[ -f "$f" ]] && SCAN_PATHS+=("$f") +done + +# Allowlist: fence definition + README stop-line + docs that *ban* the names +allowlisted() { + local f="$1" + case "$f" in + docs/IP-FENCE.md|README.md|docs/FACTIONS.md|docs/CINEMATICS.md|docs/GAMEPLAY.md) return 0 ;; + scripts/smoke/ip_fence_check.sh|scripts/smoke/README.md|kitchen/patches/D2.md) return 0 ;; + *) return 1 ;; + esac +} + +if [[ ${#SCAN_PATHS[@]} -eq 0 ]]; then + echo "FENCE FAIL: no scan paths under $ROOT" + exit 2 +fi + +# Collect matches: path:line:text (rg may be missing → grep -R fallback) +TMP="$(mktemp)" +trap 'rm -f "$TMP"' EXIT + +if command -v rg >/dev/null 2>&1; then + rg -n -i -e "$PATTERN" \ + --glob '!.git/**' \ + --glob '!kitchen/raw/**' \ + --glob '!*.mp4' --glob '!*.png' --glob '!*.jpg' --glob '!*.jpeg' \ + --glob '!*.webp' --glob '!*.bin' --glob '!*.pak' --glob '!*.ogg' \ + --glob '!*.wav' --glob '!*.webm' \ + "${SCAN_PATHS[@]}" >"$TMP" 2>/dev/null || true +else + grep -R -n -I -i -E "$PATTERN" "${SCAN_PATHS[@]}" \ + --exclude-dir=.git --exclude-dir=raw \ + --exclude='*.mp4' --exclude='*.png' --exclude='*.jpg' \ + >"$TMP" 2>/dev/null || true +fi + +FAIL_LINES=() +while IFS= read -r line || [[ -n "${line:-}" ]]; do + [[ -z "$line" ]] && continue + # rg: path:lineno:content + path="${line%%:*}" + # strip leading ./ + path="${path#./}" + if allowlisted "$path"; then + continue + fi + FAIL_LINES+=("$line") +done <"$TMP" + +if [[ ${#FAIL_LINES[@]} -gt 0 ]]; then + echo "FENCE FAIL" + printf '%s\n' "${FAIL_LINES[@]}" + exit 1 +fi + +echo "FENCE PASS" +exit 0