#!/usr/bin/env bash
# Confines a git hook invocation to build.slice — full protection chain, not
# just the bare slice (§7 ANNOYANCE_FATIGUE.md — git-hook escapee seam).
# Generalizes /home/user/Projects/platform/scripts/cpu-limit.sh's generic
# (non-turbo) core so every repo's hooks get the same protection platform's
# own scripts already have. Safe as a personal ~/.claude/bin dependency
# because git hooks only run on the interactive dev workstation, never under
# a CI runner's (possibly different, restricted) OS user.
#
# Fail-open throughout: already in build.slice, no XDG_RUNTIME_DIR, no
# systemd-run, no lock available, etc. all degrade to running the hook
# unwrapped or with fewer protections — never blocks the hook.
#
# Usage: git-hook-confine.sh <real-hook-path> "$@"   (stdin passed through)
#
# Tunables (same names/semantics as cpu-limit.sh):
#   BUILD_CPU_RESERVE=N   cores to reserve for the rest of the system (default 8)
#   BUILD_NODE_HEAP=4096  Node --max-old-space-size MiB (default 4096)
#   BUILD_NO_CAP=1        bypass all caps (debug / idle-machine fast run)
#   BUILD_NO_LOCK=1       skip the machine-wide build-serialization lock
#   BUILD_SCHED_IDLE=0    keep normal scheduling (escape hatch)
set -euo pipefail

real_hook="$1"; shift

in_build_slice() {
  grep -q '/build.slice' /proc/self/cgroup 2>/dev/null
}

if [ ! -x "$real_hook" ]; then
  exit 0  # no real hook installed at this path -> same as absent, don't block
fi

if in_build_slice || [ -n "${BUILD_NO_CAP:-}" ] || [ -z "${XDG_RUNTIME_DIR:-}" ] || ! command -v systemd-run >/dev/null 2>&1; then
  exec "$real_hook" "$@"
fi

# Serialize heavy runs machine-wide (parallel worktrees, CI runner, agent
# sessions triggering hooks concurrently). BUILD_LOCK_HELD stops a nested
# invocation (e.g. a hook that itself shells out to a build) from deadlocking.
if [ -z "${BUILD_NO_LOCK:-}" ] && [ -z "${BUILD_LOCK_HELD:-}" ] && command -v flock >/dev/null 2>&1; then
  lock_file="${XDG_RUNTIME_DIR:-/tmp}/.build-confine.lock"
  if { [ -e "$lock_file" ] && [ -w "$lock_file" ]; } || [ -w "$(dirname "$lock_file")" ]; then
    exec 9>>"$lock_file"
    if ! flock -n 9; then
      echo "[git-hook-confine] waiting for machine-wide build lock ($lock_file)" >&2
      flock 9
    fi
    export BUILD_LOCK_HELD=1
  fi
fi

total=$(nproc 2>/dev/null || echo 4)
all=$(nproc --all 2>/dev/null || echo "$total")
reserve=${BUILD_CPU_RESERVE:-8}
if [ "$total" -lt "$all" ]; then
  limit=$total
else
  limit=$((total - reserve))
fi
if [ "$limit" -lt 1 ]; then limit=1; fi
last=$((limit - 1))

heap=${BUILD_NODE_HEAP:-4096}
export NODE_OPTIONS="${NODE_OPTIONS:-} --max-old-space-size=${heap}"
export UV_THREADPOOL_SIZE="${UV_THREADPOOL_SIZE:-4}"
export GOMAXPROCS="${BUILD_GOMAXPROCS:-$limit}"
export VITEST_MAX_FORKS="${BUILD_VITEST_MAX_FORKS:-$limit}"
export VITEST_MAX_THREADS="${BUILD_VITEST_MAX_THREADS:-$limit}"

cmd=(systemd-run --user --scope --collect --quiet
     --slice=build.slice
     -p CPUQuota="${limit}00%"
     -p CPUWeight=50
     -p IOWeight=50)

command -v nice >/dev/null 2>&1 && cmd+=(nice -n 19)
command -v ionice >/dev/null 2>&1 && cmd+=(ionice -c 3)
if [ "${BUILD_SCHED_IDLE:-1}" != "0" ] && command -v chrt >/dev/null 2>&1 && chrt --idle 0 true 2>/dev/null; then
  cmd+=(chrt --idle 0)
fi
if [ "$limit" -lt "$total" ] && command -v taskset >/dev/null 2>&1; then
  cmd+=(taskset -c 0-"$last")
fi

exec "${cmd[@]}" -- "$real_hook" "$@"
