Skip to content

Pre-commit hook

Driven by husky. Runs every quality gate in parallel before any commit lands locally.

Source .husky/pre-commit

Phase 1 — Prettier autoformat

Runs first and auto-fixes whitespace / quote style / trailing commas. If anything changes, the gate cache key is recomputed after this step so cosmetic fixes don't invalidate the cache.

Phase 2 — 15 gates in parallel

The hook spawns each gate as a background process and waits for them all. Cache key per gate is git write-tree (the SHA of the staged tree); when the same SHA passes a gate, the next commit on the same content set skips it.

# Gate Hook label npm script
1 TypeScript tsc type-check
2 ESLint eslint lint (eslint + architecture + canaries + format:check)
3 Biome biome biome lint src --max-diagnostics=50
4 npm audit audit npm audit --omit=dev
5 Phase isolation lint:phases:strict eslint src/Tests/Unit/Pipeline/CrossValidation/Phases --max-warnings 0
6 Architecture architecture lint:architecture src/Scrapers/Pipeline
7 Build build lint + tsup
8 Canaries canaries lint:canaries
9 Dead code dead-code lint:dead-code
10 Guideline coverage guideline-coverage lint:guideline-coverage (asserts eslint.config.mjs enforces CLEAN_CODE.md caps for every Pipeline cluster)
11 Docs strict docs-strict lint:docs-strict (only fires when docs/**, root *.md, or mkdocs.yml is staged; runs mkdocs build --strict — soft-skips when Python/mkdocs not on PATH locally, CI is the hard gate)
12 Docs coverage docs-coverage .github/scripts/ci/docs-coverage.sh (fires when any src/Scrapers/Pipeline/**/*.ts is staged; diffs new public exports against origin/main/main and fails if a new symbol is undocumented + un-allowlisted — soft-skips when base ref unresolvable locally, CI is the hard gate)
13 Pipeline tests + coverage test:pipeline test:pipeline
14 Bank tests bank-tests test:e2e-factory-tests
15 Mock suite test:mock test:mock

Total wall-clock: 3-5 minutes on a modern laptop (everything is parallelised; the gate that takes the longest gates the whole run).

Reading the output

🛑  PIPELINE QUALITY GATE
══════════════════════════════════════════════════════
📝 Phase 1: Prettier format...
🔑 cache key: ba898e775bb13de1049bfb2ee4753b6d48954b1e

⚡ Phase 2: All gates parallel...
  ❌ tsc FAILED
  ✅ eslint passed
  ❌ biome FAILED
  ✅ audit passed
  ...
❌ FAILED GATES: tsc biome

The summary at the end names every failing gate. Detail logs are written to .pre-commit-output.log at repo root (overwritten each run).

Why these particular 15?

Gate Role
tsc + eslint + biome Static correctness — catches type errors and rule violations before they reach review
audit Supply-chain hygiene — fails on known CVEs in dependencies
architecture + canaries + lint:phases:strict + dead-code Architectural invariants — fails when a PR reaches across a layer boundary, breaks a canary fixture, or leaves a dead export
guideline-coverage Process invariant — fails when eslint.config.mjs drifts from CLEAN_CODE.md canonical caps
docs-strict Docs build correctness — fails on broken internal links / missing pages that would break mkdocs --strict on CI
docs-coverage Docs/code consistency — fails when a new src/Scrapers/Pipeline/ export ships without a docs/ mention or allowlist entry
build Produces the actual lib/ ESM + CJS bundle, ensuring tsup can reach a green state
test:pipeline + bank-tests + test:mock Functional regression — every PR proves all tests still pass and coverage hits the gate

Skipping the hook (don't)

The husky hook can be bypassed with --no-verify. Don't. The CI re-runs the same gates and will reject the PR anyway, wasting your iteration time.

What happens on hook failure

The commit is not created — git status still shows your changes staged. Fix the issues, re-stage, retry. There's no half-state to clean up.