Skip to content

build(ruff): pin lint.select to CI's rule set + stale-noqa audit (#246)#267

Merged
pbean merged 2 commits into
mainfrom
chore/ruff-lint-select-align
Jul 23, 2026
Merged

build(ruff): pin lint.select to CI's rule set + stale-noqa audit (#246)#267
pbean merged 2 commits into
mainfrom
chore/ruff-lint-select-align

Conversation

@pbean

@pbean pbean commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Part of #246 (lint-alignment half; the env-var registry PR — F-9b — remains a separate follow-up, so this intentionally does NOT auto-close the issue).

What & why

pyproject.toml had no [tool.ruff.lint] table, so local ruff ran whatever default its version ships while CI's trunk runs a pinned ruff@0.15.17. Trunk's ruff plugin (v1.10.2) injects no --select — it runs ruff check and lets ruff resolve config from our pyproject.toml. With no lint table that resolves to ruff's default set, so a local ruff of a different version can diverge: local green ≠ CI green.

Changes

1. Pin the rule set (mirror CI, no ratcheting)

[tool.ruff.lint]
select = ["E4", "E7", "E9", "F"]

This reproduces ruff's default byte-for-byte — verified with ruff check --show-settings (0.15.17): the enabled-rule set is identical (59 rules) with and without the explicit select. Nothing stricter than what CI already enforces.

2. Stale-noqa audit (issue title)

Used ruff's own RUF100 (unused-noqa) as an oracle over the pinned set. It flagged 59 directives in src/ plus the ANN001/E301 directives in tests/ as referencing non-enabled codes (BLE001, S603/S607, PLC0415, ANN001, B027, E301) — inert suppressions accreted from past reviews.

  • Removed the stale ruff codes, preserving every # nosec bandit suppression (bandit is a separate, enabled trunk linter — count unchanged, 31 → 31) and the design rationale on each except Exception seam-translation site.
  • Kept the 15 F401 re-export pins and the E402/F811 directives (all enabled codes) — RUF100 flagged none, confirming each is load-bearing. The verify.py/install.py/runsetup.py/cli.py:40-54 re-exports were not touched (the F401-autofix trap was avoided: --no-fix throughout).
  • RUF100 is intentionally not added to select (that would ratchet stricter than CI); it was used only as an audit oracle.

Result: ruff check --extend-select RUF100 is clean across src/ + tests/; every surviving # noqa references an enabled code.

Verification

  • trunk check --all — 238 files, no issues (ruff / black / isort / bandit all green).
  • Full test suite — 2766 passed, 1 skipped. The 2 test_module_skills_sync failures are pre-existing on clean main (.agents/.claude module.yaml drift), unrelated to this change.

No CHANGELOG entry (internal tooling config).

Summary by CodeRabbit

  • Chores

    • Standardized linting configuration and removed outdated inline lint-suppression annotations.
    • Preserved existing error handling, cleanup behavior, diagnostics, and plugin workflows.
  • Tests

    • Updated test code to align with the streamlined linting rules without changing test behavior.

CI lints with trunk's ruff (pinned 0.15.17), whose plugin injects no
`--select`, so it runs ruff's own default rule set resolved from our
config. With no [tool.ruff.lint] table, a local `ruff` of a different
version could enforce a different default — local green != CI green.

Pin `select = ["E4", "E7", "E9", "F"]`, which reproduces that default
byte-for-byte (verified via `ruff check --show-settings`: identical
enabled-rule set), so local == CI regardless of ruff version. No
ratcheting beyond what CI already enforces.

Stale-noqa audit: using RUF100 (unused-noqa) as an oracle over the
pinned set, 59 directives in src/ plus the ANN001/E301 directives in
tests/ referenced non-enabled codes (BLE001, S603/S607, PLC0415,
ANN001, B027, E301) — inert suppressions from past reviews. Removed
the stale ruff codes while preserving every `# nosec` bandit
suppression and the design rationale on each seam-translation site.
Kept the F401 re-exports and E402/F811 directives (enabled codes),
all verified load-bearing (RUF100 flagged none). RUF100 now clean
across src/ + tests/.

Refs #246
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@pbean, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f90c8597-6529-4023-a925-f0006d28150a

📥 Commits

Reviewing files that changed from the base of the PR and between c45e4c0 and fc77daf.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • pyproject.toml

Walkthrough

Ruff lint selection is now explicit and aligned with CI. Inline lint suppressions were removed or clarified across runtime modules and tests, while exception handling, subprocess calls, lazy imports, and test behavior remain unchanged.

Changes

Lint configuration and adapter cleanup

Layer / File(s) Summary
Ruff policy and adapter cleanup
pyproject.toml, src/bmad_loop/adapters/*, src/bmad_loop/process_host.py, src/bmad_loop/install.py, src/bmad_loop/resolve.py
Ruff selects E4, E7, E9, and F; related inline suppressions were removed without changing runtime behavior.
Best-effort exception paths
src/bmad_loop/diagnostics.py, src/bmad_loop/engine.py, src/bmad_loop/plugins/*, src/bmad_loop/runsetup.py, src/bmad_loop/tui/*, src/bmad_loop/{probe,recovery_flow,runs,stories_engine,sweep,verify}.py
Broad-exception suppression comments were removed or clarified while existing fallback, logging, and failure-isolation behavior remains intact.
Test lint cleanup
tests/test_hook_bus.py, tests/test_plugin_tea.py, tests/test_plugin_workflows.py
Test-only function-definition suppressions were removed without changing assertions or control flow.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: dracic

Poem

A bunny trims the noqa leaves,
While Ruff keeps watch in tidy eaves.
The code hops on, its paths unchanged,
Its tests stay calm, its style rearranged.
“Clean lint!” I cheer, and wiggle my ears.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the Ruff rule-set pinning and stale-noqa audit changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/ruff-lint-select-align

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@pbean

pbean commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyproject.toml`:
- Around line 64-69: Update the Ruff configuration comment near select to avoid
claiming exact CI parity across arbitrary Ruff versions. Either pin the Ruff
executable through the repository’s existing tooling, or revise the comment to
state only that the selected rule families match CI; preserve select = ["E4",
"E7", "E9", "F"] unless the executable pin requires a related configuration
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fe832653-da08-4b5b-8c92-05578e15f57b

📥 Commits

Reviewing files that changed from the base of the PR and between 750c14a and c45e4c0.

📒 Files selected for processing (27)
  • pyproject.toml
  • src/bmad_loop/adapters/base.py
  • src/bmad_loop/adapters/generic.py
  • src/bmad_loop/adapters/multiplexer.py
  • src/bmad_loop/adapters/opencode_http.py
  • src/bmad_loop/data/plugins/tea/tea_plugin.py
  • src/bmad_loop/data/plugins/unity/unity_plugin.py
  • src/bmad_loop/data/plugins/unity/unity_teardown.py
  • src/bmad_loop/diagnostics.py
  • src/bmad_loop/engine.py
  • src/bmad_loop/install.py
  • src/bmad_loop/plugins/bus.py
  • src/bmad_loop/plugins/registry.py
  • src/bmad_loop/probe.py
  • src/bmad_loop/process_host.py
  • src/bmad_loop/recovery_flow.py
  • src/bmad_loop/resolve.py
  • src/bmad_loop/runs.py
  • src/bmad_loop/runsetup.py
  • src/bmad_loop/stories_engine.py
  • src/bmad_loop/sweep.py
  • src/bmad_loop/tui/app.py
  • src/bmad_loop/tui/widgets.py
  • src/bmad_loop/verify.py
  • tests/test_hook_bus.py
  • tests/test_plugin_tea.py
  • tests/test_plugin_workflows.py

Comment thread pyproject.toml Outdated
Address CodeRabbit review on #267: `select` pins rule families, not ruff's
implementation version, so the old comment's "any version enforces exactly
what CI enforces" overclaimed. Reword to scope exact parity to the repo's
pinned ruff — `trunk check` (trunk 0.15.17, what CI runs and the documented
local lint) and the uv.lock-resolved 0.15.17 — and note a bare newer ruff
gets rule-family parity only (new rules can land under these prefixes).

Also raise the dev-dependency floor `ruff>=0.4` -> `ruff>=0.15.17` so the
uv-path pin is explicit rather than relying on lock resolution; the resolved
version is unchanged (uv.lock stays 0.15.17, specifier-only diff). Selected
rule set is unchanged (verified via `ruff check --show-settings`).

Refs #246
@pbean
pbean merged commit cd12526 into main Jul 23, 2026
10 checks passed
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