---
phase: 10-manual-ruff-leaf-to-hub
plan: 03
type: execute
wave: 2
depends_on:
  - 10-01
files_modified:
  - src/corpus_analyzer/core/database.py
  - src/corpus_analyzer/generators/advanced_rewriter.py
  - src/corpus_analyzer/generators/templates.py
  - tests/cli/test_search_status.py
  - tests/config/test_schema.py
  - tests/ingest/test_indexer.py
  - tests/ingest/test_scanner.py
  - tests/search/test_engine.py
  - tests/test_analyzers/test_quality_logic.py
  - tests/test_core/test_db_migration.py
autonomous: true
requirements:
  - RUFF-03

must_haves:
  truths:
    - "All E501 violations in core/database.py, generators/, and all test files are resolved"
    - "All 281 tests pass after structural line-wrapping"
    - "database.py ternary expressions use explicit `is not None` form (not `or` shortcut)"
    - "Long string literals in generators/advanced_rewriter.py use implicit concatenation"
  artifacts:
    - path: "src/corpus_analyzer/core/database.py"
      provides: "E501-clean — lines 207, 318, 320, 321 wrapped"
      contains: "if row.get(\"category_confidence\") is not None"
    - path: "src/corpus_analyzer/generators/advanced_rewriter.py"
      provides: "E501-clean — 9 long lines wrapped (lines 43, 78, 87, 93, 94, 97, 107, 108, 142)"
    - path: "src/corpus_analyzer/generators/templates.py"
      provides: "E501-clean — line 67 wrapped"
    - path: "tests/cli/test_search_status.py"
      provides: "E501-clean — line 126 wrapped"
    - path: "tests/config/test_schema.py"
      provides: "E501-clean — line 75 wrapped"
    - path: "tests/ingest/test_indexer.py"
      provides: "E501-clean — line 365 wrapped"
    - path: "tests/ingest/test_scanner.py"
      provides: "E501-clean — line 168 wrapped"
    - path: "tests/search/test_engine.py"
      provides: "E501-clean — lines 288, 318 wrapped"
    - path: "tests/test_analyzers/test_quality_logic.py"
      provides: "E501-clean — line 35 wrapped"
    - path: "tests/test_core/test_db_migration.py"
      provides: "E501-clean — line 38 wrapped"
  key_links:
    - from: "src/corpus_analyzer/core/database.py"
      to: "Document constructor"
      via: "category_confidence ternary wrapped across 3 lines"
      pattern: "if row\\.get\\(\"category_confidence\"\\) is not None"
    - from: "src/corpus_analyzer/generators/advanced_rewriter.py"
      to: "system_prompt string"
      via: "implicit string concatenation across lines"
      pattern: "system_prompt = \\("
---

<objective>
Wrap all remaining E501 violations: the core/database.py hub (4 violations), generators/ (9 violations), and all 8 test file violations. This completes RUFF-03 — no E501 violations remain anywhere outside cli.py.

Purpose: After this plan (combined with Plan 02), ruff check exits with zero E501 violations in all non-cli.py files.
Output: 10 files changed; zero E501 violations remaining outside cli.py; 281 tests still 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
@.planning/phases/10-manual-ruff-leaf-to-hub/10-01-SUMMARY.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Wrap E501 violations in core/database.py and generators/</name>
  <files>
    src/corpus_analyzer/core/database.py
    src/corpus_analyzer/generators/advanced_rewriter.py
    src/corpus_analyzer/generators/templates.py
  </files>
  <action>
Fix 13 E501 violations across 3 files. All structural wraps — no logic changes.

**core/database.py line 207** — long method call. Read the line and wrap using parentheses:
```python
# Generic pattern:
result = self.some_method(
    arg1, arg2, arg3
)
```

**core/database.py lines 318, 320, 321** — three ternary expressions inside a `Document(...)` constructor. Use the explicit `is not None` form (NOT the `or` shortcut — `or` changes semantics when the stored value is 0.0):
```python
category_confidence=float(
    row.get("category_confidence")
    if row.get("category_confidence") is not None
    else 0.0
),
quality_score=float(
    row.get("quality_score")
    if row.get("quality_score") is not None
    else 0.0
),
is_gold_standard=bool(
    row.get("is_gold_standard")
    if row.get("is_gold_standard") is not None
    else False
),
```
Read the actual lines 315-325 to confirm the exact field names and existing structure before editing.

**generators/advanced_rewriter.py line 43** — long `raise FileNotFoundError(...)`. Wrap the message string:
```python
raise FileNotFoundError(
    f"Template not found: {template_path}"
)
```

**generators/advanced_rewriter.py line 78** — long ternary with chained call (146 chars). Break the ternary into a parenthesised block:
```python
gold_docs = (
    list(self.client.db.get_gold_standard_documents(category=doc.category, tag=tag))
    if hasattr(self.client, "db")
    else []
)
```
Note: the inner list(...) call itself (83 chars) fits within 100 chars, so no further wrapping needed.

**generators/advanced_rewriter.py line 87** — long line in an f-string or expression. Read the actual line, then wrap by extracting a local variable or wrapping the call.

**generators/advanced_rewriter.py lines 93-94, 97, 107-108** — these are long string literals inside a parenthesised `system_prompt = (...)` or similar. Use implicit string concatenation:
```python
system_prompt = (
    "First part of the long string that fits within 100 chars "
    "second part continuing the string "
    "third part..."
)
```
Split at natural word boundaries. Do NOT split in the middle of a word or escape sequence.

**generators/advanced_rewriter.py line 142** — long string in an f-string or variable. Read the line and wrap using the same implicit concatenation or parenthesised expression approach.

**generators/templates.py line 67** — one long line. Read and wrap using parentheses.

After editing these three files:
```bash
uv run ruff check src/corpus_analyzer/core/database.py src/corpus_analyzer/generators/
uv run pytest -v --tb=short -q 2>&1 | tail -5
```
  </action>
  <verify>
    <automated>uv run ruff check src/corpus_analyzer/core/database.py src/corpus_analyzer/generators/ && uv run pytest -v --tb=short -q 2>&1 | tail -5</automated>
    <manual>Confirm zero E501 violations in database.py and generators/. Confirm 281 tests passed.</manual>
  </verify>
  <done>Zero ruff violations in core/database.py and generators/; pytest 281 passed.</done>
</task>

<task type="auto">
  <name>Task 2: Wrap E501 violations in all test files and run final ruff gate</name>
  <files>
    tests/cli/test_search_status.py
    tests/config/test_schema.py
    tests/ingest/test_indexer.py
    tests/ingest/test_scanner.py
    tests/search/test_engine.py
    tests/test_analyzers/test_quality_logic.py
    tests/test_core/test_db_migration.py
  </files>
  <action>
Fix 8 E501 violations across 7 test files. All are structural wraps — long assertion strings, long variable assignments, or long function calls in tests.

For each file and line, read the actual line then wrap using standard patterns:

- **Long assertion**: wrap into `assert (\n    expression\n)`
- **Long string in pytest.raises or similar**: break with implicit concatenation
- **Long function call**: break arguments across lines

Files and their violations:
- `tests/cli/test_search_status.py:126` — one violation
- `tests/config/test_schema.py:75` — one violation
- `tests/ingest/test_indexer.py:365` — one violation
- `tests/ingest/test_scanner.py:168` — one violation
- `tests/search/test_engine.py:288, 318` — two violations
- `tests/test_analyzers/test_quality_logic.py:35` — one violation
- `tests/test_core/test_db_migration.py:38` — one violation

After all test file edits, run the full ruff check for Phase 10 scope and the complete test suite:

```bash
uv run ruff check . --output-format=concise 2>&1 | grep -v "cli.py"
uv run pytest -v --tb=short -q 2>&1 | tail -5
```

Expected output: zero violations listed (excluding cli.py lines which are Phase 11 scope); 281 passed.

If any E501 violations remain in src/ or tests/ (excluding cli.py), fix them before committing.
  </action>
  <verify>
    <automated>uv run ruff check . --output-format=concise 2>&1 | grep -v "cli.py" | grep -E "E501|E741|E402|B017" && uv run pytest -v --tb=short -q 2>&1 | tail -5</automated>
    <manual>The grep command should produce NO output (empty = all Phase 10 violations cleared). Confirm 281 passed. If grep has output, fix remaining violations before proceeding.</manual>
  </verify>
  <done>Zero E501 violations in any file outside cli.py; zero E741/E402/B017 violations anywhere; pytest 281 passed. Phase 10 requirements RUFF-03, RUFF-04, RUFF-05, RUFF-06 are all satisfied.</done>
</task>

</tasks>

<verification>
Final gate for Phase 10:
```bash
# Phase 10 scope — should be zero lines of output:
uv run ruff check . --output-format=concise 2>&1 | grep -v "cli\.py"

# Full test suite:
uv run pytest -v --tb=short -q 2>&1 | tail -5
```
Acceptable remaining violations after Phase 10: only cli.py violations (E501 x45, B023 x1, B904 x1) which are Phase 11 scope.
</verification>

<success_criteria>
- `uv run ruff check . --output-format=concise | grep -v "cli.py"` returns empty (zero non-cli violations)
- `uv run pytest -v -q --tb=short` reports 281 passed, 0 failed
- database.py ternary expressions use `is not None` form (not `or` shortcut)
- generators/advanced_rewriter.py long strings use implicit concatenation (not backslash continuation)
- All 10 files in files_modified have zero ruff violations
</success_criteria>

<output>
After completion, create `.planning/phases/10-manual-ruff-leaf-to-hub/10-03-SUMMARY.md` with:
- Files changed and exact line numbers for each wrap
- Final `uv run ruff check .` output showing only cli.py violations remain
- Test result confirming 281 passed
- Confirmation that RUFF-03, RUFF-04, RUFF-05, RUFF-06 are all satisfied
</output>
