You are a senior code reviewer preparing to review code changes.

## Code Changes

```diff
diff --git a/ftl_code_expert/cli.py b/ftl_code_expert/cli.py
index 0536bfc..8c22305 100644
--- a/ftl_code_expert/cli.py
+++ b/ftl_code_expert/cli.py
@@ -2986,6 +2986,44 @@ def _build_negative_issue_body(belief: dict) -> str:
     return "\n".join(lines)
 
 
+def _ensure_labels(platform: str, repo_slug: str, required: set[str]) -> None:
+    """Create any missing labels on the target repo."""
+    if platform == "github":
+        result = subprocess.run(
+            ["gh", "label", "list", "--repo", repo_slug, "-L", "1000",
+             "--json", "name", "-q", ".[].name"],
+            capture_output=True, text=True,
+        )
+        existing = set(result.stdout.strip().splitlines()) if result.returncode == 0 else set()
+        for label in required - existing:
+            click.echo(f"  Creating label: {label}", err=True)
+            subprocess.run(
+                ["gh", "label", "create", label, "--repo", repo_slug,
+                 "--description", "Auto-created by code-expert file-issues"],
+                capture_output=True, text=True,
+            )
+    elif platform == "gitlab":
+        result = subprocess.run(
+            ["glab", "label", "list", "--repo", repo_slug, "-F", "json"],
+            capture_output=True, text=True,
+        )
+        existing: set[str] = set()
+        if result.returncode == 0 and result.stdout.strip():
+            try:
+                for item in json.loads(result.stdout):
+                    name = item.get("name", "")
+                    if name:
+                        existing.add(name)
+            except (json.JSONDecodeError, TypeError):
+                pass
+        for label in required - existing:
+            click.echo(f"  Creating label: {label}", err=True)
+            subprocess.run(
+                ["glab", "label", "create", label, "--repo", repo_slug],
+                capture_output=True, text=True,
+            )
+
+
 def _create_issue(platform: str, repo_slug: str, title: str, body: str,
                   labels: list[str]) -> str | None:
     """Create an issue and return its URL, or None on failure."""
@@ -3494,6 +3532,11 @@ def file_issues(ctx, repo_slug, platform_override, labels, dry_run, skip_confirm
             click.echo(f"  {unconfirmed} belief(s) no longer present in code", err=True)
         remaining = confirmed
 
+    # Ensure required labels exist
+    if not dry_run and remaining:
+        required_labels = {"reasons-gate", "reasons-negative"} | set(labels)
+        _ensure_labels(platform, repo_slug, required_labels)
+
     # File issues
     filed = []
     skipped_ids = list(existing)
diff --git a/ftl_code_expert/data/CLAUDE.md.template b/ftl_code_expert/data/CLAUDE.md.template
index 985bdeb..c65a552 100644
--- a/ftl_code_expert/data/CLAUDE.md.template
+++ b/ftl_code_expert/data/CLAUDE.md.template
@@ -56,6 +56,45 @@ reasons compact
 code-expert update --since-last      # walk commits + propose + derive + summary
 code-expert generate-summary         # standalone morning summary
 
+# Verification
+code-expert verify <belief-id>           # check one belief against current code
+code-expert verify --all                 # verify all IN beliefs
+code-expert verify --gated               # verify beliefs blocking downstream chains
+code-expert verify --negative --retract  # verify negative beliefs, retract stale ones
+code-expert infer-sources --all          # infer source files for beliefs missing them
+
 # Status
 code-expert status
 ```
+
+## Using the Reasons Database
+
+When a `reasons.db` exists, **search beliefs before reading code**. The belief network contains verified claims about the codebase — architecture, patterns, bugs, invariants — that persist across sessions.
+
+### Answering questions about the code
+
+1. `reasons search "<question>"` — find beliefs relevant to the question
+2. `reasons show <belief-id>` — read the full belief, its justification chain, and metadata
+3. `reasons explain <belief-id>` — trace why a belief is IN or OUT
+4. Read the actual source code to confirm the belief still holds
+5. If a belief is stale, run `code-expert verify <belief-id>` to formally check it
+
+### Before modifying code
+
+1. `reasons search "<area being changed>"` — find beliefs about the affected code
+2. Check for negative beliefs (bugs, gaps, risks) that the change might interact with
+3. Check for gated beliefs — changes might unblock or break downstream reasoning chains
+4. After making changes, verify affected beliefs: `code-expert verify --category <keyword>`
+
+### Key reasons commands
+
+```bash
+reasons search "thread safety"     # semantic search across all beliefs
+reasons show <id>                  # full details including dependents
+reasons explain <id>               # trace justification chain (why IN or OUT)
+reasons list                       # all nodes with status
+reasons list --negative            # bugs, gaps, risks
+reasons list --gated               # beliefs blocked by negative findings
+```
+
+The belief network is the project's accumulated knowledge. Searching it first avoids re-discovering what is already known and surfaces constraints that pure code reading would miss.

```

## Your Task

Analyze the diff and identify what additional information you need to render confident verdicts.
Do NOT render verdicts yet. Only request observations.

## Available Observation Tools

| Tool | Purpose | When to use |
|------|---------|-------------|
| `exception_hierarchy` | Show exception MRO and subclasses | Retry logic, exception handling |
| `raises_analysis` | What exceptions a function raises | New function calls, error paths |
| `call_graph` | What a function calls | Impact analysis |
| `find_usages` | Where a symbol is used (with prod/test split) | Quick integration lookup |
| `find_callers` | Caller analysis with prod/test split and calling context | Method signature changes, return type changes, constructor modifications, integration verification |
| `test_coverage` | Find tests for a file (uses coverage-map if available) | Test coverage claims |
| `coverage_map_tests` | Find tests covering a file (from coverage-map.json) | Precise test coverage from actual execution |
| `coverage_map_files` | Find files covered by tests matching a pattern | Impact analysis for test changes |
| `function_body` | Full source of a function/method | Need complete function context beyond diff hunks |
| `file_imports` | Extract imports from a file | Verify import changes, check dependencies |
| `project_dependencies` | Get pyproject.toml/requirements.txt | Verify new imports have dependencies |
| `related_test_files` | Find test files for a source file | Discover tests by naming, imports, and coverage map |
| `class_hierarchy` | Show base classes and their `__init__` signatures | Class changes its parent, modifies `__init__`, or uses `super()` |
| `symbol_migration` | Check if a rename is complete across the repo | Symbol renamed in diff — verify old name is fully removed |
| `generator_info` | Report whether a function uses `yield` | Function might be a generator — affects return value semantics |

## What to Look For

1. **Exception handling**: Any `retry_if_exception_type`, `except`, or exception class references
2. **New dependencies**: Calls to external libraries where you don't know the error behavior
3. **Behavioral changes**: Modified logic where you need to verify callers/callees
4. **Test claims**: References to tests you can't see in the diff
5. **Inheritance changes**: Class definition changes, new base classes, `super()` calls
6. **Renames**: Symbols that appear to have been renamed in the diff
7. **Factory methods**: Calls to `@classmethod` / `@staticmethod` constructors (e.g. `Result.error(...)`) — request `function_body` to see their implementation

## Output Format

Output a JSON array of observation requests:

```json
[
  {"name": "descriptive_name", "tool": "tool_name", "params": {"param": "value"}},
  ...
]
```

If you don't need any observations (simple changes, all context is in the diff), output:

```json
[]
```

## Examples

For a diff containing `retry_if_exception_type((OSError, httpx.TransportError))`:
```json
[
  {"name": "oserror_subclasses", "tool": "exception_hierarchy", "params": {"class_name": "builtins.OSError"}},
  {"name": "transport_errors", "tool": "exception_hierarchy", "params": {"class_name": "httpx.TransportError"}}
]
```

For a diff adding a new function that calls `oauth_client.get_access_token()`:
```json
[
  {"name": "oauth_exceptions", "tool": "raises_analysis", "params": {"file_path": "src/auth/oauth.py", "function_name": "get_access_token"}}
]
```

For a diff modifying a method but you need the full function to verify:
```json
[
  {"name": "full_getattr", "tool": "function_body", "params": {"file_path": "src/proxy.py", "function_name": "__getattr__"}}
]
```

For a diff changing a method signature or return type (verify all callers):
```json
[
  {"name": "handle_request_callers", "tool": "find_callers", "params": {"symbol": "handle_request"}}
]
```

For a diff adding new imports (e.g., `import httpx`):
```json
[
  {"name": "file_imports", "tool": "file_imports", "params": {"file_path": "src/client.py"}},
  {"name": "project_deps", "tool": "project_dependencies", "params": {}}
]
```

For a diff calling a factory method like `ModuleResult.error_result(msg)`:
```json
[
  {"name": "error_result_body", "tool": "function_body", "params": {"file_path": "src/models.py", "function_name": "error_result"}}
]
```

For a diff where a class changes its parent class:
```json
[
  {"name": "client_hierarchy", "tool": "class_hierarchy", "params": {"class_name": "MyClient", "file_path": "src/client.py"}}
]
```

For a diff that renames a symbol (e.g., `OldClient` to `NewClient`):
```json
[
  {"name": "client_rename", "tool": "symbol_migration", "params": {"old_name": "OldClient", "new_name": "NewClient"}}
]
```

For a diff modifying a function that might be a generator:
```json
[
  {"name": "process_gen", "tool": "generator_info", "params": {"file_path": "src/pipeline.py", "function_name": "process_items"}}
]
```

Now analyze the diff above and output your observation requests as JSON:
