---
name: pre_commit_git_guardrails
description: Set up portable pre-commit git guardrails for a repo worked on by agent fleets: secret scanning, lint gates, commit-message linting, speed budgets that keep agents from disabling the hooks, and bypass auditing so skipped hooks get caught. Triggers when someone asks for pre-commit hooks, git guardrails, commit hygiene, secret-leak prevention, or hook setups that survive autonomous coding agents.
---

# Pre-commit Git Guardrails

## Purpose

Give any repo a guardrail setup that works for humans and for fleets of coding agents: pre-commit hooks that catch secrets, lint failures, and bad commit messages before they land, fast enough that agents do not reach for `--no-verify`, and audited so that bypasses get noticed instead of slipping through.

The recipe is portable. It does not depend on any single AI harness, editor, or agent framework. Same config file, same hooks, same checks in CI.

## Workflow

1. **Survey the repo.** Note the languages in play (Python, JS/TS, Go, shell), the package managers, and where secrets already live (`.env` files, `.env.example` presence). Keep the guardrail set to what the repo actually contains.
2. **Draft the config.** Copy `assets/guardrail-config.yaml` to `.pre-commit-config.yaml` and trim it to the repo's languages. Follow `references/guardrail-recipe.md` for rev pins and hook choices. Pin every `rev` to a verified tag, never a branch.
3. **Apply the speed budget.** Assign every hook to a stage: commit (fast, seconds), pre-push (slower), CI/manual (the rest). Read `references/speed-budget.md` before adding anything to the commit stage. If a hook cannot run inside the commit-stage budget, it moves to pre-push or CI, no exceptions.
4. **Wire bypass auditing.** Add the guardrail trailer hook and the audit script per `references/bypass-audit.md`. Client-side hooks are advisory; the audit and the CI parity checks are what make them stick.
5. **Install and verify.** `pre-commit install`, plus `pre-commit install --hook-type commit-msg --hook-type pre-push` for the message and push stages. Then run the verification below. Do not call the setup done until all three pass.

## Output Contract

A finished guardrail setup has:

- `.pre-commit-config.yaml` at the repo root, all revs pinned, every hook assigned an explicit `stages` list.
- Secret scanning on the commit stage (gitleaks or detect-secrets) with a committed allowlist/baseline file.
- A commit-msg stage linter enforcing Conventional Commits (`<type>[scope]: <subject>`).
- A prepare-commit-msg trailer hook stamping `Guardrails: v1 <config-hash>` on each commit.
- `bin/audit_guardrails.py` (copy from this skill's `bin/`) runnable in CI, failing on commits missing the trailer.
- CI parity: the same guardrails re-run as required checks on the default branch, plus the audit job.
- Measured timings: every hook's elapsed time recorded at least once, all inside the budget from `references/speed-budget.md`.

## Operating Rules

1. **Speed is a security property.** A hook that takes 90 seconds will be disabled with `--no-verify` by an agent under time pressure, and then you have no guardrails at all. Budget first, enforce second.
2. **Client-side hooks never stand alone.** `--no-verify`, `git -c core.hooksPath=/dev/null`, and direct edits to `.git/hooks` all bypass local hooks. Treat them as a courtesy layer; enforcement lives in CI and on the server side.
3. **Never gate on unpinned or floating hook versions.** Pin `rev` to a tag. Re-pin on a schedule with `pre-commit autoupdate`, review the diff, and run the audit after updating.
4. **Fail closed on secrets, fail open on lint.** A scanner error on the secret hook blocks the commit until a human looks. A linter crashing on a weird file must not brick the fleet: keep lint hooks narrow (`files:`/`types:` filters) and let `--no-verify` remain a documented emergency escape for lint only, with the audit flagging each use.
5. **Verify with real runs.** Minimum verification:
   - `pre-commit run --all-files` passes on a clean tree.
   - A staged file containing a fake AWS key is blocked by the secret hook (use a gitleaks test fixture, never a real secret).
   - `git commit -m "wip stuff"` is rejected by the commit-msg hook; `git commit -m "chore: wip stuff"` passes and carries the trailer.
   - `bin/audit_guardrails.py --since HEAD~5` reports the trailer on every commit.

## Verification quickstart (dry run)

```bash
# 1. install
pip install pre-commit && pre-commit install \
  && pre-commit install --hook-type commit-msg --hook-type pre-push

# 2. stage a fake secret, confirm the block (fixture only)
echo 'AKIAIOSFODNN7EXAMPLE' > /tmp/fixture.txt   # gitleaks test key, safe
git add /tmp/fixture.txt && pre-commit run gitleaks   # expect: Failed

# 3. time the commit stage
time pre-commit run   # every hook prints its own elapsed time; sum must fit the budget

# 4. audit recent history for bypasses
python3 bin/audit_guardrails.py --since origin/main
```
