---
phase: 10-manual-ruff-leaf-to-hub
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/corpus_analyzer/core/database.py
  - src/corpus_analyzer/llm/chunked_processor.py
  - src/corpus_analyzer/llm/rewriter.py
  - tests/store/test_schema.py
  - scripts/run_rewrite_dry_run.py
autonomous: true
requirements:
  - RUFF-04
  - RUFF-05
  - RUFF-06

must_haves:
  truths:
    - "No E741 ambiguous-name violations exist in database.py or chunked_processor.py"
    - "No E402 import-ordering violations exist in llm/rewriter.py"
    - "No B017 blind-exception violations exist in tests/store/test_schema.py"
    - "All 281 tests still pass after variable renames and import moves"
    - "scripts/ violations are eliminated by ruff auto-fix"
  artifacts:
    - path: "src/corpus_analyzer/core/database.py"
      provides: "E741-clean — `l` renamed to `link` (line 150) and `lnk` (line 310)"
      contains: "link.model_dump()"
    - path: "src/corpus_analyzer/llm/chunked_processor.py"
      provides: "E741-clean — `l` renamed to `level_val` (line 64) and `line` (line 141)"
      contains: "level_val = force_level"
    - path: "src/corpus_analyzer/llm/rewriter.py"
      provides: "E402-clean — `import concurrent.futures` moved to top block; duplicate `from typing import NamedTuple` at line 233 deleted"
      contains: "import concurrent.futures"
    - path: "tests/store/test_schema.py"
      provides: "B017-clean — `pytest.raises(Exception)` replaced with `pytest.raises(ValidationError)` at lines 148 and 153"
      contains: "pytest.raises(ValidationError)"
    - path: "scripts/run_rewrite_dry_run.py"
      provides: "F401/W293-clean via ruff --fix"
  key_links:
    - from: "src/corpus_analyzer/core/database.py"
      to: "Link model"
      via: "list comprehension rename"
      pattern: "link\\.model_dump\\(\\)"
    - from: "src/corpus_analyzer/llm/chunked_processor.py"
      to: "Atom constructor"
      via: "level_val variable reference update"
      pattern: "level=level_val"
    - from: "src/corpus_analyzer/llm/rewriter.py"
      to: "top-of-file import block"
      via: "concurrent.futures addition"
      pattern: "import concurrent\\.futures"
    - from: "tests/store/test_schema.py"
      to: "pydantic ValidationError"
      via: "import + raises replacement"
      pattern: "from pydantic import ValidationError"
---

<objective>
Fix all semantic ruff violations in the non-cli.py codebase: E741 ambiguous variable names in database.py and chunked_processor.py, E402 mid-file imports in rewriter.py, B017 blind exception assertions in test_schema.py, and auto-fixable violations in scripts/.

Purpose: Semantic violations must be fixed before E501 wrapping (changes variable references, import positions — must be verified green with tests before the mechanical E501 work begins).
Output: Five files changed; zero E741, E402, B017 violations remaining; 281 tests passing.
</objective>

<execution_context>
@/home/ollie/.claude/get-shit-done/workflows/execute-plan.md
@/home/ollie/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/10-manual-ruff-leaf-to-hub/10-RESEARCH.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Fix E741 ambiguous variable names in database.py and chunked_processor.py</name>
  <files>
    src/corpus_analyzer/core/database.py
    src/corpus_analyzer/llm/chunked_processor.py
  </files>
  <action>
Fix the four E741 `l` variable violations by renaming to semantically meaningful names.

**database.py line 150** — in a list comprehension serialising document links:
```python
# Before:
"links": json.dumps([l.model_dump() for l in doc.links]),
# After:
"links": json.dumps([link.model_dump() for link in doc.links]),
```

**database.py line 310** — in a list comprehension deserialising links from a DB row:
```python
# Before:
links=[Link(**l) for l in json.loads(row.get("links") or "[]")],
# After:
links=[Link(**lnk) for lnk in json.loads(row.get("links") or "[]")],
```

**chunked_processor.py line 64** — assignment used as a heading level variable:
```python
# Before:
l = force_level if force_level else current_level
# (all downstream references to `l` within that scope also become `level_val`)
# After:
level_val = force_level if force_level else current_level
```
After the rename, find all uses of `l` within the same scope and update them to `level_val`. Typical usage: passed into `Atom(... level=l ...)` or similar.

**chunked_processor.py line 141** — loop variable in a generator:
```python
# Before:
atom_size = sum(len(l) for l in atom.lines)
# After:
atom_size = sum(len(line) for line in atom.lines)
```

After both files are edited, run:
```bash
uv run ruff check src/corpus_analyzer/core/database.py src/corpus_analyzer/llm/chunked_processor.py
uv run pytest -v 2>&1 | tail -20
```
Confirm: zero E741 violations in both files; all 281 tests pass.
  </action>
  <verify>
    <automated>uv run ruff check src/corpus_analyzer/core/database.py src/corpus_analyzer/llm/chunked_processor.py && uv run pytest -v --tb=short -q 2>&1 | tail -5</automated>
    <manual>Confirm `ruff check` output shows no E741 violations for the two files. Confirm pytest shows 281 passed.</manual>
  </verify>
  <done>Zero E741 violations in database.py and chunked_processor.py; pytest 281 passed.</done>
</task>

<task type="auto">
  <name>Task 2: Fix E402 mid-file imports in llm/rewriter.py and eliminate duplicate NamedTuple import</name>
  <files>
    src/corpus_analyzer/llm/rewriter.py
  </files>
  <action>
Fix the two E402 violations at lines 232-233 of rewriter.py.

**Step 1 — Inspect existing top-of-file imports (lines 1-20)** to confirm whether `from typing import NamedTuple` is already present and whether `import concurrent.futures` is present.

The research confirms:
- `from typing import NamedTuple` IS already imported at line 6 → line 233 is a pure duplicate, delete it.
- `import concurrent.futures` is NOT at the top → add it to the top-of-file import block (with the other stdlib imports like `import re`, `import json`).

**Step 2 — Add `import concurrent.futures`** to the top-of-file stdlib import block, grouped with other `import X` lines (not `from X import Y` lines). Insert it alphabetically among stdlib imports.

**Step 3 — Delete lines 232-233** (the `import concurrent.futures` and duplicate `from typing import NamedTuple` that appear mid-file after a prompt string). Remove both lines and any blank lines left orphaned immediately around them if they are now redundant.

After editing:
```bash
uv run ruff check src/corpus_analyzer/llm/rewriter.py
uv run pytest -v --tb=short -q 2>&1 | tail -5
```
Confirm: zero E402 violations in rewriter.py; tests still pass.
  </action>
  <verify>
    <automated>uv run ruff check src/corpus_analyzer/llm/rewriter.py && uv run pytest -v --tb=short -q 2>&1 | tail -5</automated>
    <manual>Confirm no E402 violations in rewriter.py output. Confirm pytest 281 passed.</manual>
  </verify>
  <done>Zero E402 violations in llm/rewriter.py; `import concurrent.futures` is in the top-of-file import block; duplicate NamedTuple import removed; pytest 281 passed.</done>
</task>

<task type="auto">
  <name>Task 3: Fix B017 blind-exception assertions in tests/store/test_schema.py and auto-fix scripts/</name>
  <files>
    tests/store/test_schema.py
    scripts/run_rewrite_dry_run.py
  </files>
  <action>
**Part A — B017 in test_schema.py:**

Read the test file around lines 146-154 to understand what `_make_valid_record` does. The docstrings indicate it creates a ChunkRecord pydantic model with an invalid vector dimension — pydantic raises `ValidationError`.

Check test file imports at the top. Add `from pydantic import ValidationError` if not already imported.

Replace both `pytest.raises(Exception)` calls with `pytest.raises(ValidationError)`:

```python
# Line 148 — before:
with pytest.raises(Exception):  # pydantic ValidationError or similar
# After:
with pytest.raises(ValidationError):

# Line 153 — before:
with pytest.raises(Exception):
# After:
with pytest.raises(ValidationError):
```

Run the specific test class after editing to verify the fix is correct (the tests must PASS — not fail with wrong exception type):
```bash
uv run pytest tests/store/test_schema.py -v
```
If any test fails, check whether `ValidationError` should be `pydantic.ValidationError` or `pydantic_core.ValidationError` and adjust the import accordingly.

**Part B — auto-fix scripts/:**

The scripts/ directory has F401 and W293 violations (all auto-fixable). Run:
```bash
uv run ruff check --fix scripts/
```
Confirm no violations remain:
```bash
uv run ruff check scripts/
```

Full regression check:
```bash
uv run pytest -v --tb=short -q 2>&1 | tail -5
```
  </action>
  <verify>
    <automated>uv run ruff check tests/store/test_schema.py scripts/ && uv run pytest tests/store/test_schema.py -v --tb=short && uv run pytest -v --tb=short -q 2>&1 | tail -5</automated>
    <manual>Confirm ruff shows zero violations for test_schema.py and scripts/. Confirm all test_schema.py tests pass (not just fail silently). Confirm full suite 281 passed.</manual>
  </verify>
  <done>Zero B017 violations in test_schema.py; zero violations in scripts/; pytest 281 passed.</done>
</task>

</tasks>

<verification>
After all tasks complete, run the full semantic-violation check:
```bash
uv run ruff check . --output-format=concise 2>&1 | grep -E "E741|E402|B017"
uv run pytest -v --tb=short -q 2>&1 | tail -5
```
Expected: zero lines matching E741, E402, or B017; 281 passed.
</verification>

<success_criteria>
- `uv run ruff check . --output-format=concise | grep -E "E741|E402|B017"` returns empty
- `uv run pytest -v -q --tb=short` reports 281 passed, 0 failed
- `import concurrent.futures` is in the top import block of rewriter.py
- `pytest.raises(ValidationError)` appears in test_schema.py lines 148 and 153
- scripts/run_rewrite_dry_run.py has zero ruff violations
</success_criteria>

<output>
After completion, create `.planning/phases/10-manual-ruff-leaf-to-hub/10-01-SUMMARY.md` with:
- What was changed in each file
- Exact line numbers modified
- Test result confirming 281 passed
- Any surprises (e.g., if ValidationError import needed adjustment)
</output>
