#!/bin/sh set -eu # The source hook fails closed unless rendered; install_git_hooks.py # replaces these placeholders with absolute paths in installed copies. repo=$(git rev-parse --show-toplevel) checker="/home/wait4x/.pi/agent/skills/spec/scripts/check_all_spec_packages.py" # Checker resolution: # 1. SPEC_CHECK_ALL_SCRIPT environment override — the explicit operator # escape hatch; the path in effect is echoed to stderr so a swap is # never silent. # 2. The absolute path rendered by install_git_hooks.py, already held in # $checker when the placeholder above was replaced. Repository content # can never replace the gate checker; an unrendered template fails # closed below. if [ -n "${SPEC_CHECK_ALL_SCRIPT:-}" ]; then checker="$SPEC_CHECK_ALL_SCRIPT" printf '%s\n' "spec gate: using SPEC_CHECK_ALL_SCRIPT override: $checker" >&2 fi if [ ! -f "$checker" ]; then printf '%s\n' \ "error: Spec gate checker not found at: $checker" \ " The checker script is expected at the path above." \ " Fix:" \ " 1. Re-run: python3 scripts/install_git_hooks.py --root \"$repo\"" \ " 2. The checker lives in the spec skill package under scripts/" >&2 exit 1 fi python="/home/wait4x/miniconda3/bin/python3.13" python_placeholder_prefix=__SPEC_PYTHON_ python_placeholder="${python_placeholder_prefix}PLACEHOLDER__" if [ "$python" = "$python_placeholder" ]; then python=${SPEC_PYTHON:-$(command -v python3 || true)} fi if [ -z "$python" ]; then printf '%s\n' "error: Spec gate requires a trusted Python interpreter" >&2 exit 1 fi if [ "$python" != "${python##*/}" ] && [ ! -x "$python" ]; then printf '%s\n' "error: Spec gate requires a trusted Python interpreter" >&2 exit 1 fi # Branch-aware gate routing (AUD-07): the full gate guards the irreversible # paths — commits on the main branch (the merge destination) and commits # carrying archive paths on any branch. In-flight WIP commits on spec/* # integration branches get the structural lint only; the full gate # re-engages at the main-branch merge (pre-push + push_spec_package). # The channel is decided by objective Git facts (branch name, staged # paths), never by a committer-declared footer. branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true) case "$branch" in spec/*) if git diff --cached --name-only --diff-filter=ACMRT 2>/dev/null | grep -q "specs/archive/"; then : # Archive-carrying commits keep the full gate on every branch. else printf '%s\n' \ "spec gate: WIP commit on integration branch $branch — structural lint only;" \ "the full gate runs for main-branch and archive-carrying commits" >&2 exec "$python" "$checker" --root "$repo" --lint-only fi ;; esac baseline="" for ref in refs/heads/main refs/heads/master; do if baseline=$(git rev-parse --verify "$ref" 2>/dev/null); then break fi baseline="" done set -- if [ -n "$baseline" ]; then set -- --legacy-baseline "$baseline" fi "$python" "$checker" --root "$repo" "$@" "$python" "$checker" --root "$repo" --index "$@"