---
phase: 11-manual-ruff-cli-mypy
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/corpus_analyzer/cli.py
autonomous: true
requirements:
  - RUFF-07

must_haves:
  truths:
    - "`uv run ruff check src/corpus_analyzer/cli.py` exits 0 with zero violations"
    - "All 45 E501 violations in cli.py are fixed by wrapping Annotated[...] signatures across lines"
    - "B023 progress_callback is fixed with default-argument capture (no signature change for callers)"
    - "B904 raise in except clause is fixed with `raise ... from None`"
    - "All 281 tests still pass after wrapping"
  artifacts:
    - path: "src/corpus_analyzer/cli.py"
      provides: "Ruff-clean CLI module (zero violations)"
  key_links:
    - from: "src/corpus_analyzer/cli.py"
      to: "ruff check"
      via: "E501 wrapping + B023/B904 fixes"
      pattern: "uv run ruff check src/corpus_analyzer/cli.py"
---

<objective>
Fix all 47 ruff violations in cli.py: 45 E501 line-length violations and 2 semantic violations (B023, B904).

Purpose: RUFF-07 — cli.py is the last file with outstanding ruff violations. Zero violations here unlocks a clean `ruff check .` exit in Phase 12.
Output: A ruff-clean cli.py with no content changes beyond line formatting.
</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/ROADMAP.md
@.planning/STATE.md
@.planning/phases/11-manual-ruff-cli-mypy/11-RESEARCH.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Fix B023 and B904 semantic violations in cli.py</name>
  <files>src/corpus_analyzer/cli.py</files>
  <action>
Fix the two non-E501 ruff violations first (easier to isolate):

**B023 fix (line 189):** `progress_callback` closes over `task_id` from the enclosing for-loop. Fix with default-argument capture:
```python
# Before:
def progress_callback(n: int) -> None:
    progress.advance(task_id, n)

# After:
def progress_callback(n: int, _task_id: TaskID = task_id) -> None:
    progress.advance(_task_id, n)
```
Before editing: read `ingest/indexer.py` to confirm the `index_source(progress_callback=...)` parameter accepts `Callable[[int], None]`. Default-argument capture does NOT change the external signature — `n` is still the only positional parameter. `TaskID` is imported from `rich.progress`; check the existing imports in cli.py and add if needed.

**B904 fix (line ~294):** Inside an `except RuntimeError as e:` block, change:
```python
raise typer.Exit(code=1)
```
to:
```python
raise typer.Exit(code=1) from None
```
Locate the exact line by searching for `except RuntimeError` in cli.py. Use `from None` (not `from e`) because the exit is deliberate suppression of the runtime error.

After both fixes: run `uv run ruff check src/corpus_analyzer/cli.py 2>&1 | grep -E "^B"` — must return empty.
  </action>
  <verify>
    <automated>uv run ruff check src/corpus_analyzer/cli.py 2>&1 | grep -E "^B" | wc -l | grep -q "^0$" && echo "B-violations: 0"</automated>
  </verify>
  <done>B023 and B904 are gone from ruff output; the progress_callback default-arg capture is in place; raise in except uses `from None`.</done>
</task>

<task type="auto">
  <name>Task 2: Wrap all 45 E501 violations in cli.py</name>
  <files>src/corpus_analyzer/cli.py</files>
  <action>
Wrap every line exceeding 100 characters. Run `uv run ruff check src/corpus_analyzer/cli.py 2>&1 | grep E501` to get the current line numbers before editing; use those line numbers as your edit targets.

**Primary wrapping technique — Typer Annotated parameters:**
Split `Annotated[type, typer.Option(...)]` across lines:
```python
# Before:
name: Annotated[str | None, typer.Option("--name", "-n", help="Source name (default: directory basename)")] = None,

# After:
name: Annotated[
    str | None,
    typer.Option("--name", "-n", help="Source name (default: directory basename)"),
] = None,
```

**For help= strings still too long after wrapping:** shorten the help text to fit within 100 chars. Example (line ~800, 145 chars):
```python
# Before (too long even wrapped):
typer.Option("--auto-category", help="Auto-select category by classification confidence score"),

# After (shortened help):
typer.Option("--auto-category", help="Auto-select category by confidence score"),
```

**For console.print(f"...") strings:** wrap with parentheses and implicit string concatenation:
```python
# Before:
console.print(f"[red]Error:[/] Source '{source_name}' or path '{directory}' already exists. Use --force to re-add.")

# After:
console.print(
    f"[red]Error:[/] Source '{source_name}' or path '{directory}' already exists. "
    "Use --force to re-add."
)
```

**For list comprehensions / plain expressions** that run long: wrap inside parentheses at a logical operator or comma boundary.

**B006 list defaults** (`= ["**/*"]`, `= []`): these are already suppressed by `pyproject.toml` per-file-ignores for cli.py. Do NOT touch them — they are not violations.

Process violations top-to-bottom. After each batch of edits run `uv run ruff check src/corpus_analyzer/cli.py 2>&1 | grep E501 | wc -l` to track progress toward 0.

Final gate: `uv run ruff check src/corpus_analyzer/cli.py` must print `Found 0 errors.` or exit silently.

Then run the full test suite to confirm no regressions.
  </action>
  <verify>
    <automated>uv run ruff check src/corpus_analyzer/cli.py && uv run pytest -x -q 2>&1 | tail -5</automated>
  </verify>
  <done>`uv run ruff check src/corpus_analyzer/cli.py` exits 0 with zero violations printed; all 281 tests pass.</done>
</task>

</tasks>

<verification>
After both tasks complete:
1. `uv run ruff check src/corpus_analyzer/cli.py` — exits 0, zero output
2. `uv run ruff check .` — cli.py contributes zero violations (other files may still have mypy work but no ruff violations)
3. `uv run pytest -v` — all 281 tests pass
</verification>

<success_criteria>
- `uv run ruff check src/corpus_analyzer/cli.py` exits 0
- Zero E501, B023, B904 violations in cli.py
- All 281 tests still pass (no logic changes, only formatting)
</success_criteria>

<output>
After completion, create `.planning/phases/11-manual-ruff-cli-mypy/11-01-SUMMARY.md`
</output>
