# Implementation Plan: REQ-010 -- Kernel Evolution + CLAUDE.md Patch + Uninstall

## Domain Research

- **Problem type**: Content editing (markdown injection/removal via regex), CLI command wiring, filesystem cleanup. Not CPU-bound. The heaviest operation is regex replacement on a file typically 1-10 KB. The rest is `Path.unlink()` on <15 files.
- **Approaches evaluated**:
  1. AST-based markdown parsing for marker injection/removal -- rejected. Markdown AST libraries (mistune, markdown-it-py) add a dependency for something that is safely handled by string search + regex. The marker block is machine-generated with fixed delimiters -- there is no ambiguity to resolve.
  2. Plain string `in` check + append for injection, regex for removal -- chosen. `"<!-- ievo:start -->" in content` is the idempotency check. `re.sub(r"\n*<!-- ievo:start -->[\s\S]*?<!-- ievo:end -->\n*", "\n", content)` for removal. This is the minimal path.
  3. Separate `claude_md.py` module in `core/` -- evaluated but rejected. The spec explicitly places `patch_claude_md(path)` in `src/ievo/commands/init.py` (AC Part B: "a standalone function `patch_claude_md(path: Path) -> bool` in `src/ievo/commands/init.py`"). The unpatch logic belongs in `src/ievo/commands/uninstall.py` which is the only consumer. No shared module needed.
- **Recommended approach**: Three parallel workstreams mapping to Parts A, B, C of the spec. Part A is pure .md content editing (no Python tests needed, but content verification tests confirm the edits are correct). Part B is a pure function `patch_claude_md()` + template edit. Part C is a new Typer command with filesystem operations.
- **Libraries/tools**: None new. `re` (stdlib) for marker block regex. `pathlib` for file operations. `typer.confirm()` for the prompt. Existing `find_project_root()` for project detection.
- **Known pitfalls**:
  - The regex `\n*<!-- ievo:start -->[\s\S]*?<!-- ievo:end -->\n*` must use non-greedy `*?` to avoid eating content between multiple marker blocks (defensive). It must also handle the block at the very start of file (no leading `\n`) and at the very end (no trailing `\n`).
  - `patch_claude_md()` must handle files ending without a trailing newline -- the spec says "always ensure the injected block is separated from existing content by exactly one blank line."
  - The uninstall command removes `.claude/agents/*.md` but NOT the `.claude/agents/` directory itself (per negative AC).
  - `uninstall` should use `find_project_root()`, not `require_project()`, because `require_project()` prints a different error message ("Not in an iEvo project. Run ievo init first.") than what the spec requires ("No iEvo project found in this directory or any parent.").
  - The `CLAUDE_MD` template in `init_templates.py` needs the marker block added to its static content. This means existing tests that assert on `CLAUDE.md` content (e.g., `test_init_claude_md_has_scaffold`) may need updating if they check for exact content.
  - The 13 agent `.md` files need a new line in their Context Loading section. 12 agents have Context Loading; `debugger.md` does not (it has a Workflow section instead). The spec says "All 13 agent `.md` files" -- the debugger needs a Context Loading section added.
  - The evolution agent's Step 1 section needs a classification decision tree: "Is the root cause in pipeline rules? -> write to `.ievo/evolution/iEVO.md`" vs. "Is it in an agent's instructions? -> write to `.ievo/evolution/<agent-name>.md`".
  - The evolution agent's Step 3 section needs `**Target:** iEVO.md` or `**Target:** agents/<agent-name>.md` in the curator issue body template.

## Architecture Assessment

- **Fits existing architecture**: yes -- follows existing patterns (Typer command in `commands/`, console helpers for output, `find_project_root()` for project detection).
- **New patterns introduced**:
  - Marker block injection/removal pattern (first use of `<!-- ievo:start -->...<!-- ievo:end -->` in the codebase). Documented in VOCABULARY.md as "ievo marker block".
  - Kernel evolution overlay (`.ievo/evolution/iEVO.md`) as a new write target for the evolution agent.
- **Modules affected**:
  - `src/ievo/marketplace/agents/evolution.md` -- Step 1 target classification, Step 3 issue body target field
  - `src/ievo/marketplace/templates/IEVO.md` -- new "Kernel overlay" subsection
  - `src/ievo/marketplace/agents/*.md` (all 13) -- new `.ievo/evolution/iEVO.md` in Context Loading
  - `src/ievo/commands/init_templates.py` -- marker block in `CLAUDE_MD` template
  - `src/ievo/commands/init.py` -- `patch_claude_md()` function + call it after CLAUDE.md creation
  - `src/ievo/commands/uninstall.py` -- NEW file, `uninstall()` command
  - `src/ievo/cli.py` -- register `uninstall` command
  - `tests/test_init.py` -- update existing tests, add new tests for `patch_claude_md()`
  - `tests/test_uninstall.py` -- NEW file
- **Risk level**: low -- additive changes. Part A is markdown-only. Part B adds content to existing template + a new pure function. Part C is a new command that does not touch any existing code paths.
- **Estimated complexity**: M (approximately 3-4 hours of agent work across 5 sub-tasks)

## Pre-conditions

- [x] REQ-000 implemented (project setup)
- [x] Git workflow confirmed in DECISIONS.md (D-012: trunk-based, `feat/<ticket>-<description>`)

## Sub-tasks

### TASK-010-1: Markdown content edits -- evolution agent, IEVO.md template, 13 agent files

**Scope**: Edit bundled `.md` files to add kernel evolution target classification (evolution agent), kernel overlay documentation (IEVO.md template), and `.ievo/evolution/iEVO.md` to all 13 agents' Context Loading.
**Tests**: ~5 content verification tests (grep/assert the expected strings are present in the bundled files)
**Files**: `src/ievo/marketplace/agents/evolution.md`, `src/ievo/marketplace/templates/IEVO.md`, all 13 `src/ievo/marketplace/agents/*.md`, `tests/test_marketplace_content.py` (NEW)
**Time estimate**: ~12 minutes

### TASK-010-2: `CLAUDE_MD` template + `patch_claude_md()` function + tests

**Scope**: Add marker block to `CLAUDE_MD` template constant. Create `patch_claude_md(path: Path) -> bool` function in `init.py`. Call it from `init()` after CLAUDE.md creation. Write unit tests for all patch scenarios.
**Tests**: ~8
**Files**: `src/ievo/commands/init_templates.py`, `src/ievo/commands/init.py`, `tests/test_init.py`
**Time estimate**: ~10 minutes

### TASK-010-3: `ievo uninstall` command -- core logic + tests

**Scope**: Create `src/ievo/commands/uninstall.py` with `uninstall()` command. Register in `cli.py`. Write comprehensive tests covering all AC scenarios.
**Tests**: ~14
**Files**: `src/ievo/commands/uninstall.py`, `src/ievo/cli.py`, `tests/test_uninstall.py`
**Time estimate**: ~15 minutes

### TASK-010-4: Integration tests -- init-then-uninstall round-trip

**Scope**: End-to-end test: `init` creates project with marker -> `uninstall --force` cleans up -> verify only integration files removed, `.ievo/` preserved, CLAUDE.md body preserved. Also test re-init after uninstall.
**Tests**: ~4
**Files**: `tests/test_uninstall.py` (additional tests)
**Time estimate**: ~8 minutes

### TASK-010-5: Coverage verification and cleanup

**Scope**: Run full test suite, verify 100% coverage on all changed files, fix any gaps, run pre-commit.
**Tests**: 0 new (fix uncovered branches if any)
**Files**: any of the above if coverage gaps found
**Time estimate**: ~5 minutes

## Micro-Steps

### Step 1: Content verification tests for marketplace .md edits -- Red

Write `tests/test_marketplace_content.py` (NEW file) with content verification tests that assert the expected strings exist in bundled marketplace files. These tests will fail before the edits are made.

**Tests to write:**

1. `test_evolution_agent_has_kernel_target_classification` -- Read `src/ievo/marketplace/agents/evolution.md`. Assert it contains `.ievo/evolution/iEVO.md` as a log target path.
2. `test_evolution_agent_curator_issue_has_target_field` -- Read `src/ievo/marketplace/agents/evolution.md`. Assert the curator issue body template contains `**Target:**` (replacing the old `**Target agent:**`).
3. `test_ievo_md_template_has_kernel_overlay_section` -- Read `src/ievo/marketplace/templates/IEVO.md`. Assert it contains "Kernel overlay" subsection text and `.ievo/evolution/iEVO.md`.
4. `test_all_agents_context_loading_includes_kernel_overlay` -- For each of the 13 agent files in `src/ievo/marketplace/agents/`, read the file, assert it contains `.ievo/evolution/iEVO.md` in the text (within the Context Loading area).
5. `test_evolution_agent_step4_includes_kernel_target` -- Read `src/ievo/marketplace/agents/evolution.md`. Assert Step 4 mentions writing to `.ievo/evolution/iEVO.md` when the target is pipeline rules.

**Criteria covered**: AC Part A (all 4 criteria)
**Files**: `tests/test_marketplace_content.py`
**Commit**: `REQ-010: step 1 -- red content verification tests for marketplace .md edits`

### Step 2: Edit bundled marketplace .md files -- Green

Edit all bundled `.md` files to make the content verification tests pass.

**2a. Evolution agent (`src/ievo/marketplace/agents/evolution.md`)**:

In "### Step 1: Log locally" (around line 119-133), add target classification logic BEFORE the existing log entry template:

Replace the current instruction:
```
Append to `.ievo/evolution/<agent-name>.md` (the agent that made the error, NOT `evolution.md`):
```

With:
```
Classify the finding target:

- **Agent-level finding** (root cause is in an agent's own instructions or behavior):
  Log target: `.ievo/evolution/<agent-name>.md`
- **Kernel-level finding** (root cause is in pipeline rules -- e.g., IEVO.md conventions, pipeline ordering, naming conventions, document lifecycle):
  Log target: `.ievo/evolution/iEVO.md`

Append to the appropriate target file:
```

In "### Step 3: Create GitHub issue in curator" (around line 153-180), replace `**Target agent:**` with:

```
**Target:** <iEVO.md (kernel) | agents/<agent-name>.md>
```

In "### Step 4: Propagate to working docs" (around line 184-192), add kernel path handling:

After the existing numbered list item 2 ("Write a concise rule to `.ievo/evolution/<agent-name>.md`"), add:
```
   - For agent-level findings: write to `.ievo/evolution/<agent-name>.md`
   - For kernel-level findings: write to `.ievo/evolution/iEVO.md`
```

**2b. IEVO.md template (`src/ievo/marketplace/templates/IEVO.md`)**:

In the "## Evolution Overlay" section, after the "### Rules" subsection (after line ~201) and before "## Naming Conventions" (line ~203), insert a new subsection:

```markdown

### Kernel overlay

When an evolution finding targets the pipeline rules themselves (not an individual agent), it is logged to `.ievo/evolution/iEVO.md` -- the **kernel evolution overlay**.

- **Same format** as per-agent overlays (append-only, same YAML structure).
- **Read by all agents** via Context Loading (`.ievo/evolution/iEVO.md` -- read if exists).
- **Purpose**: lessons about pipeline conventions, document lifecycle rules, naming conventions, and cross-agent coordination patterns.
- **File**: `.ievo/evolution/iEVO.md`

The evolution agent decides the target:
- Root cause in an agent's own behavior -> `.ievo/evolution/<agent>.md`
- Root cause in pipeline rules (IEVO.md conventions, ordering, lifecycle) -> `.ievo/evolution/iEVO.md`
```

**2c. All 13 agent `.md` files in `src/ievo/marketplace/agents/`**:

For each agent, add the following line to the **FIRST** numbered list in the Context Loading section. Insert it as the LAST item in the "read these files before doing anything" list, just before the blank line that separates it from the "LAST" section.

The new line to add (as the last numbered item):
```
N. `.ievo/evolution/iEVO.md` -- kernel evolution overlay (read if exists)
```

Where `N` is the next number after the current last item. Specific edits per agent:

| Agent | Current last # | New line # |
|-------|---------------|------------|
| acceptance.md | 4 | 5 |
| architect.md | 7 | 8 |
| code-reviewer.md | 7 | 8 |
| coder.md | 5 | 6 |
| debugger.md | (no section) | Add Context Loading section with items 1-2 |
| defrag.md | 5 | 6 |
| docs.md | 5 | 6 |
| evolution.md | 5 | 6 |
| hr.md | 4 | 5 |
| qa.md | 5 | 6 |
| researcher.md | 5 | 6 |
| spec-writer.md | 6 | 7 |
| tech-lead.md | 8 | 9 |

**Special case -- debugger.md**: This agent has no Context Loading section. Add one after the frontmatter/intro section and before "## Workflow". The section should follow the minimal pattern used by acceptance.md:

```markdown
## Context Loading

**FIRST -- read these files before doing anything:**
1. `.ievo/evolution/debugger.md` -- local evolution rules (if exists)
2. `.ievo/evolution/iEVO.md` -- kernel evolution overlay (read if exists)
```

All 5 content verification tests from Step 1 must pass.

**Criteria covered**: AC Part A (all 4 criteria)
**Files**: `src/ievo/marketplace/agents/evolution.md`, `src/ievo/marketplace/templates/IEVO.md`, all 13 `src/ievo/marketplace/agents/*.md`
**Commit**: `REQ-010: step 2 -- add kernel evolution target to evolution agent, IEVO.md, and all 13 agents`

### Step 3: `patch_claude_md()` tests -- Red

Write failing tests in `tests/test_init.py`.

**Tests to write:**

1. `test_patch_claude_md_new_file_from_template` -- Run `init(name="patch-test")`. Read `CLAUDE.md`. Assert it contains `<!-- ievo:start -->` and `<!-- ievo:end -->` and `Read \`.ievo/IEVO.md\``.
2. `test_patch_claude_md_existing_file_without_marker` -- Create a `CLAUDE.md` with custom content (no marker). Create `.ievo/manifest.yaml` so the project exists but CLAUDE.md pre-exists. Call `patch_claude_md(path)`. Assert returns `True`. Assert file now contains the marker block AND the original content is preserved before the marker.
3. `test_patch_claude_md_existing_file_with_marker` -- Create a `CLAUDE.md` that already contains `<!-- ievo:start -->`. Call `patch_claude_md(path)`. Assert returns `False`. Assert file is unchanged.
4. `test_patch_claude_md_nonexistent_file` -- Call `patch_claude_md(path)` with a path that does not exist. Assert returns `False`.
5. `test_patch_claude_md_file_without_trailing_newline` -- Create a `CLAUDE.md` ending without `\n`. Call `patch_claude_md(path)`. Assert the marker block is separated from existing content by exactly one blank line.
6. `test_patch_claude_md_idempotent_during_init` -- Run `init(name="idem-test")`. Read `CLAUDE.md`. Count occurrences of `<!-- ievo:start -->`. Assert exactly 1.
7. `test_claude_md_template_contains_marker` -- Import `CLAUDE_MD` from `init_templates`. Assert `"<!-- ievo:start -->"` in `CLAUDE_MD` and `"<!-- ievo:end -->"` in `CLAUDE_MD`.
8. `test_init_with_preexisting_claude_md_patches_it` -- Pre-create `CLAUDE.md` without marker. Run `init()` (which does `_write_if_missing` -- skips writing since file exists). Then `patch_claude_md()` is called and appends the block. Assert both original content and marker are present.

**Criteria covered**: AC Part B (all 5 criteria)
**Files**: `tests/test_init.py`
**Commit**: `REQ-010: step 3 -- red tests for patch_claude_md and CLAUDE_MD template`

### Step 4: `CLAUDE_MD` template + `patch_claude_md()` -- Green

**4a. Edit `src/ievo/commands/init_templates.py`**:

Add the marker block to the end of the `CLAUDE_MD` string constant, after the "## What NOT to do" section:

```python
CLAUDE_MD = """\
# {project_name}

<!-- Fill this before running agents. It is read at the start of every session. -->

## What this project is
...
## What NOT to do

<!-- Anti-patterns, explicitly forbidden approaches, deprecated paths -->

<!-- ievo:start -->
## iEvo Pipeline

Read `.ievo/IEVO.md` for pipeline conventions.
If `.ievo/evolution/iEVO.md` exists, read it for kernel evolution overlay rules.
<!-- ievo:end -->
"""
```

**4b. Add `patch_claude_md()` to `src/ievo/commands/init.py`**:

```python
# Marker block constants
IEVO_MARKER_START = "<!-- ievo:start -->"
IEVO_MARKER_END = "<!-- ievo:end -->"

IEVO_MARKER_BLOCK = """\
<!-- ievo:start -->
## iEvo Pipeline

Read `.ievo/IEVO.md` for pipeline conventions.
If `.ievo/evolution/iEVO.md` exists, read it for kernel evolution overlay rules.
<!-- ievo:end -->
"""


def patch_claude_md(path: Path) -> bool:
    """Inject iEvo marker block into CLAUDE.md if not already present.

    Returns True if the patch was applied, False if already present or
    the file does not exist.
    """
    if not path.is_file():
        return False

    content = path.read_text()

    if IEVO_MARKER_START in content:
        return False

    # Ensure separation from existing content
    if content and not content.endswith("\n"):
        content += "\n"
    if content and not content.endswith("\n\n"):
        content += "\n"

    content += IEVO_MARKER_BLOCK
    path.write_text(content)
    return True
```

**4c. Call `patch_claude_md()` from `init()` in `src/ievo/commands/init.py`**:

After the `_write_if_missing(root / "CLAUDE.md", ...)` call (line 132-133), add:

```python
    # Patch CLAUDE.md with iEvo marker block (idempotent -- skips if already present)
    claude_md_path = root / "CLAUDE.md"
    if patch_claude_md(claude_md_path):
        step("CLAUDE.md (patched with iEvo pipeline reference)")
```

Note: If the file was just written by `_write_if_missing` from the updated template, it already contains the marker, so `patch_claude_md()` returns `False` immediately. If the file pre-existed without the marker, `patch_claude_md()` appends it.

All 8 tests from Step 3 must pass. All existing `test_init.py` tests must still pass (update `test_init_claude_md_has_scaffold` if needed to also assert the marker is present).

**Criteria covered**: AC Part B (all 5 criteria)
**Files**: `src/ievo/commands/init_templates.py`, `src/ievo/commands/init.py`, `tests/test_init.py`
**Commit**: `REQ-010: step 4 -- implement CLAUDE_MD marker template and patch_claude_md function`

### Step 5: `ievo uninstall` command tests -- Red

Write `tests/test_uninstall.py` (NEW file).

**Tests to write:**

1. `test_uninstall_no_project_exits_with_error` -- `tmp_path` with no `.ievo/manifest.yaml`. Run `uninstall()`. Assert exit code 1. Assert output contains `"No iEvo project found"`.
2. `test_uninstall_user_declines_no_changes` -- Create a project with all integration files. Patch `typer.confirm` to return `False`. Run `uninstall()`. Assert all files still exist.
3. `test_uninstall_force_removes_marker_block` -- Create `CLAUDE.md` with content + marker block. Run `uninstall(force=True)`. Assert marker block is removed. Assert rest of content preserved exactly.
4. `test_uninstall_force_skips_claude_md_without_marker` -- Create `CLAUDE.md` without marker. Run `uninstall(force=True)`. Assert file is unchanged.
5. `test_uninstall_force_removes_hooks_file` -- Create `.claude/hooks/precompact_save.py`. Run `uninstall(force=True)`. Assert file is gone.
6. `test_uninstall_force_removes_settings_local` -- Create `.claude/settings.local.json`. Run `uninstall(force=True)`. Assert file is gone.
7. `test_uninstall_force_removes_agent_md_files` -- Create `.claude/agents/spec-writer.md` and `.claude/agents/coder.md`. Run `uninstall(force=True)`. Assert both are gone. Assert `.claude/agents/` directory still exists.
8. `test_uninstall_force_removes_workflow_files` -- Create all 3 workflow files. Run `uninstall(force=True)`. Assert all gone.
9. `test_uninstall_force_removes_issue_templates` -- Create both issue template files. Run `uninstall(force=True)`. Assert both gone.
10. `test_uninstall_force_preserves_ievo_dir` -- Create full project. Run `uninstall(force=True)`. Assert `.ievo/` and `.ievo/manifest.yaml` still exist.
11. `test_uninstall_force_preserves_priority_md` -- Create `PRIORITY.md`. Run `uninstall(force=True)`. Assert still exists.
12. `test_uninstall_force_preserves_gitignore` -- Create `.gitignore`. Run `uninstall(force=True)`. Assert still exists.
13. `test_uninstall_force_skips_missing_files` -- Create project with only `.ievo/manifest.yaml` and nothing else. Run `uninstall(force=True)`. No error, no crash. Assert success message printed.
14. `test_uninstall_prints_summary_before_prompt` -- Create project with some integration files. Capture output. Assert summary lists existing files. Assert files that don't exist are omitted.

**Criteria covered**: AC Part C (all 10 criteria) + Negative ACs
**Files**: `tests/test_uninstall.py`
**Commit**: `REQ-010: step 5 -- red tests for ievo uninstall command`

### Step 6: `ievo uninstall` command -- Green

**6a. Create `src/ievo/commands/uninstall.py`**:

```python
"""ievo uninstall -- remove iEvo integration files from a project."""

from __future__ import annotations

import re
from pathlib import Path

import typer

from ievo.core.config import find_project_root
from ievo.utils.console import error, info, step, success

# Marker block regex -- matches the block and surrounding blank lines
_MARKER_RE = re.compile(r"\n*<!-- ievo:start -->[\s\S]*?<!-- ievo:end -->\n*")

# Files to remove (relative to project root)
_INTEGRATION_FILES = [
    ".claude/hooks/precompact_save.py",
    ".claude/settings.local.json",
    ".github/workflows/spec-writer.yml",
    ".github/workflows/architect-agent.yml",
    ".github/workflows/coder-agent.yml",
    ".github/ISSUE_TEMPLATE/feature.md",
    ".github/ISSUE_TEMPLATE/change.md",
]


def _unpatch_claude_md(path: Path) -> bool:
    """Remove iEvo marker block from CLAUDE.md.

    Returns True if the block was found and removed, False otherwise.
    """
    if not path.is_file():
        return False
    content = path.read_text()
    if "<!-- ievo:start -->" not in content:
        return False
    new_content = _MARKER_RE.sub("\n", content)
    # Strip trailing whitespace but keep one trailing newline
    new_content = new_content.rstrip() + "\n" if new_content.strip() else ""
    path.write_text(new_content)
    return True


def uninstall(
    force: bool = typer.Option(
        False,
        "--force",
        help="Skip confirmation prompt.",
    ),
) -> None:
    """Remove iEvo integration files from the project.

    Removes Claude Code hooks, agent files, workflow files, and the
    CLAUDE.md marker block. Project data (.ievo/) is preserved.
    """
    root = find_project_root()
    if root is None:
        error("No iEvo project found in this directory or any parent.")
        raise typer.Exit(1)

    # Collect files that exist
    claude_md = root / "CLAUDE.md"
    has_marker = claude_md.is_file() and "<!-- ievo:start -->" in claude_md.read_text()

    existing_files: list[tuple[str, Path]] = []
    if has_marker:
        existing_files.append(("CLAUDE.md       patch block", claude_md))

    for rel in _INTEGRATION_FILES:
        p = root / rel
        if p.is_file():
            existing_files.append((rel, p))

    # Agent .md files in .claude/agents/
    agents_dir = root / ".claude" / "agents"
    agent_files: list[Path] = []
    if agents_dir.is_dir():
        agent_files = sorted(agents_dir.glob("*.md"))
        for af in agent_files:
            existing_files.append((f".claude/agents/{af.name}", af))

    if not existing_files:
        info("No integration files found to remove.")
        success("iEvo integration removed. Run ievo init to reinstall.")
        raise typer.Exit(0)

    # Print summary
    info("[bold]Files to remove:[/bold]")
    for label, _ in existing_files:
        step(label)

    if not force:
        if not typer.confirm(
            "\nRemove iEvo integration? (project data is kept)",
            default=False,
        ):
            raise typer.Exit(0)

    # Execute removals
    if has_marker:
        _unpatch_claude_md(claude_md)
        step("CLAUDE.md patch block removed")

    for rel in _INTEGRATION_FILES:
        p = root / rel
        if p.is_file():
            p.unlink()

    for af in agent_files:
        if af.is_file():
            af.unlink()

    success("iEvo integration removed. Run ievo init to reinstall.")
```

**6b. Register in `src/ievo/cli.py`**:

Add import:
```python
from ievo.commands import uninstall
```

Add under `# -- Active commands`:
```python
app.command(name="uninstall", help="Remove iEvo integration files from the project.")(
    uninstall.uninstall
)
```

All 14 tests from Step 5 must pass. Run full test suite to verify no regressions.

**Criteria covered**: AC Part C (all 10 criteria) + Negative ACs
**Files**: `src/ievo/commands/uninstall.py`, `src/ievo/cli.py`, `tests/test_uninstall.py`
**Commit**: `REQ-010: step 6 -- implement ievo uninstall command`

### Step 7: Integration tests -- init-then-uninstall round-trip

Add integration tests to `tests/test_uninstall.py`.

**Tests to write:**

1. `test_init_then_uninstall_roundtrip` -- Run `init(name="roundtrip")`. Assert CLAUDE.md has marker. Run `uninstall(force=True)` in that project. Assert marker removed. Assert `.ievo/` preserved. Assert `.claude/agents/` dir exists but empty of `.md` files.
2. `test_uninstall_preserves_claude_md_body` -- Create CLAUDE.md with custom content + marker. Run `uninstall(force=True)`. Assert custom content is preserved exactly (no trailing double-blank or missing newlines).
3. `test_uninstall_then_reinit` -- Run `init()`. Run `uninstall(force=True)`. Remove `.ievo/manifest.yaml` to allow re-init. Run `init()` again. Assert marker block is present again. Assert all files recreated.
4. `test_uninstall_marker_at_different_positions` -- Test marker block removal when block is at: (a) end of file, (b) middle of file (between two content sections), (c) start of file. Assert in all cases surrounding content is preserved cleanly.

**Criteria covered**: Cross-cutting integration validation of Parts B and C
**Files**: `tests/test_uninstall.py`
**Commit**: `REQ-010: step 7 -- integration tests for init-uninstall round-trip`

### Step 8: Coverage verification and cleanup

Run `uv run pytest --cov --cov-report=term-missing` and verify 100% coverage on:
- `src/ievo/commands/init.py` -- `patch_claude_md()` function, marker constants, call site
- `src/ievo/commands/init_templates.py` -- `CLAUDE_MD` constant (imported in tests)
- `src/ievo/commands/uninstall.py` -- all branches (`_unpatch_claude_md`, `uninstall`, force/no-force, empty/non-empty)
- `src/ievo/cli.py` -- `uninstall` registration

Fix any uncovered branches. Run `uv run pre-commit run --files <all-changed-files>`.

**Files**: potentially any of the above if coverage gaps found
**Commit**: `REQ-010: step 8 -- coverage verification and cleanup`

## Traceability

| Acceptance Criterion | Covered in Step |
|---------------------|-----------------|
| Part A: evolution.md kernel target classification | Steps 1-2 |
| Part A: evolution.md curator issue **Target:** field | Steps 1-2 |
| Part A: IEVO.md "Kernel overlay" subsection | Steps 1-2 |
| Part A: All 13 agents include `.ievo/evolution/iEVO.md` in Context Loading | Steps 1-2 |
| Part B: init creates CLAUDE.md with marker block | Steps 3-4 |
| Part B: init appends marker to existing CLAUDE.md without marker | Steps 3-4 |
| Part B: init skips CLAUDE.md that already has marker (idempotent) | Steps 3-4 |
| Part B: CLAUDE_MD template contains marker block | Steps 3-4 |
| Part B: patch_claude_md() is standalone function returning bool | Steps 3-4 |
| Part C: uninstall no project -> error exit 1 | Steps 5-6 |
| Part C: uninstall user declines -> no changes | Steps 5-6 |
| Part C: uninstall --force skips prompt | Steps 5-6 |
| Part C: uninstall removes marker block, preserves rest | Steps 5-6, 7 |
| Part C: uninstall skips CLAUDE.md without marker | Steps 5-6 |
| Part C: uninstall removes 9 target files | Steps 5-6 |
| Part C: uninstall preserves .ievo/, PRIORITY.md, .gitignore | Steps 5-6 |
| Part C: uninstall prints summary before prompt | Steps 5-6 |
| Part C: uninstall prints success message after removal | Steps 5-6 |
| Part C: uninstall registered as active (non-deprecated) command | Steps 5-6 |
| Negative: .ievo/ never deleted | Steps 5-6, 7 |
| Negative: .claude/agents/ dir preserved | Steps 5-6, 7 |
| Negative: CLAUDE.md body outside marker preserved | Steps 5-6, 7 |

## Scope Guard

- **Considered but excluded**: A shared `claude_md.py` module in `core/` -- spec explicitly places `patch_claude_md` in `init.py`. The unpatch logic is only used by `uninstall.py`. No shared module needed.
- **Considered but excluded**: TUI integration for uninstall (e.g., button in TUI) -- spec only defines a CLI command.
- **Considered but excluded**: Automatic kernel overlay file creation during init -- `.ievo/evolution/iEVO.md` is created by the evolution agent when needed, not by init.
- **Considered but excluded**: Removing empty parent directories (`.github/workflows/`, `.github/ISSUE_TEMPLATE/`) after uninstall -- spec says to remove files only.
- **Considered but excluded**: Version-specific marker blocks (embedding CLI version in the marker) -- spec defines a fixed block format.

## Architecture Notes for Coder

1. **`patch_claude_md()` lives in `src/ievo/commands/init.py`**, not in a new `core/claude_md.py` module. The spec explicitly states this (AC Part B). The constants `IEVO_MARKER_START`, `IEVO_MARKER_END`, and `IEVO_MARKER_BLOCK` should also be defined in `init.py` since they are only imported by `init.py` and `test_init.py`. The `_unpatch_claude_md()` function in `uninstall.py` can define its own regex inline.

2. **The marker block text must be identical** in three places: `IEVO_MARKER_BLOCK` constant in `init.py`, the end of `CLAUDE_MD` template in `init_templates.py`, and the regex capture in `uninstall.py`. Use the exact text from the spec:
   ```
   <!-- ievo:start -->
   ## iEvo Pipeline

   Read `.ievo/IEVO.md` for pipeline conventions.
   If `.ievo/evolution/iEVO.md` exists, read it for kernel evolution overlay rules.
   <!-- ievo:end -->
   ```

3. **`uninstall.py` uses `find_project_root()`**, not `require_project()`**. The error message differs: `require_project()` says "Not in an iEvo project. Run ievo init first." but the spec requires "No iEvo project found in this directory or any parent." Write the custom error message directly.

4. **`.claude/agents/*.md` removal**: Use `pathlib.Path.glob("*.md")` to find all agent `.md` files. Do NOT hardcode agent names -- the user may have added custom agents. Remove all `.md` files in that directory.

5. **`_unpatch_claude_md()` regex edge cases**: The regex `\n*<!-- ievo:start -->[\s\S]*?<!-- ievo:end -->\n*` eats leading and trailing newlines around the block. After substitution with `"\n"`, strip trailing whitespace and ensure exactly one trailing newline. Test with block at start, middle, and end of file.

6. **Existing test updates**: `test_init_claude_md_has_scaffold` (line 150 in `tests/test_init.py`) currently asserts `CLAUDE.md` contains `# claude-test`, `Tech stack`, `Conventions`, `What this project is`. After adding the marker block to the template, these assertions still pass (the marker is appended at the end). But add a new assertion for the marker: `assert "<!-- ievo:start -->" in content`.

7. **`test_init_skips_existing_files` (line 242)**: This test pre-creates `CLAUDE.md` with content `"# Existing CLAUDE.md"` and asserts it is not overwritten. After the patch, `patch_claude_md()` will append the marker block to it. Update the assertion to expect both the original content and the marker block. OR add `<!-- ievo:start -->` to the pre-created content so that `patch_claude_md()` returns `False` and the file is truly unchanged.

8. **Debugger agent**: The debugger agent (`src/ievo/marketplace/agents/debugger.md`) has no Context Loading section. Add a minimal one between the intro paragraph and `## Workflow`. Include only the debugger's own evolution overlay and the kernel overlay -- the debugger is invoked reactively and does not need full pipeline context on startup.

9. **Import ordering in `cli.py`**: When adding the `uninstall` import, add it to the existing `from ievo.commands import (...)` block alphabetically. The registration line goes under `# -- Active commands`, on a new line after the `update` registration.

10. **Console output in uninstall**: Use `info()` for the summary header, `step()` for each file, `success()` for the final message, `error()` for the no-project case. This matches the patterns used in `init.py`.

11. **Content verification tests**: The `tests/test_marketplace_content.py` file reads the bundled `.md` files directly from the filesystem using `pathlib`. Import `bundled_agent_path` from `ievo.marketplace` to locate agent files, or use `Path(__file__).parent.parent / "src/ievo/marketplace/agents/"`. The simpler approach is to use the `importlib.resources` path or just relative path from the test. Check how existing marketplace tests locate bundled files (e.g., `tests/test_marketplace.py`).

12. **`uninstall` prompt**: Use `typer.confirm()` with `default=False` (default No). The prompt text is `"Remove iEvo integration? (project data is kept)"`. When user answers No, exit silently with code 0 -- no additional output.
