Skip to content

Re-enable ruff BLE (blind-except) rule and fix all violations - #10002

Open
saltas888 wants to merge 14 commits into
developfrom
pha/INBOX-19
Open

Re-enable ruff BLE (blind-except) rule and fix all violations#10002
saltas888 wants to merge 14 commits into
developfrom
pha/INBOX-19

Conversation

@saltas888

@saltas888 saltas888 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Removes BLE from the global ruff ignore list in pyproject.toml and resolves all 78 violations it surfaces (the card's original ~32 estimate was from a February analysis; the count grew — reconciled and re-measured as part of this work, see dev/specs/002-ruff-ble-reenable/data-model.md).
  • 8 sites (test/tooling code) got genuine exception narrowing to the specific type(s) the guarded code can raise.
  • 70 sites kept except Exception with a targeted # noqa: BLE001 plus a one-line justification comment, where a broad catch is a deliberate design choice (best-effort telemetry, top-level migration/error-collection loops, etc.) — annotation-only, no logic or control-flow changes.
  • Full spec-kit trail in dev/specs/002-ruff-ble-reenable/ (spec, plan, critique, tasks, alignment check, final implementation report).

Reviewer note: 24 of the changed files fall in paths this repo normally reviews extra carefully — 19 under backend/infrahub/core/migrations/graph/, 4 auth modules (api/auth.py, api/oauth2.py, api/oidc.py, auth/auth.py), and core/schema/update_coordinator.py. Every touch in those files is the identical mechanical pattern (justification comment + # noqa: BLE001 above an existing except Exception:) — confirmed by an explicit diff-audit task (SC-007) and independently re-verified by the review pass below. No behavior change. Worth a close look given the sensitivity of those paths regardless.

Source: Engineering Inbox INBOX-19, from Patrick's pyproject suppression-analysis thread (ranked BLE re-enable priority #1 — direct bug risk, small bounded count, lowest effort).

Test plan

  • ruff check --select=BLE . — 0 violations repo-wide
  • uv run invoke backend.lint (ruff + ty + mypy) — clean
  • uv run invoke backend.test-unit — 1846 passed
  • Two previously-non-collecting component tests (test_process_idempotency.py, test_uniqueness_propagation.py) confirmed passing — 12 passed (the earlier "no tests collected" signal was a CLI-proxy summarizer false negative, not a real defect)
  • Suppression audit — 70/70 sites carry a justification comment; enforcement canary (reintroducing one unjustified BLE001) confirmed the rule now fails as expected, then reverted
  • uv lock --check, schema.validate-graphqlschema, schema.validate-jsonschema, backend.validate-generated, docs.validate — all clean, zero diff
  • Two independent review passes (error-handling, simplification) across the full diff — no CRITICAL/HIGH findings; one pre-existing/accepted design tradeoff and a few cosmetic nits noted in the implementation report, none blocking

🤖 Generated with Claude Code


Summary by cubic

Re-enabled the ruff BLE (blind-except) rule and resolved all 78 violations by narrowing where safe and justifying intentional broad handlers. Aligns with INBOX-19: remove the ignore, make exceptions explicit, and keep behavior unchanged in sensitive paths.

  • Refactors
    • Removed BLE from pyproject.toml ignore; repo passes ruff with the rule active.
    • Narrowed 8 sites to specific types: SchemaNotFoundError (component tests), httpx.HTTPError (git integration fixtures), packaging.InvalidVersion (tasks/release.py).
    • Kept 70 deliberate except Exception/BaseException handlers with # noqa: BLE001 and a one-line justification (migrations, auth, scheduler, telemetry, message bus, etc.); comments only.
    • Added changelog fragment and spec-kit docs under dev/specs/002-ruff-ble-reenable/ for traceability.
    • No logic or control-flow changes; invoke backend.lint and existing tests remain green.

Written for commit 89e7ec4. Summary will update on new commits.

Review in cubic

saltas888 and others added 13 commits July 22, 2026 07:25
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78-site inventory with per-site treatment matrix (8 narrow / 70 suppress),
research decisions, and validation quickstart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verdict PROCEED, no must-address findings. Applied: PR-narrative note
(plan), rollback + release.py sanity checks (quickstart), changelog
assumption harmonized (spec).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
22 tasks across 3 user stories: 8 parallel suppress batches (US3),
3 parallel narrow batches (US2), config flip + verification (US1),
plus setup drift-check and polish audits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Source PRD resolved: inline card text + Slack suppression-analysis
thread (fetched). No missing/changed/dropped requirements; 0 of 2
remediation passes used.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ciled, no drift (INBOX-19)

Re-ran `uv run ruff check --select=BLE --output-format=concise .` from the
repo root: 78 BLE001 violations (77 `Exception`, 1 `BaseException` at
backend/tests/helpers/test_worker.py:107), byte-identical to the
data-model.md batch tables — zero line drift, zero new sites, zero
removed sites, no python_sdk findings. data-model.md unchanged; only the
T001 checkbox is ticked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tasks T002–T004: annotation-only SUPPRESS treatment for all 30 Batch A
sites per data-model.md — a justification comment above each except plus
a line-targeted `# noqa: BLE001`. Zero semantic tokens changed (SC-007);
m066/m073 and shared.py:157/:245 use the transaction-safe wordings.
The noqas read as RUF100 "non-enabled" until T015 removes BLE from the
ignore list — expected interim state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…NBOX-19)

Tasks T005–T007: annotation-only SUPPRESS treatment for the 8 Batch B
auth sites and 16 Batch C backend-runtime sites per data-model.md — a
justification comment above each except plus a line-targeted
`# noqa: BLE001`. Zero semantic tokens changed; auth diffs are
comment/noqa-only (SC-007). Two flagged sites got noqa only, keeping
their existing justification comments: git/sync.py:120 (lines 121-122)
and telemetry/tasks.py:129 (line 125 already states why broad).
The noqas read as RUF100 "non-enabled" until T015 removes BLE from the
ignore list — expected interim state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…green (INBOX-19)

Batch D (7 backend-test sites) and Batch E (9 tooling sites) receive the
SUPPRESS treatment from data-model.md: a justification comment above each
except plus a line-targeted noqa. Annotation-only — zero semantic tokens
changed; test_worker.py keeps its BaseException catch and the load tester's
latent missing-return stays untouched by design.

US3 checkpoint (T010): remaining BLE001 violations are exactly the 8 NARROW
sites; ruff format clean on all 42 touched files; migrations+auth diff vs
base is mechanically verified comment/noqa-only (SC-007).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…een (INBOX-19)

Narrow the 8 analyzable BLE001 handlers per the data-model treatment
matrix (Batches D/E NARROW rows), handler bodies untouched:

- schema_branch component tests: except Exception -> SchemaNotFoundError
  in the duplicated _describe_hash_diff helper (2 sites per file)
- integration/git/conftest.py poll loops: except Exception -> httpx.HTTPError,
  dropping the now-stale noqa: S110 (typed excepts are S110-exempt)
- tasks/release.py version probes: except Exception -> InvalidVersion,
  extending the deliberate function-local packaging imports

US2 checkpoint: repo BLE001 = 0 (--exclude python_sdk, per the CI lint
gate); ruff format clean; InvalidVersion probe and invoke --list green;
the 12 touched component tests pass locally against the neo4j
testcontainer (12 passed in 65.16s).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…green (INBOX-19)

Phase 5 (US1), tasks T015-T018:
- T015: remove "BLE" from the [tool.ruff.lint] ignore list in pyproject.toml
- T016: towncrier housekeeping fragment for the BLE enforcement
- T017: full gates green — ruff --select=BLE (0, python_sdk excluded:
  initialized submodule is out of scope), full ruff check, ruff format
  --check, invoke backend.lint (ruff + ty + mypy) all exit 0
- T018: canary mutation check — 1 x BLE001 reported on a planted blind
  except in tasks/utils.py, canary reverted, tree clean

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…quickstart replay (INBOX-19)

T019: 70/70 noqa: BLE001 suppressions counted (= data-model.md SUPPRESS total);
68 carry the justification on/above the except line, 2 use the tabled alternate
positions (git/sync.py:120 below-line, telemetry/tasks.py:129 try-block header);
E722 backstop clean.
T020: backend unit suite green — 1846 passed, 17 warnings in 33.63s.
T021: constraint-area diff from feature fork point 9247c67 is annotation-only —
23 files, +76/-38; every addition is a comment or a byte-matched noqa'd except
line (0 violations). Note: the stable merge-base 01a1ab5 predates the fork and
includes unrelated upstream develop work; audit uses the fork parent.
T022: full quickstart replay green (BLE=0, CI gates, backend.lint, canary
mutation check, suppression audit, InvalidVersion probe); component tests cited
from the prior local run (12 passed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…een (INBOX-19)

Verification + review tail for the ruff BLE re-enablement feature. All 22
tasks were already [X] on entry (completed by two prior, killed sessions);
this run independently re-verified every gate rather than trusting the
inherited state, then ran the review phase and wrote the final report.

Re-confirmed fresh: BLE=0 repo-wide (--exclude python_sdk), full
invoke backend.lint (ruff+ty+mypy), full ruff check/format --exclude
python_sdk, E722=0, 70/70 noqa: BLE001 suppressions, SC-007 diff audit
(migrations/auth annotation-only), SC-006 enforcement canary (1 x BLE001,
reverted clean), tasks/release.py InvalidVersion + invoke --list sanity,
full backend unit suite (1846 passed), and the two previously-flagged
component tests (12 passed, 44.33s).

Root-caused the "pytest component-test collection issue" flagged by prior
runs: it was a false negative from the rtk CLI proxy's output summarizer
("Pytest: No tests collected" on a fully passing run), not a real
collection defect — reproduced and confirmed via the raw (unfiltered)
command path. No code change needed.

Ran the error-handling and simplification review agents in parallel across
the full feature diff (9247c67..06fd34d). No CRITICAL/HIGH findings;
2 LOW/MEDIUM findings recorded (one is a pre-existing, deliberately
accepted planning tradeoff already documented in data-model.md/
quickstart.md, not a new defect) plus 3 LOW simplify nits — none block
merge, all deferred per the report.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@saltas888
saltas888 requested a review from a team as a code owner July 22, 2026 10:02

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

3 issues found across 59 files

Confidence score: 3/5

  • In backend/infrahub/core/migrations/graph/m066_consolidate_duplicate_number_pools.py, returning from inside async with db.start_transaction() appears to undermine the intended exception-to-MigrationResult handling, which could misreport or mishandle migration failures and leave migration behavior unpredictable—move the return/error mapping outside the transaction block and verify failure-path tests before merging.
  • In backend/infrahub/core/validators/tasks.py, the broad exception catch can swallow transient errors and bypass the task’s retries=3 behavior, increasing the chance of silent validation failures instead of automatic recovery—narrow the exception types (or add a clear justification) so retryable failures still retry.
  • In utilities/infrahub_load_tester.py, when a request fails all_branches may remain undefined, causing follow-on branch deletion attempts to fail with a misleading secondary error—initialize all_branches on failure or guard the cleanup path so the original failure is reported clearly.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/infrahub/core/validators/tasks.py">

<violation number="1" location="backend/infrahub/core/validators/tasks.py:86">
P2: Per linked Jira issue INBOX-19, sites should either narrow the exception type or provide justification; here the broad catch defeats the task's own retries=3 retry policy by turning every failure (including transient/infra errors) into a permanent violation on the first attempt. Consider narrowing to the specific exceptions `run_constraints` is expected to raise so unexpected/transient errors can propagate and trigger the configured retries.</violation>
</file>

<file name="backend/infrahub/core/migrations/graph/m066_consolidate_duplicate_number_pools.py">

<violation number="1" location="backend/infrahub/core/migrations/graph/m066_consolidate_duplicate_number_pools.py:83">
P1: The new justification comment describes this except as safely converting failures into a `MigrationResult` error, but because the `return` happens inside the `async with db.start_transaction()` block, the exception never propagates to `__aexit__`, so the transaction is committed (not rolled back) on failure — partial pool consolidation/deletion can be persisted while the migration reports an error. Consider re-raising (or moving the try/except outside the `async with` block) so `__aexit__` sees the exception and rolls back before converting it to a `MigrationResult`.</violation>
</file>

<file name="utilities/infrahub_load_tester.py">

<violation number="1" location="utilities/infrahub_load_tester.py:48">
P2: The new justification comment claims this except "absorb[s] any request failure and continue[s]," but `all_branches` is never defined on failure, so every subsequent branch-deletion attempt will fail with a confusing `UnboundLocalError` message instead of cleanly skipping. Consider returning (like `delete_admin_branches` does) or defaulting `all_branches = []` in the except block.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic


except Exception as exc:
# Failures become MigrationResult errors so the runner reports them instead of crashing
except Exception as exc: # noqa: BLE001

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: The new justification comment describes this except as safely converting failures into a MigrationResult error, but because the return happens inside the async with db.start_transaction() block, the exception never propagates to __aexit__, so the transaction is committed (not rolled back) on failure — partial pool consolidation/deletion can be persisted while the migration reports an error. Consider re-raising (or moving the try/except outside the async with block) so __aexit__ sees the exception and rolls back before converting it to a MigrationResult.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/infrahub/core/migrations/graph/m066_consolidate_duplicate_number_pools.py, line 83:

<comment>The new justification comment describes this except as safely converting failures into a `MigrationResult` error, but because the `return` happens inside the `async with db.start_transaction()` block, the exception never propagates to `__aexit__`, so the transaction is committed (not rolled back) on failure — partial pool consolidation/deletion can be persisted while the migration reports an error. Consider re-raising (or moving the try/except outside the `async with` block) so `__aexit__` sees the exception and rolls back before converting it to a `MigrationResult`.</comment>

<file context>
@@ -79,7 +79,8 @@ async def execute(self, migration_input: MigrationInput) -> MigrationResult:
 
-            except Exception as exc:
+            # Failures become MigrationResult errors so the runner reports them instead of crashing
+            except Exception as exc:  # noqa: BLE001
                 error_msg = str(exc) or f"{type(exc).__name__}: {repr(exc)}"
                 return MigrationResult(errors=[error_msg])
</file context>

violations = await aggregated_constraint_checker.run_constraints(constraint_request)
except Exception as exc:
# Degrade any checker failure into a reported violation so schema validation fails visibly instead of crashing the task
except Exception as exc: # noqa: BLE001

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Per linked Jira issue INBOX-19, sites should either narrow the exception type or provide justification; here the broad catch defeats the task's own retries=3 retry policy by turning every failure (including transient/infra errors) into a permanent violation on the first attempt. Consider narrowing to the specific exceptions run_constraints is expected to raise so unexpected/transient errors can propagate and trigger the configured retries.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/infrahub/core/validators/tasks.py, line 86:

<comment>Per linked Jira issue INBOX-19, sites should either narrow the exception type or provide justification; here the broad catch defeats the task's own retries=3 retry policy by turning every failure (including transient/infra errors) into a permanent violation on the first attempt. Consider narrowing to the specific exceptions `run_constraints` is expected to raise so unexpected/transient errors can propagate and trigger the configured retries.</comment>

<file context>
@@ -82,7 +82,8 @@ async def schema_path_validate(
             violations = await aggregated_constraint_checker.run_constraints(constraint_request)
-        except Exception as exc:
+        # Degrade any checker failure into a reported violation so schema validation fails visibly instead of crashing the task
+        except Exception as exc:  # noqa: BLE001
             violation = SchemaViolation(
                 node_id="unknown",
</file context>

log.info(f"✅ Created proposed change for branch {branch_name}")
except Exception as e:
# Load test: absorb any request failure and continue
except Exception as e: # noqa: BLE001

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: The new justification comment claims this except "absorb[s] any request failure and continue[s]," but all_branches is never defined on failure, so every subsequent branch-deletion attempt will fail with a confusing UnboundLocalError message instead of cleanly skipping. Consider returning (like delete_admin_branches does) or defaulting all_branches = [] in the except block.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At utilities/infrahub_load_tester.py, line 48:

<comment>The new justification comment claims this except "absorb[s] any request failure and continue[s]," but `all_branches` is never defined on failure, so every subsequent branch-deletion attempt will fail with a confusing `UnboundLocalError` message instead of cleanly skipping. Consider returning (like `delete_admin_branches` does) or defaulting `all_branches = []` in the except block.</comment>

<file context>
@@ -44,7 +44,8 @@ async def _create_one(idx: int, client: InfrahubClient, prefix: str, log: loggin
         log.info(f"✅ Created proposed change for branch {branch_name}")
-    except Exception as e:
+    # Load test: absorb any request failure and continue
+    except Exception as e:  # noqa: BLE001
         log.error(f"❌ Error creating proposed change for branch {branch_name}: {e}")
 
</file context>

@github-actions github-actions Bot added group/backend Issue related to the backend (API Server, Git Agent) type/spec A specification for an upcoming change to the project labels Jul 22, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".specify/feature.json">

<violation number="1">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

The `.specify/feature.json` file is updated to point to `specs/001-entities-arch-migration`, but that directory does not exist in the repository. The PR's stated purpose is re-enabling the ruff BLE rule with spec-kit documentation located at `dev/specs/002-ruff-ble-reenable/` (and `specs/002-ruff-ble-reenable` also exists). This appears to be an unintended change — likely a leftover from a different branch or feature — and contradicts the PR's scope. It should remain `specs/002-ruff-ble-reenable` to align with the BLE re-enable work being merged.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

@codspeed-hq

codspeed-hq Bot commented Jul 22, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 12 untouched benchmarks


Comparing pha/INBOX-19 (89e7ec4) with develop (01a4672)

Open in CodSpeed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

group/backend Issue related to the backend (API Server, Git Agent) type/spec A specification for an upcoming change to the project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant