Research digest — Pre-commit config audit (2026-05-30)¶
Companion ADR: ADR-0893 Branch: chore/pre-commit-audit Audited file: .pre-commit-config.yaml Working tree: /tmp/wt-precommit (worktree on origin/master @ 544299fae1)
1. Audit method¶
- Read every
repo:/localblock in.pre-commit-config.yaml, listed eachid:with its pinned revision and anyargs:/exclude:/files:/stages:overrides. - For upstream repos: ran
pre-commit autoupdateagainst a clean worktree, captured proposed bumps, then independently verified each viagit ls-remote --tags --refs <repo>because autoupdate has a known sort-order bug on repos that release point versions out of branch order. - For
localhooks: verified eachentry:resolves to a real, executable script (scripts/ci/*.sh,scripts/git-hooks/*.sh) and that anylanguage: systemhook's binary is present on the dev box (mypy,semgrep,gh). - Re-ran
pre-commit run --all-filesagainst the edited config to confirm no new violations.
2. Inventory + drift per hook¶
| Repo / id | Pinned rev | Latest stable | Δ class | Action |
|---|---|---|---|---|
pre-commit/pre-commit-hooks/trailing-whitespace | v6.0.0 | v6.0.0 | none | keep |
pre-commit/pre-commit-hooks/end-of-file-fixer | v6.0.0 | v6.0.0 | none | already present (one of user-listed hygiene hooks) |
pre-commit/pre-commit-hooks/check-merge-conflict | v6.0.0 | v6.0.0 | none | already present (PR #182 wired it) |
pre-commit/pre-commit-hooks/check-yaml | v6.0.0 | v6.0.0 | none | already present |
pre-commit/pre-commit-hooks/check-json | v6.0.0 | v6.0.0 | none | keep |
pre-commit/pre-commit-hooks/check-toml | v6.0.0 | v6.0.0 | none | keep |
pre-commit/pre-commit-hooks/check-added-large-files | v6.0.0 | v6.0.0 | none | keep (maxkb=1024) |
pre-commit/pre-commit-hooks/mixed-line-ending | v6.0.0 | v6.0.0 | none | already present |
pre-commit/pre-commit-hooks/detect-private-key | v6.0.0 | v6.0.0 | none | already present |
pre-commit/pre-commit-hooks/forbid-new-submodules | — (absent) | v6.0.0 | missing | add — fork uses Meson wraps + ffmpeg-patches/, never .gitmodules; supply-chain guard |
pre-commit/mirrors-clang-format/clang-format | v22.1.5 | v22.1.5 | none | keep |
psf/black | 26.5.1 | 26.5.1 | none | keep |
PyCQA/isort/isort | 5.13.2 | 8.0.1 (latest); 6.0.1 (conservative) | stale | bump 5.13.2 → 6.0.1 (autoupdate proposed 9.0.0a3 alpha; rejected) |
astral-sh/ruff-pre-commit/ruff-check | v0.15.13 | v0.15.15 | patch behind | bump v0.15.13 → v0.15.15 |
scop/pre-commit-shfmt/shfmt-src | v3.13.1-1 | v3.13.1-1 | none | keep |
shellcheck-py/shellcheck-py/shellcheck | v0.11.0.1 | v0.11.0.1 | none | keep |
gitleaks/gitleaks/gitleaks | v8.30.1 | v8.30.1 | none (autoupdate falsely proposes v8.30.0) | keep v8.30.1 — autoupdate downgrade rejected |
compilerla/conventional-pre-commit/conventional-pre-commit | v4.4.0 | v4.4.0 | none | keep |
local/agent-worktree-drift-guard | script | n/a | script exists + executable | keep |
local/check-copyright | script | n/a | script exists + executable | keep |
local/check-adr-numbering | script | n/a | script exists + executable | keep |
local/assertion-density | script | n/a | script exists + executable | keep |
local/mypy-local | language: system | mypy on PATH | binary present | keep |
local/semgrep-local | language: python (additional_deps semgrep>=1.78,<2.0) | semgrep on PATH | binary present; pre-existing io_uring fan-out failure | defer to PR #340 (ADR-0867) |
local/ffmpeg-patches-apply-check | script | n/a | script exists + executable | keep |
local/validate-pr-body | script | n/a | script exists + executable | keep |
3. Why each bump or non-bump¶
isort 5.13.2 → 6.0.1 (chosen over 9.0.0a3 / 8.0.1)¶
9.0.0a3is an alpha pre-release; project hygiene pins stable releases only.- 8.0.1 is the latest stable but is two majors away with multiple
profile = "black"interaction changes in 7.x → 8.x; auditing those is a separate PR (and a separate ADR). - 6.0.1 has been the de facto stable line since ~2024 H2, brings Python 3.13 / 3.14 parsing fixes the fork needs (we're on Py 3.14 per ADR-0691 era container pins), and surfaces one latent import-grouping fix in
tools/vmaf-tune/tests/test_codec_adapter_av1_videotoolbox.pythat isort 5.13.2 was silently ignoring.
ruff-pre-commit v0.15.13 → v0.15.15¶
- Patch-level bump; no rule selection changes that affect the fork's
[tool.ruff.lint]set. pre-commit run --all-filesconfirms zero new findings.
gitleaks: keep v8.30.1 (reject autoupdate's v8.30.0)¶
git ls-remote --tags --refs https://github.com/gitleaks/gitleaksshows v8.30.1 ranks above v8.30.0 lexicographically and chronologically — v8.30.1 is the latest tag.- pre-commit's autoupdate tag-sort heuristic mis-handles point releases that land out of the default branch's commit order; documented upstream as a long-standing wart.
forbid-new-submodules (new)¶
- Cost: ~0 ms at commit time (no files match in the default tree).
- Benefit: catches a
git submodule addbefore it lands, which would bypass: subprojects/*.wrappinning (Meson wraps with sha256s),- the SBOM machinery (CycloneDX scan walks wraps, not
.gitmodules), - and the licence-allow-list audit.
- Fits between
detect-private-keyand the next repo block, with an inline comment explaining the dependency posture.
4. PR-conflict avoidance¶
- PR #340 (
fix(ci): semgrep-local pre-commit hook serial execution, ADR-0867) edits thesemgrep-locallocal hook to addrequire_serial: true. This audit does NOT touch that block. - PR #342 (
chore(lint): wire markdownlint-cli2 into make lint + pre-commit + CI, ADR-0866) inserts a newmarkdownlint-cli2repo block betweenshellcheck-pyandgitleaks. This audit does NOT touch that file region nor insert any block in that range.
5. Verification — pre-commit run --all-files (post-edit)¶
trim trailing whitespace........................................................Passed
fix end of files................................................................Passed
check for merge conflicts.......................................................Passed
check yaml......................................................................Passed
check json......................................................................Passed
check toml......................................................................Passed
check for added large files.....................................................Passed
mixed line ending...............................................................Passed
detect private key..............................................................Passed
forbid new submodules.......................................(no files to check)Skipped
clang-format....................................................................Passed
black...........................................................................Passed
isort...........................................................................Passed
ruff check......................................................................Passed
shfmt...........................................................................Passed
shellcheck......................................................................Passed
Detect hardcoded secrets........................................................Passed
ADR-0332 agent worktree-drift guard.............................................Passed
ADR-0105 copyright header present (C/C++/CUDA)..................................Passed
ADR-0386 ADR number collision + heading consistency check.......................Passed
semgrep (.semgrep.yml — local rules, error-gated)..............................Skipped (SKIP=semgrep-local; PR #340 issue)
All hooks green except semgrep-local, which fails identically on master tip (io_uring/RLIMIT_MEMLOCK exhaustion under pre-commit's per-CPU fan-out — fixed by PR #340 / ADR-0867, not by this audit). A direct semgrep scan --config=.semgrep.yml --error against the worktree exits 0 with 0 findings, confirming the rules themselves are clean and the failure is purely the pre-commit fan-out shape.
6. Reproducer¶
git worktree add -b chore/pre-commit-audit-repro /tmp/wt-pre-audit origin/master
cd /tmp/wt-pre-audit
# Show current pinned revs
grep -E '^\s+(repo|rev):' .pre-commit-config.yaml
# Show autoupdate's (sometimes wrong) suggestions
pre-commit autoupdate
# Verify gitleaks tag order manually
git ls-remote --tags --refs https://github.com/gitleaks/gitleaks \
| grep -E 'v8\.30\.' | sort -V
# Apply the audit deltas (the three rev bumps + forbid-new-submodules)
# … then …
pre-commit run --all-files
# Expect every hook PASS or SKIP except semgrep-local
# (pre-existing io_uring bug fixed by PR #340).
7. Rebase impact¶
None against upstream Netflix — .pre-commit-config.yaml is fork-only. The two coordination points are in-flight fork PRs (#340, #342) and are handled by NOT touching their file regions.
8. Follow-ups¶
- Re-audit
isort7.x → 8.x bump as a separate PR once #340 and #342 land (separate diff-with-blame on the changed default rules). - Consider adding
pyupgradeandadd-trailing-commato the hygiene chain (deferred — would need an ADR for the default-arg-style change those rules enforce across the Python tree).