Pre-commit guard gaps: 500KB warn tier, lower block threshold, and first-submission binary detection
Problem
The pre-commit hook's large-file check blocks only files over 10 MB (hooks/pre-commit, the du -k / $1 > 10240 check). There is no warning tier below that, and no binary detection at all.
This let a real incident through: two 9.68 MB plain-text capture files were committed to a public repo and shipped in every release tag across several minor versions before anyone noticed. They passed the gate by ~3% (9,916 KB < 10,240 KB). Cleanup required a history rewrite on a public repo with signed tags.
The failure modes the current check cannot catch:
- Just-under-threshold files: 9.9 MB sails through a 10 MB gate silently — no warning, no prompt, nothing in the commit output.
- Privacy-heavy small files: a 2 MB filesystem manifest is as much of an exposure as a 10 MB one; size alone was never the real risk signal.
- Binaries: a first-time binary submission (image, archive, database, compiled artifact) is usually either an accident or something that deserves a deliberate decision (LFS, gitignore, allowlist) — the hook says nothing.
Proposed solution
Tiered size guard plus first-submission binary detection, all in hooks/pre-commit:
- Warn tier at >500 KB (default): staged files over 500 KB print a prominent warning with the file name and size, but the commit proceeds. The warning is the point — the incident above would have been caught by a human reading two lines of output.
- Block tier lowered to >5 MB (default): same behavior as today's block (exit 1, suggest LFS / gitignore / allowlist), threshold halved. Files that genuinely belong at that size go through the existing
.repokit-allowlist bypass.
- First-submission binary detection: for staged files that are newly added (
--diff-filter=A), detect binary content and warn. Detection is free — git diff --cached --numstat prints - - instead of line counts for binary blobs, so no file reads, no extra processes, and it uses git's own binary heuristic rather than a homegrown one.
Example output:
! Large file staged: tests/captures/big-output.txt (9,916 KB > 500 KB warn threshold)
If this is deliberate, consider private/ (untracked) or .repokit-allowlist.
! New binary file staged: docs/images/diagram.png (git reports binary content)
First-time binaries deserve a deliberate decision: LFS, .gitignore, or allowlist.
Configuration
Defaults should be overridable per-repo without editing the hook (the hook ships via subtree; local edits leak upstream — see #5). Simplest mechanism consistent with what exists: read optional limits from a .repokit-limits file in repo root (same comment/blank-line conventions as .repokit-allowlist):
# .repokit-limits -- optional; defaults apply when absent
warn_kb = 500
block_kb = 5120
binary_warn = true
.repokit-allowlist continues to bypass both size tiers and the binary warning for listed paths — one escape hatch, already documented, already understood.
Design considerations
- Warn-not-block at the low tier is deliberate. A 500 KB block would fight legitimate workflows (fixtures, snapshots, vendored assets) and train people to bypass the hook. A warning costs nothing and would have caught the motivating incident.
- Binary detection only on first submission (
--diff-filter=A) keeps noise at zero for repos that already track binaries — modifying an existing tracked binary stays silent.
git diff --cached --numstat is the right detector: it reflects exactly what git will store, respects .gitattributes text/binary overrides, and is one process for the whole staged set (matching the hook's existing one-grep-for-everything performance posture).
- Thresholds are opinions, not physics — 500 KB / 5 MB are proposed defaults; the config file is the pressure valve. GitHub's own UI warns at 50 MB and blocks at 100 MB, which is far too late for the privacy-exposure class of mistake this guard is really for.
- The size check must keep using the NUL-delimited staged list (paths with spaces) — the existing
xargs -0 du -k pattern already handles this; the new tiers slot into the same pipeline.
Acceptance criteria
Related issues
Pre-commit guard gaps: 500KB warn tier, lower block threshold, and first-submission binary detection
Problem
The pre-commit hook's large-file check blocks only files over 10 MB (
hooks/pre-commit, thedu -k/$1 > 10240check). There is no warning tier below that, and no binary detection at all.This let a real incident through: two 9.68 MB plain-text capture files were committed to a public repo and shipped in every release tag across several minor versions before anyone noticed. They passed the gate by ~3% (9,916 KB < 10,240 KB). Cleanup required a history rewrite on a public repo with signed tags.
The failure modes the current check cannot catch:
Proposed solution
Tiered size guard plus first-submission binary detection, all in
hooks/pre-commit:.repokit-allowlistbypass.--diff-filter=A), detect binary content and warn. Detection is free —git diff --cached --numstatprints- -instead of line counts for binary blobs, so no file reads, no extra processes, and it uses git's own binary heuristic rather than a homegrown one.Example output:
Configuration
Defaults should be overridable per-repo without editing the hook (the hook ships via subtree; local edits leak upstream — see #5). Simplest mechanism consistent with what exists: read optional limits from a
.repokit-limitsfile in repo root (same comment/blank-line conventions as.repokit-allowlist):.repokit-allowlistcontinues to bypass both size tiers and the binary warning for listed paths — one escape hatch, already documented, already understood.Design considerations
--diff-filter=A) keeps noise at zero for repos that already track binaries — modifying an existing tracked binary stays silent.git diff --cached --numstatis the right detector: it reflects exactly what git will store, respects.gitattributestext/binary overrides, and is one process for the whole staged set (matching the hook's existing one-grep-for-everything performance posture).xargs -0 du -kpattern already handles this; the new tiers slot into the same pipeline.Acceptance criteria
--diff-filter=A) binary file prints a first-submission warning; modified existing binaries stay silent.repokit-allowlistentries bypass all three checks.repokit-limitsoverrides the default thresholds and can disable the binary warning; absent file means defaultsRelated issues