Files
claude/lib/tests/run-review-guards.sh
Bastien Chanot 4e83f39a70 test(guards): add anti-partial-fix regression guards (fil rouge)
lib/tests/run-review-guards.sh — 5 whole-surface guards that RED if a banned
pattern subsists anywhere, auto-run by make test (run-*.sh glob):
  G1 trailer (A1), G2 false CLAUDE.md attribution (A5), G3 strict-YAML frontmatter
  (A4), G4 reconcile hermeticity (job3 B1), G5 hook-drift installed==emit (A2).
This is the check that would have caught A1/A4/A5/A2 at make-test time instead of
an adversarial review — the series' recurring failure was fixing one instance and
leaving twins. G3/G5 degrade to SKIP if pyyaml/emit-hook absent (portability).
Teeth verified: a planted trailer in a real agent REDs G1. Review fil rouge.
2026-07-08 15:48:51 +02:00

78 lines
3.6 KiB
Bash

#!/usr/bin/env bash
# run-review-guards.sh — anti-"partial-fix" regression guards.
#
# Genesis: .audit/review-release-1.0.0.md fil rouge. The 9-job series repeatedly
# fixed ONE instance of a banned pattern and left the twins (A1 trailer, A4 YAML,
# A5 false attribution, A2 hook drift). Each guard below greps the WHOLE surface
# for a pattern and REDs if any occurrence subsists — the check that would have
# caught A1/A4/A5/A2 at make-test time instead of an adversarial review.
set -uo pipefail
GREP=/usr/bin/grep # LRN-074: pin grep
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
cd "$REPO"
pass=0; fail=0; skip=0
ok() { echo "GREEN ✓ $*"; pass=$((pass+1)); }
no() { echo "RED ✗ $*"; fail=$((fail+1)); }
warn() { echo "SKIP ~ $*"; skip=$((skip+1)); }
echo "=== review-guards: anti-partial-fix surface checks ==="
# G1 — banned commit-attribution trailers must not live in our own config surface
# (the ban is [[no-commit-attribution]]; skills-external/ = gstack submodule, excluded).
# This guard file is excluded: it names the pattern literally as its own search term.
if hits=$($GREP -rInE --exclude=run-review-guards.sh 'Co-Authored-By|Claude-Session' agents/ lib/ hooks/ templates/ skills/ 2>/dev/null); then
echo "$hits"; no "G1 trailer: banned attribution trailer present in tracked config surface"
else
ok "G1 trailer: zero Co-Authored-By/Claude-Session in agents|lib|hooks|templates|skills"
fi
# G2 — false CLAUDE.md attribution (asserting a user policy CLAUDE.md does not contain)
if hits=$($GREP -rInE 'per user.{0,5}CLAUDE\.md|User CLAUDE\.md default' agents/ skills/ 2>/dev/null); then
echo "$hits"; no "G2 attribution: false 'per user CLAUDE.md' policy reference present"
else
ok "G2 attribution: zero false CLAUDE.md policy references in agents|skills"
fi
# G3 — every agent frontmatter must be strict-YAML valid (degrade if pyyaml absent)
if python3 -c 'import yaml' 2>/dev/null; then
if python3 - "$REPO" <<'PY'
import glob, os, sys, yaml
root=sys.argv[1]; bad=0
for f in sorted(glob.glob(os.path.join(root,'agents','*.md'))):
try: yaml.safe_load(open(f).read().split('---')[1])
except Exception as e: print(" FAIL", os.path.relpath(f,root), str(e).splitlines()[0]); bad+=1
sys.exit(1 if bad else 0)
PY
then ok "G3 strict-YAML: all agents/*.md frontmatter parse"
else no "G3 strict-YAML: an agent frontmatter fails yaml.safe_load"
fi
else
warn "G3 strict-YAML: python3+pyyaml unavailable — skipped"
fi
# G4 — the reconcile test must stay hermetic (fixtures, never the live registry) [job3 B1]
if $GREP -q '\.claude/memory' lib/tests/run-reconcile.sh 2>/dev/null; then
no "G4 hermetic: run-reconcile.sh reads the live .claude/memory registry"
else
ok "G4 hermetic: run-reconcile.sh reads fixtures only, not the live registry"
fi
# G5 — installed pre-commit hook must match the generator (catches the A2 silent drift:
# editing _gitflow_emit_pre_commit without re-installing). Degrade if emit-hook absent.
if emitted=$(bash lib/gitflow.sh emit-hook 2>/dev/null) && [ -n "$emitted" ]; then
if [ -f .githooks/pre-commit ] && diff -q <(printf '%s\n' "$emitted") .githooks/pre-commit >/dev/null 2>&1; then
ok "G5 hook-drift: installed .githooks/pre-commit == generator emit-hook"
else
no "G5 hook-drift: installed hook diverges from generator (run 'gitflow.sh install-hook')"
fi
else
warn "G5 hook-drift: gitflow.sh emit-hook unavailable — skipped"
fi
echo
echo "================ $pass GREEN / $fail RED / $skip SKIP (review-guards) ================"
[ "$fail" -eq 0 ]