---
phase: 09-config-and-auto-fix
plan: 02
type: execute
wave: 2
depends_on:
  - 09-01
files_modified:
  - src/corpus_analyzer/**/*.py
  - tests/**/*.py
autonomous: true
requirements:
  - RUFF-01
  - RUFF-02

must_haves:
  truths:
    - "All auto-fixable violations (W293/W291/F401/UP045/I001/UP035/F541) are eliminated from src/ and tests/"
    - "No __init__.py re-exports are broken — top-level import passes smoke-test"
    - "All 281+ tests pass after auto-fix"
    - "A per-rule fix summary is printed before committing (audit trail)"
    - "Only non-auto-fixable violations remain (E501 outside llm/, F841, E741, B905, SIM102, B007, E402, B023, B904)"
  artifacts:
    - path: "src/corpus_analyzer/"
      provides: "Source files with all auto-fixable violations eliminated"
  key_links:
    - from: "ruff check --fix"
      to: "src/ and tests/"
      via: "uv run ruff check --fix src/ tests/"
      pattern: "ruff --fix"
    - from: "smoke test"
      to: "corpus_analyzer package"
      via: "python -c \"import corpus_analyzer\""
      pattern: "import corpus_analyzer"
    - from: "pytest"
      to: "all test files"
      via: "uv run pytest -v"
      pattern: "pytest"
---

<objective>
Run `ruff check --fix` across `src/` and `tests/` to eliminate all auto-fixable violations in one pass, then validate with smoke-test and full pytest before committing.

Purpose: Eliminates ~282 auto-fixable violations (W293, W291, F401, UP045, I001, UP035, F541) cleanly in a single automated pass. The two-commit strategy (config in Plan 01, auto-fix here) provides a clean audit trail.
Output: All source files with auto-fixable violations corrected. Committed as Commit 2 (auto-fix commit).
</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/09-config-and-auto-fix/09-RESEARCH.md
@.planning/phases/09-config-and-auto-fix/09-01-SUMMARY.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Audit auto-fixable violations and run ruff --fix</name>
  <files>src/corpus_analyzer/**/*.py, tests/**/*.py, scripts/run_rewrite_dry_run.py</files>
  <action>
Work from the project root `/home/ollie/Development/Tools/vibe_coding/corpus-analyzer`.

**Step 1 — Print per-rule summary of fixable violations (audit trail):**
```bash
uv run ruff check src/ tests/ --output-format=json 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
fixable = [d for d in data if d.get('fix')]
from collections import Counter
by_rule = Counter(d['code'] for d in fixable)
print('Auto-fixable violations before fix:')
for k,v in by_rule.most_common():
    print(f'  {k}: {v}')
print(f'Total fixable: {sum(by_rule.values())}')
"
```

**Step 2 — Run ruff auto-fix across src/ and tests/:**
```bash
uv run ruff check --fix src/ tests/
```

Note: `scripts/run_rewrite_dry_run.py` is in `scripts/` which is NOT excluded — ruff will auto-fix its W293/F401 violations too. This is correct behaviour (it is a legitimate project file, not a debug scratch file).

**Step 3 — Check remaining violations (only non-auto-fixable should remain):**
```bash
uv run ruff check src/ tests/
```

Expected remaining violations: E501 (lines outside llm/), F841, E741, B905, SIM102, B007, E402, B023, B904. If ANY of the following appear, something went wrong — fail loudly and DO NOT proceed to commit:
- W293 (trailing whitespace on blank line) → auto-fix should have removed all
- W291 (trailing whitespace) → same
- I001 (unsorted imports) → same
- UP045 (Optional[X] → X | None) → same
- UP035 (typing.List → list) → same
- F541 (f-string without placeholder) → same

If surprise violations appear from unexpected files, print the file paths and abort.

**Step 4 — Smoke test (must pass before full pytest):**
```bash
cd /home/ollie/Development/Tools/vibe_coding/corpus-analyzer && python -c "import corpus_analyzer; print('Smoke test PASSED')"
```

If the smoke test fails: print the full traceback, DO NOT commit, stop and report what broke.

**Step 5 — Full test suite:**
```bash
uv run pytest -v
```

All tests must pass. If any test fails: print the failure details, DO NOT commit, stop and report.

**Step 6 — Show git diff summary (audit trail for commit message):**
```bash
git diff --stat
```

**Step 7 — Commit all auto-fixed files as Commit 2:**
Stage only `src/` and `tests/` (and `scripts/` if modified):
```bash
git add src/ tests/ scripts/
```

Build the commit message from the per-rule summary printed in Step 1. Example format:
```
fix: ruff auto-fix — eliminate all auto-fixable violations

Rules fixed: W293 (212), W291 (26), F401 (17), UP045 (14), I001 (8), UP035 (3), F541 (2)
Total: 282 violations eliminated across src/ and tests/

All 281 tests pass. Smoke test: import corpus_analyzer OK.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
```

Use the actual counts from Step 1 in the commit message.
  </action>
  <verify>
    <automated>cd /home/ollie/Development/Tools/vibe_coding/corpus-analyzer && uv run ruff check src/ tests/ --output-format=json 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
bad_rules = {'W293','W291','I001','UP045','UP035','F541'}
bad = [d for d in data if d['code'] in bad_rules]
if bad:
    print('FAIL — auto-fixable violations remain:')
    from collections import Counter
    for k,v in Counter(d['code'] for d in bad).most_common():
        print(f'  {k}: {v}')
    sys.exit(1)
else:
    print('PASS — no auto-fixable violations remain')
"</automated>
    <manual>Confirm git log shows two clean commits: one for pyproject.toml (config), one for auto-fixed source files. Run `python -c "import corpus_analyzer"` manually to verify imports.</manual>
  </verify>
  <done>All auto-fixable violations eliminated from src/ and tests/. Smoke test passes. All 281+ tests green. Committed as Commit 2 with per-rule counts in the commit message. Only non-auto-fixable violations remain (E501 outside llm/, and manual violations for Phases 10–11).</done>
</task>

</tasks>

<verification>
```bash
# Confirm no auto-fixable violations remain
uv run ruff check src/ tests/ --output-format=json 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
auto_fixable = {'W293','W291','I001','UP045','UP035','F541','F401'}
remaining = [d for d in data if d['code'] in auto_fixable]
if remaining:
    print('FAIL:', len(remaining), 'auto-fixable violations remain')
    for d in remaining[:10]:
        print(f'  {d[\"code\"]} {d[\"filename\"]}:{d[\"location\"][\"row\"]}')
    sys.exit(1)
else:
    print('PASS: zero auto-fixable violations')
"

# Smoke test
python -c "import corpus_analyzer; print('import OK')"

# Full suite
uv run pytest -v --tb=short
```
</verification>

<success_criteria>
- `ruff check src/ tests/` reports zero W293, W291, I001, UP045, UP035, F541, F401 violations
- `python -c "import corpus_analyzer"` exits 0
- `uv run pytest -v` exits 0 with all tests passing
- Git log shows Commit 2 with per-rule fix counts in the message
- Remaining violations are only E501 (outside llm/) and manual-fix violations (F841, E741, B905, SIM102, B007, E402, B023, B904) — these are expected and left for Phases 10–11
</success_criteria>

<output>
After completion, create `.planning/phases/09-config-and-auto-fix/09-02-SUMMARY.md`
</output>
