From 399ae4cb72c710cc811e99ae44a405e5b62cceaa Mon Sep 17 00:00:00 2001 From: Anthony Sligar Date: Tue, 28 Jul 2026 22:07:06 -0400 Subject: [PATCH] fix(ci): pin the ruff version AND state the lint rule set explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main is currently RED on CI, and no commit caused it. The lint gate runs `ruff check src tests examples` after `pip install -e ".[dev]"`, where the extra declared an open-ended `ruff>=0.1`. CI therefore installs whatever ruff shipped most recently, and `[tool.ruff]` set only line-length/target-version — so the ENFORCED RULE SET was whatever that version happened to default to. ruff 0.16.0 widened those defaults (I/RUF/SIM/PL/UP). The result: 40 findings appeared at once — 17 import-sort (I001), 7 RUF022, plus SIM/PL/UP — in files no open PR had touched, retroactively failing work that was green when written. Verified by running 0.16.0 against unmodified `main`: 40 errors, the same failure seen on PR #8, which is therefore blocked by this and not by its own diff. Two changes, because there are two independent causes: - `ruff>=0.5,<0.17` — bound the tool on both sides so the gate is reproducible. Floor 0.5 because the rule set below uses the `[tool.ruff.lint]` table. - `[tool.ruff.lint] select = ["E4","E7","E9","F"]` — state the enforced set explicitly so it is a property of this repo, not of the installed ruff. This is the load-bearing half: without it, the next default-widening release moves the gate again regardless of any version pin. These four are what the project has actually been enforcing (ruff's historical default). Adopting import sorting or the RUF/SIM/UP families is a deliberate choice for its own commit, with its ~40 fixes reviewed — not a side effect of a dependency resolving forward. No source file is touched here. Verified: ruff 0.16.0 (what CI installs) and local ruff 0.15.15 BOTH report "All checks passed" against this commit — the point is that they now agree. pytest 1654 passed; the one failure (test_version_matches_package_metadata) is a stale editable install on the dev host, reproduces on unmodified main, and CI installs fresh. Co-Authored-By: Claude Opus 5 (1M context) --- pyproject.toml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8f9d1d9..fbcc2df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,13 @@ classifiers = [ dependencies = ["pydantic>=2.5", "pyyaml>=6.0"] [project.optional-dependencies] -dev = ["pytest>=7.4", "ruff>=0.1", "hypothesis>=6.0"] +# ruff is bounded on BOTH sides on purpose. CI installs this extra fresh on every +# run, so an open-ended floor means the lint gate silently adopts whatever ruff +# shipped that morning — which is exactly how main went red: 0.16.0 widened the +# DEFAULT rule set (I/RUF/SIM/PL/UP) and 40 pre-existing findings appeared in +# files nobody had touched. The floor is 0.5 because the rule set below uses the +# `[tool.ruff.lint]` table; the ceiling keeps a new minor from moving the gate again. +dev = ["pytest>=7.4", "ruff>=0.5,<0.17", "hypothesis>=6.0"] [project.scripts] dndwright = "dndwright.cli:main" @@ -47,3 +53,15 @@ testpaths = ["tests"] [tool.ruff] line-length = 100 target-version = "py310" + +[tool.ruff.lint] +# State the enforced rule set EXPLICITLY rather than inheriting ruff's defaults. +# Inheriting means the gate's meaning is a property of whichever ruff CI happened +# to install, so a release that widens the defaults retroactively fails commits +# that were green when written — with findings in files the change never touched. +# These four are what this project has actually been enforcing (ruff's historical +# default): pyflakes plus the pycodestyle errors that catch real mistakes. +# Adopting more (import sorting `I`, `RUF`, `SIM`, `UP`) is a deliberate choice to +# make on its own, with the resulting fixes in their own commit — not a side effect +# of a dependency resolving forward. +select = ["E4", "E7", "E9", "F"]