Skip to content

fix(lint): pin the ruff rule set, and clear what it selects - #56

Merged
ProtocolWarden merged 1 commit into
mainfrom
claude/clear-ruff-findings
Aug 3, 2026
Merged

fix(lint): pin the ruff rule set, and clear what it selects#56
ProtocolWarden merged 1 commit into
mainfrom
claude/clear-ruff-findings

Conversation

@ProtocolWarden

Copy link
Copy Markdown
Owner

CI was red with 201 findings that no code change caused.

This repo carries no ruff configuration at all, so its rule set is whatever the installed ruff happens to default to — and CI installs it unpinned (ruff>=0.5). ruff 0.16 widened its defaults, CI picked up 0.16.1, and a green repo went red on rules the project never opted into.

Measured, not assumed

ruff selection findings
0.15.13 no config (its defaults) 0
0.16.1 --select E4,E7,E9,F (documented default set) 0
0.16.1 no config (its new defaults) 201

Every one of the 201 came from the default set expanding underneath us. Custodian hit the same class of failure from the other side in its adapters, where a global 0.16.1 shadowed a pinned 0.15.13 and produced 1222 phantom findings against a gate no venv could fix.

The fix

State the intent once, in [tool.ruff.lint].select, where a future release cannot revise it. The selection is the defaults this repo has always passed, plus nine rules adopted deliberately — each mechanical, auto-fixable, and cleared in this same commit rather than left as a promise:

I001, UP017, UP035, UP037, UP045, RUF022, RUF023, FURB188, PLR0402

112 fixes applied by ruff check --fix across 37 files — mostly datetime.timezone.utcdatetime.UTC and import ordering.

The one risky class, checked

FURB188 (slice → removeprefix) is the only autofix here that can change behaviour — the two differ when the prefix is absent. All four sites were the guarded form:

-  target = raw[2:] if raw.startswith("./") else raw
+  target = raw.removeprefix("./")

which removeprefix reproduces exactly.

Rules deliberately NOT selected

Listed in the config with a reason each, rather than silently absent:

  • BLE001, S110/S112, PLC0415, E402 — mostly deliberate patterns already annotated at their sites (best-effort discovery, lazy imports, the conftest venv guard that must run before anything loads)
  • B008 — a straight false positive for Typer, whose entire CLI surface is built from typer.Option(...) defaults
  • DTZ*, PLW1510, TRY004, RUF059 — real signals worth adopting later, but each needs judgement per site, and adopting one means clearing every hit in that pass rather than sprinkling suppressions

Verification

  • 0 findings under both ruff 0.15.13 and 0.16.1 with the new config
  • Suite 16 failed / 488 passed — identical to before the autofix, diffed by name rather than counted
  • Custodian audit 0 findings
  • CI's floor moves 0.50.15 so every selected code is known. It's a floor, not a pin — the config is what stops the drift now.

No log entry

.console/log.md is at exactly 400/400, so any entry trips RC1 and fails the audit. That the convention to log every change and the gate capping the log are now mutually exclusive here is itself the argument in Custodian ADR 0001, which recommends rationale live in the commit message. It does.

🤖 Generated with Claude Code

CI was red with 201 findings that no code change caused. This repo carries no
ruff configuration at all, so its rule set is whatever the installed ruff
happens to default to -- and CI installs it unpinned (`ruff>=0.5`). ruff 0.16
widened its defaults, CI picked up 0.16.1, and a green repo went red on rules
the project never opted into.

Measured rather than assumed: ruff 0.16.1 with `--select E4,E7,E9,F` (its own
documented default set) reports ZERO findings here, exactly as 0.15.13 does with
no config. Every one of the 201 came from the default set expanding underneath
us. Custodian hit the same class of failure from the other side in its adapters,
where a global 0.16.1 shadowed a pinned 0.15.13 and produced 1222 phantom
findings against a gate no venv could fix.

So the fix is to state the intent once, in `[tool.ruff.lint].select`, where a
future release cannot revise it. The selection is the defaults this repo has
always passed, plus nine rules adopted deliberately -- each mechanical,
auto-fixable, and cleared in this same commit rather than left as a promise:
I001, UP017, UP035, UP037, UP045, RUF022, RUF023, FURB188, PLR0402.

112 fixes applied by `ruff check --fix` across 37 files, the bulk of it
`datetime.timezone.utc` -> `datetime.UTC` and import ordering. The one class
that can change behaviour is FURB188; all four sites were the guarded form
(`if s.startswith(p): s = s[len(p):]`), which `removeprefix` reproduces exactly.
Suite is unchanged at 16 failed / 488 passed, and the 16 are identical by name
before and after -- diffed, not counted.

The rules NOT selected are listed in the config with a reason each, rather than
being silently absent. BLE001, S110/S112, PLC0415 and E402 are mostly deliberate
patterns already annotated at their sites; B008 is a straight false positive for
Typer, whose whole CLI surface is built from `typer.Option(...)` defaults.
DTZ*, PLW1510, TRY004 and RUF059 are real signals worth adopting later, but each
needs judgement per site and adopting one means clearing every hit in that pass.

CI's floor moves 0.5 -> 0.15 so every selected code is known to the installed
ruff. It is a floor, not a pin: the config is what stops the drift now.

No log entry: `.console/log.md` is at exactly 400/400, so any entry trips RC1 and
fails the audit. That the convention to log every change and the gate capping the
log are now mutually exclusive here is itself the argument in Custodian ADR 0001,
which recommends rationale live in the commit message. It does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ProtocolWarden
ProtocolWarden merged commit a0f03b9 into main Aug 3, 2026
7 checks passed
@ProtocolWarden
ProtocolWarden deleted the claude/clear-ruff-findings branch August 3, 2026 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant