DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path scoping-notes/loomworks-b48-ruff-followon-scoping-note-v0_1.md

Loomworks — scoping note — ruff gate follow-on (v0.1)

Version. 0.1 Date. 2026-08-06 Author. Claude.ai. Operator: Marvin Percival. Companion to. inspection-briefs/loomworks-b48-quality-tooling-step-0-findings-v0_2.md; CR-2026-173 (B-48), which landed the mypy gate and deferred this one per its own escape clause. Status. Draft — for Operator review before a CR is drawn from it.


1. What this is

CR-2026-173 got ruff from 3,618 findings to 543 via config fixes and auto-fix, then stopped — the remaining 543 are real, not misconfiguration, and hand-touching them wasn't in that CR's scope. This note decides what to do with each category before a follow-on CR gets drafted. Same discipline as B-54: size it correctly before building it.


2. The 543, by category, with a proposed disposition for each

| Count | Code | What it is | Proposed disposition | Why | |---|---|---|---|---| | 225 | E501 | Line too long | Raise the configured line-length limit, not manual wrapping | Wrapping 225 lines by hand is pure busywork with no correctness benefit. Check what the current limit is set to and what the longest offending lines actually need — likely a config number change (e.g. 88→100 or 120), not a code change. If a handful of lines are absurdly long even after a reasonable limit bump, wrap only those. | | 92 | B904 | raise inside except without from | Fix, for real | This one's a genuine defect class — it silently drops the original exception's traceback, which makes debugging harder. Mechanical per-instance (raise X from err or from None where deliberate), but needs a human/CC read at each site to pick the right one, not a blind pattern. | | 37 | F841 | Unused variable | Fix, for real | Standard cleanup. Low risk. A few may be deliberate (e.g. unpacking for clarity) — those get a # noqa with a reason, not a rename-to-underscore reflex. | | 36 | N818 | Exception class name doesn't end in Error | Needs a blast-radius check first, not an automatic rename | Renaming an exception class is a breaking change for anything that imports or catches it by name. Before touching these, CC should grep for every import/reference to each of the 36 classes across all three repos (engine, surface, stele) — if any class is part of a public-ish contract (caught by name elsewhere, or referenced in tests asserting exception type), the rename needs a deprecation-alias step, not a straight rename. | | 33 | E402 | Import not at top of file | Verify first, fix only the illegitimate ones | Some of these are likely deliberate — deferred imports to dodge circular-import issues, or path setup that has to run before the import. Each needs a one-line judgment: legitimate pattern (leave, add a scoped # noqa with reason) vs. genuine disorganization (move to top). Not a blanket fix. | | 24 | F821 | Undefined name | Verify exhaustively before fixing anything | Step 0's sample suggested these are forward-refs under from __future__ import annotations — a legitimate pattern, not bugs — but that wasn't checked on all 24. This category gets its own read-only pass first: confirm each one really is a forward-ref, not an actual typo/missing-import bug hiding in the noise. Any real ones get fixed; the legitimate pattern ones get a file-level or line-level # noqa. | | ~96 | 22 other codes, ≤17 each | Long tail | Triage case-by-case, batch the ones that share a fix pattern | Too scattered to generalize. CC reads the actual list, groups by fix-pattern (e.g. several codes might resolve with the same one-line change), fixes what's mechanical once grouped, flags anything that looks like a real design question back to this note before touching it.


3. What happens to the zero-tolerance gate

Once every category above is resolved (fixed, config-adjusted, or deliberately suppressed with a documented reason), ruff should land at genuine zero and the CR-2026-173 gate — already built and wired, just never turned on — gets flipped on. No new mechanism needed; the gate itself isn't the open question, the count getting to zero honestly is.

If any category turns out to have a irreducible handful that shouldn't be fixed (e.g. a couple of E402s that are structurally necessary), those get scoped # noqa suppressions with a one-line reason each, not a lowered bar for the whole gate.


4. Recommended execution order

  1. E501 config bump — fastest win, likely clears ~40% of the total in one config-file change.
  2. N818 blast-radius check (read-only) — needs to happen before any rename, so front-load the investigation even though the fix itself lands later.
  3. F821 verification pass (read-only) — same reasoning; confirm before touching.
  4. B904 and F841 fixes — mechanical-per-instance, can proceed in parallel with the above once scoped.
  5. E402 case-by-case — quick once each is judged.
  6. N818 renames (only for classes cleared as safe) + F821 fixes (only for the real ones).
  7. Long tail triage.
  8. Full suite + flip the zero-tolerance gate on.

This is CR-sized as a single follow-on (call it CR-2026-174 when drafted), not a multi-CR arc — none of the individual categories are big enough alone to warrant their own CR, and splitting them would just add checkpoint overhead for no benefit. The two read-only verification steps (N818, F821) go first inside that CR's own Step 0/1, same pattern as CR-2026-171's B-54 safety check.


5. Open question for the Operator

None that block drafting — the category-by-category dispositions above are my judgment calls, offered as a package. If any single row looks wrong to you (particularly N818's rename risk, since that's the one with real blast-radius exposure), say which one and I'll adjust before this goes to a CR. Otherwise this is ready to become CR-2026-174.


DUNIN7 — Done In Seven LLC — Miami, Florida Scoping note — ruff gate follow-on — v0.1 — 2026-08-06