---
title: "feat: Activate pre-commit hooks (black, ruff, isort)"
type: feat
status: active
date: 2026-04-24
---

# Activate Pre-commit Hooks

## Overview

`.pre-commit-config.yaml` exists but is explicitly disabled ("intentionally minimal and does not enable hooks by installing them"). Activating black, ruff, and isort would enforce CODE_STYLE.md conventions automatically and eliminate style-only review comments.

## Problem Frame

- Code style is documented in CODE_STYLE.md but not enforced automatically
- Contributors may submit PRs with inconsistent formatting
- Review time is spent on style nits instead of logic
- No CI check for formatting violations

## Requirements Trace

- R1. Pre-commit hooks run black, ruff, and isort
- R2. Hooks are enforced in CI (fail build on violations)
- R3. Hooks use the same versions as pyproject.toml dev dependencies
- R4. Initial run reformats existing code without breaking tests

## Scope Boundaries

**Included:**
- Update `.pre-commit-config.yaml`
- Add CI workflow step for pre-commit
- Run initial format across codebase

**Excluded:**
- Adding new linters or rules
- Changing CODE_STYLE.md conventions
- Fixing logic bugs found by ruff (separate PR)

## Key Technical Decisions

- **Use pre-commit.ci or GitHub Action** — pre-commit.ci is zero-config but may not work on Gitea. Use a GitHub Actions step as fallback.
- **Single large format commit** — Run once, commit formatting changes separately from config changes so reviewers can see the diff.
- **Skip tests during format** — Formatting should not change behavior, but run tests after to verify.

## Implementation Units

- [ ] U1. **Update .pre-commit-config.yaml**

**Goal:** Enable black, ruff, and isort with versions matching pyproject.toml.

**Requirements:** R1, R3

**Dependencies:** None

**Files:**
- Modify: `.pre-commit-config.yaml`

**Approach:**
- Remove the "does not enable hooks" comment
- Add repos for black, ruff, isort with pinned versions
- Set ruff to match CODE_STYLE.md rules
- Configure isort profile (black-compatible)

**Test scenarios:**
- Happy path: `pre-commit run --all-files` completes successfully
- Error path: A file with style violations fails the hook
- Integration: Versions match pyproject.toml dev deps

**Verification:**
- `pre-commit run --all-files` runs without config errors

---

- [ ] U2. **Add pre-commit CI step**

**Goal:** Block PRs that violate formatting rules.

**Requirements:** R2

**Dependencies:** U1

**Files:**
- Modify: `.github/workflows/pytest.yml` (or create separate lint.yml)

**Approach:**
- Add a job that runs `pre-commit run --all-files`
- Use the same uv setup as the pytest workflow
- Install pre-commit via uv

**Test scenarios:**
- Happy path: Clean code passes pre-commit CI
- Error path: Violations fail the CI job

**Verification:**
- Pushing a formatting violation fails the check
- Pushing clean code passes

---

- [ ] U3. **Run initial format across codebase**

**Goal:** Bring all existing code into compliance so future PRs only touch their own changes.

**Requirements:** R4

**Dependencies:** U1

**Files:**
- Modify: All Python files (mechanical reformatting)

**Approach:**
- Run `pre-commit run --all-files`
- Commit formatting changes separately
- Run full test suite: `uv run pytest tests/ -q`

**Execution note:** This is a mechanical change. Characterization tests should pass unchanged. If tests fail, the formatter broke something — investigate before committing.

**Test scenarios:**
- Integration: All existing tests pass after formatting
- Edge case: No logic changes introduced by formatting

**Verification:**
- `uv run pytest tests/ -q` passes
- `git diff` shows only whitespace/import changes

---

## Risks & Dependencies

| Risk | Mitigation |
|------|------------|
| Massive format commit obscures git blame | Use `.git-blame-ignore-revs` to ignore the format commit |
| Ruff finds existing logic issues | Fix or suppress in separate PR; don't mix with activation |
| Contributors without pre-commit installed | CI catches it; add setup note to README |

## Documentation / Operational Notes

- Add pre-commit setup to README quickstart
- Document `.git-blame-ignore-revs` usage

## Sources & References

- Config: `.pre-commit-config.yaml`
- Style guide: `CODE_STYLE.md`
- Dependencies: `pyproject.toml`
