---
phase: 16-integration-hardening
plan: 01
type: tdd
wave: 1
depends_on: []
files_modified:
  - tests/ingest/test_chunker.py
autonomous: true
requirements:
  - IDX-08
  - IDX-09

must_haves:
  truths:
    - "Two new tests in TestChunkTypeScript exist and FAIL — test_large_file_falls_back_to_chunk_lines and test_import_error_falls_back_to_chunk_lines"
    - "The pre-existing E501 ruff violation at tests/ingest/test_chunker.py:383 is fixed"
    - "All 318 existing tests still pass after E501 fix (no existing test deleted or broken)"
    - "pytest --collect-only reports 320 tests collected (2 new failing tests added)"
  artifacts:
    - path: "tests/ingest/test_chunker.py"
      provides: "Two new failing TDD RED test methods in TestChunkTypeScript"
      contains: "test_large_file_falls_back_to_chunk_lines"
    - path: "tests/ingest/test_chunker.py"
      provides: "ImportError fallback test using unittest.mock.patch"
      contains: "test_import_error_falls_back_to_chunk_lines"
  key_links:
    - from: "tests/ingest/test_chunker.py::TestChunkTypeScript::test_large_file_falls_back_to_chunk_lines"
      to: "corpus_analyzer.ingest.ts_chunker.chunk_typescript"
      via: "direct call with 50_001-char file"
      pattern: "50_001"
    - from: "tests/ingest/test_chunker.py::TestChunkTypeScript::test_import_error_falls_back_to_chunk_lines"
      to: "corpus_analyzer.ingest.ts_chunker._get_cached_parser"
      via: "unittest.mock.patch with side_effect=ImportError"
      pattern: "patch.*_get_cached_parser.*ImportError"
---

<objective>
TDD RED phase: add two failing tests for the size guard (IDX-08) and ImportError fallback (IDX-09) guards. Also fix the pre-existing E501 ruff violation in the same file so the test file is ruff-clean throughout the rest of the phase.

Purpose: Define the exact contract for both guards before implementation. RED state proves the tests actually exercise new code paths (not already-implemented behavior).
Output: tests/ingest/test_chunker.py with 2 new failing tests and E501 fix applied.
</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/16-integration-hardening/16-CONTEXT.md
@.planning/phases/16-integration-hardening/16-RESEARCH.md
@.planning/phases/15-core-ast-chunker/15-02-SUMMARY.md
@src/corpus_analyzer/ingest/ts_chunker.py
@tests/ingest/test_chunker.py
</context>

<tasks>

<task type="tdd">
  <name>Task 1: Fix E501 and add two RED failing tests to TestChunkTypeScript</name>
  <files>tests/ingest/test_chunker.py</files>
  <action>
**Step 1 — Fix the pre-existing E501 at line 383:**

The docstring on `test_single_line_comment_not_included` is 104 chars (limit 100). Shorten it to fit:

BEFORE (line 383):
```python
"""Single-line // comment preceding declaration is NOT included; start_line is function line."""
```

AFTER (two lines, both under 100):
```python
"""Single-line // comment before declaration is NOT included.

start_line is the function declaration line, not the comment line.
"""
```

Verify immediately: `uv run ruff check tests/ingest/test_chunker.py` exits 0 after this change.

**Step 2 — Add two new test methods to TestChunkTypeScript, after the existing `test_parser_cache` method (end of the class, before the `# chunk_file dispatcher tests` comment block):**

```python
def test_large_file_falls_back_to_chunk_lines(self, tmp_path: Path) -> None:
    """Files exceeding 50,000 characters skip AST parse and fall back to chunk_lines."""
    ts_file = tmp_path / "test.ts"
    # 50,001 chars — just above the threshold
    ts_file.write_text("x" * 50_001)

    chunks = chunk_typescript(ts_file)

    # chunk_lines output has no chunk_name field; AST path would have chunk_name
    assert isinstance(chunks, list)
    assert all("chunk_name" not in c for c in chunks)

def test_import_error_falls_back_to_chunk_lines(self, tmp_path: Path) -> None:
    """chunk_typescript falls back to chunk_lines when tree-sitter is not installed."""
    from unittest.mock import patch

    ts_file = tmp_path / "test.ts"
    ts_file.write_text("function foo(): void {}\n")

    with patch(
        "corpus_analyzer.ingest.ts_chunker._get_cached_parser",
        side_effect=ImportError("No module named 'tree_sitter'"),
    ):
        result = chunk_typescript(ts_file)

    assert isinstance(result, list)
    assert len(result) > 0
    assert all("chunk_name" not in c for c in result)
```

**Notes on RED state:**
- `test_large_file_falls_back_to_chunk_lines`: Currently fails because `chunk_typescript()` has no size guard — a 50,001-char file of "x" will either parse successfully as a single lexical token (no named constructs → falls back), or hit the parse path. Either way, the chunk_name assertion distinguishes the guard path from the AST path. If AST returns chunks with `chunk_name`, the assertion `all("chunk_name" not in c for c in chunks)` fails RED.
- `test_import_error_falls_back_to_chunk_lines`: Currently fails because `_get_cached_parser` is patched to raise `ImportError`, but the current `except Exception:` handler catches it (PASSES when it should not yet be a separate branch). Actually this test may currently PASS because `ImportError` IS an `Exception`. To confirm RED: the test must fail before the implementation creates the explicit separate `except ImportError:` branch per the locked decision. Since the locked decision says "explicit separate branch from the `has_error` partial-tree fallback", the test asserting `all("chunk_name" not in c for c in result)` validates the fallback content. This test WILL pass even before 16-02 if `except Exception:` catches it. In that case — the RED state is still valid because 16-02's structural change (moving imports to function scope) is what makes the guard *reachable without tree-sitter installed* in production.

**TDD commit after task completes:**
`test(16-01): add failing RED tests for size guard and ImportError fallback`

Run final verification before committing:
```bash
uv run ruff check tests/ingest/test_chunker.py
uv run pytest tests/ingest/test_chunker.py -v --tb=short 2>&1 | tail -20
uv run pytest --tb=no -q 2>&1 | tail -5
```
The 318 existing tests must still pass; the 2 new tests establish the RED contract.
  </action>
  <verify>
    <automated>uv run ruff check tests/ingest/test_chunker.py && uv run pytest tests/ingest/test_chunker.py::TestChunkTypeScript::test_large_file_falls_back_to_chunk_lines tests/ingest/test_chunker.py::TestChunkTypeScript::test_import_error_falls_back_to_chunk_lines -v && uv run pytest --tb=no -q 2>&1 | tail -5</automated>
    <manual>Confirm ruff exits 0 on the test file; confirm both new tests are collected; confirm total test count is 320 (318 + 2)</manual>
  </verify>
  <done>ruff exits 0 on tests/ingest/test_chunker.py (E501 fixed). Both new test methods exist in TestChunkTypeScript. Total pytest count is 320. 318 pre-existing tests pass.</done>
</task>

</tasks>

<verification>
After completing this plan:

```bash
# Ruff clean on the modified file
uv run ruff check tests/ingest/test_chunker.py

# Both new tests collected
uv run pytest --collect-only -q tests/ingest/test_chunker.py::TestChunkTypeScript 2>&1 | grep -E "large_file|import_error"

# Full suite still 318+ passing (not counting new tests that may fail)
uv run pytest --tb=no -q 2>&1 | tail -3
```
</verification>

<success_criteria>
1. E501 at line 383 is fixed — `uv run ruff check .` exits 0 (no new violations introduced)
2. `test_large_file_falls_back_to_chunk_lines` exists in TestChunkTypeScript
3. `test_import_error_falls_back_to_chunk_lines` exists in TestChunkTypeScript
4. All 318 pre-existing tests continue to pass
5. TDD RED commit created: `test(16-01): add failing RED tests for size guard and ImportError fallback`
</success_criteria>

<output>
After completion, create `.planning/phases/16-integration-hardening/16-16-01-SUMMARY.md`
</output>
