Why
pithead runs a file-budget ratchet (scripts/lint-file-budget.sh + docs/dev/file-budget.tsv, its
#1105 Phase 0) that stops its biggest files growing without demanding anyone rewrite them today.
rigforge has no equivalent, and it is already carrying files in the range where pithead started
hurting.
Measured on develop:
| lines |
file |
| 8,674 |
tests/run.sh |
| 6,013 |
rigforge.sh |
| 1,386 |
tests/e2e-real.sh |
| 563 |
tests/e2e-pithead.sh |
3 files over 800 lines, 4 over 400. No gate, no budget file.
This is not a tidiness request. pithead learned the cost twice, and both are ahead of rigforge on the
same curve:
make lint-sh gets OOM-killed as one file grows. shellcheck was using over 7 GB on pithead's
tests/stack/run.sh and took whole sessions down with it; later the same pathology reached CI and
blocked every PR until the shellcheck invocation was split. tests/run.sh at 8,674 lines is in
that territory now.
- Retrofitting is far more expensive than ratcheting. pithead is ~20 PRs into splitting one test
monolith by domain, and the coupling that surfaced (shared sandbox state, order-dependent
assertions) cost several red suites to find. A ceiling recorded today costs nothing; a split
demanded in six months costs a quarter.
What to build
Port the mechanism, not necessarily the numbers:
- New tracked file: hard fail over 800 lines (target 400).
- Existing offender: record its CURRENT line count in a budget file as a personal ceiling. A PR
may not grow the file past its ceiling. Ceilings only ever go DOWN — reject any budget edit
that raises one, checked against the base branch. A file that shrinks to <=400 drops its entry.
- Enumerated exemptions, each with its reason — generated code, vendored files, data/config,
prose docs. rigforge.sh is the shipped artifact and is the obvious candidate for the same
treatment pithead gives its own pithead script; decide deliberately rather than by accident.
- Wire it into
make lint and CI, and give it a --self-test with fixtures for each failure
mode, plus a --generate that reprints the budget for seeding.
Traps pithead already paid for — inherit these, don't rediscover them
- The ratchet leaves ZERO slack the instant a shrinking cut merges (ceiling == actual), so a
follow-on PR that adds even 20 lines is refused and the ceiling cannot be raised to make room.
Before routing around this, ask whether a line-neutral form of the change exists — that
question dissolved the problem on pithead when three sessions had already tried to carry an
addition past the gate. Only if there genuinely is none, fold the addition into the shrinking cut's
own commit (never a follow-on PR), and weigh that a move PR's contract is "no behaviour change".
- Do not add a fallback that permits a recorded ceiling above the actual count. It leaves slack
any later change can consume, in a guard whose whole purpose is that slack does not accumulate.
- Regenerate the budget file per-lane; never merge it by taking a side. On pithead it is
regenerated, and a stale entry two lines out was caught only because the gate compared against the
real count.
- Enumerate exemptions by path with reasons, and detect binaries generically (
grep -I) rather
than listing them, or the exemption list becomes its own maintenance problem.
Acceptance
make lint fails on a new 801-line file and on growing an existing offender past its recorded
ceiling; passes when a file shrinks; refuses a budget edit that raises a ceiling; --self-test covers
each of those plus the empty-enumeration guard; CI runs it on every PR.
Why
pithead runs a file-budget ratchet (
scripts/lint-file-budget.sh+docs/dev/file-budget.tsv, its#1105 Phase 0) that stops its biggest files growing without demanding anyone rewrite them today.
rigforge has no equivalent, and it is already carrying files in the range where pithead started
hurting.
Measured on
develop:tests/run.shrigforge.shtests/e2e-real.shtests/e2e-pithead.sh3 files over 800 lines, 4 over 400. No gate, no budget file.
This is not a tidiness request. pithead learned the cost twice, and both are ahead of rigforge on the
same curve:
make lint-shgets OOM-killed as one file grows. shellcheck was using over 7 GB on pithead'stests/stack/run.shand took whole sessions down with it; later the same pathology reached CI andblocked every PR until the shellcheck invocation was split.
tests/run.shat 8,674 lines is inthat territory now.
monolith by domain, and the coupling that surfaced (shared sandbox state, order-dependent
assertions) cost several red suites to find. A ceiling recorded today costs nothing; a split
demanded in six months costs a quarter.
What to build
Port the mechanism, not necessarily the numbers:
may not grow the file past its ceiling. Ceilings only ever go DOWN — reject any budget edit
that raises one, checked against the base branch. A file that shrinks to <=400 drops its entry.
prose docs.
rigforge.shis the shipped artifact and is the obvious candidate for the sametreatment pithead gives its own
pitheadscript; decide deliberately rather than by accident.make lintand CI, and give it a--self-testwith fixtures for each failuremode, plus a
--generatethat reprints the budget for seeding.Traps pithead already paid for — inherit these, don't rediscover them
follow-on PR that adds even 20 lines is refused and the ceiling cannot be raised to make room.
Before routing around this, ask whether a line-neutral form of the change exists — that
question dissolved the problem on pithead when three sessions had already tried to carry an
addition past the gate. Only if there genuinely is none, fold the addition into the shrinking cut's
own commit (never a follow-on PR), and weigh that a move PR's contract is "no behaviour change".
any later change can consume, in a guard whose whole purpose is that slack does not accumulate.
regenerated, and a stale entry two lines out was caught only because the gate compared against the
real count.
grep -I) ratherthan listing them, or the exemption list becomes its own maintenance problem.
Acceptance
make lintfails on a new 801-line file and on growing an existing offender past its recordedceiling; passes when a file shrinks; refuses a budget edit that raises a ceiling;
--self-testcoverseach of those plus the empty-enumeration guard; CI runs it on every PR.