Lint had never been part of the gate. No [tool.ruff] section, ruff not a dependency, 103 findings against inherited defaults. A repository that ships a linter should not fail its own — it now reports zero.
The defect the gate surfaced
doctor walks a directory twice: once to discover candidate logs, once to judge them. The judging pass already reported what it could not read — "could not be parsed and were NOT judged".
The discovery pass did not. A file that raised there was swallowed by except Exception: pass, never became a candidate, and so never reached that note.
The result: a file plainly visible on disk, absent from the report, and indistinguishable from one that passed. That is precisely the failure NOT-CHECKED was introduced to prevent, sitting one loop earlier than anyone had looked.
Both passes now feed the same note. The regression test was verified to fail without the fix before it was kept — a test that passes either way proves nothing.
Two other broad handlers were narrowed, not removed. A non-JSON line inside a JSONL log, and a {-prefixed line that is not valid JSON, are expected inputs and skipping them is correct — but they now catch json.JSONDecodeError specifically, so a genuine fault in the surrounding code can no longer disguise itself as an unparseable line.
The ruleset is chosen, not inherited
E, F, I, B, plus RUF013 (an implicit Optional is a type hint that lies), RUF059, and S110 (try-except-pass).
Adopting everything ruff reports would make this codebase worse, because several of its opinions contradict deliberate decisions. Every exclusion carries its reason in pyproject.toml:
BLE001(blind except) — trainproof catches broad exceptions on purpose when parsing logs it did not write and probing subprocesses that can die in ways Python cannot describe. A narrowexceptthere would let an unforeseen parser error escape as a traceback instead of exit code 2, "cannot judge".S110is enforced instead: catching broadly is fine, catching and passing is not.PLW1510(subprocess withoutcheck) — every subprocess call inspectsreturncodeitself and turns it into a finding.check=Truewould raise, which is the opposite of the required behaviour.E501(line length) — most long lines are the evidence strings trainproof prints, asserted in tests and encoded byte-for-byte in the golden snapshots. Reflowing them would risk changing the tool's output to satisfy a ruler.
Verification
- Zero ruff findings, enforced in the release ritual
- 228 → 230 tests
- All 38 golden snapshots byte-identical
scripts/regenerate_evidence.py --checkexits 0
A cleanup that moves a verdict is not a cleanup.
pip install trainproof==0.17.0