#!/usr/bin/env bash # pre-commit — enforces breakpilot-compliance structural guardrails. # # 1. Blocks commits that introduce a non-test, non-generated source file > 500 LOC. # 2. Blocks commits that touch backend-compliance/migrations/ unless the commit message # contains the marker [migration-approved] (last-resort escape hatch). # 3. Blocks edits to .claude/settings.json, scripts/check-loc.sh, or # .claude/rules/loc-exceptions.txt unless [guardrail-change] is in the commit message. # # Bypass with --no-verify is intentionally NOT supported by the team workflow. # CI re-runs all of these on the server side anyway. set -euo pipefail REPO_ROOT="$(git rev-parse --show-toplevel)" mapfile -t staged < <(git diff --cached --name-only --diff-filter=ACM) [[ ${#staged[@]} -eq 0 ]] && exit 0 # 1. LOC budget on staged files. loc_targets=() for f in "${staged[@]}"; do [[ -f "$REPO_ROOT/$f" ]] && loc_targets+=("$REPO_ROOT/$f") done if [[ ${#loc_targets[@]} -gt 0 ]]; then if ! "$REPO_ROOT/scripts/check-loc.sh" "${loc_targets[@]}"; then echo echo "Commit blocked: file-size budget violated. See output above." echo "Either split the file (preferred) or add an exception with rationale to" echo " .claude/rules/loc-exceptions.txt" exit 1 fi fi # 2. Migration directories are frozen unless explicitly approved. if printf '%s\n' "${staged[@]}" | grep -qE '(^|/)(migrations|alembic/versions)/'; then if ! git log --format=%B -n 1 HEAD 2>/dev/null | grep -q '\[migration-approved\]' \ && ! grep -q '\[migration-approved\]' "$(git rev-parse --git-dir)/COMMIT_EDITMSG" 2>/dev/null; then echo "Commit blocked: this change touches a migrations directory." echo "Database schema changes require an explicit migration plan reviewed by the DB owner." echo "If approved, add '[migration-approved]' to your commit message." exit 1 fi fi # 3. Guardrail files are protected. guarded='^(\.claude/settings\.json|\.claude/rules/loc-exceptions\.txt|scripts/check-loc\.sh|scripts/githooks/pre-commit|AGENTS\.(python|go|typescript)\.md)$' if printf '%s\n' "${staged[@]}" | grep -qE "$guarded"; then if ! grep -q '\[guardrail-change\]' "$(git rev-parse --git-dir)/COMMIT_EDITMSG" 2>/dev/null; then echo "Commit blocked: this change modifies guardrail files." echo "If intentional, add '[guardrail-change]' to your commit message and explain why in the body." exit 1 fi fi exit 0