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 68259d6..d245062 100644
--- a/ftl_code_expert/cli.py
+++ b/ftl_code_expert/cli.py
@@ -24,6 +24,7 @@
     get_imports,
     get_repo_structure,
     list_commits_with_files,
+    list_source_files,
     load_diff_checkpoint,
     save_diff_checkpoint,
 )
@@ -44,6 +45,7 @@
     build_scan_prompt,
 )
 from .topics import (
+    Topic,
     add_topics,
     load_queue,
     parse_topics_from_response,
@@ -364,8 +366,20 @@ def scan(ctx):
     repo_name = os.path.basename(repo_path)
     _create_entry(f"scan-{repo_name}", f"Scan: {repo_name}", result)
 
-    # Enqueue topics
-    _enqueue_topics(result, source=f"scan:{repo_name}", project_dir=_get_project_dir(ctx))
+    project_dir = _get_project_dir(ctx)
+
+    # Enumerate all source files as file topics (BFS: breadth before depth)
+    source_files = list_source_files(repo_path)
+    if source_files:
+        file_topics = [
+            Topic(title=f, kind="file", target=f, source=f"scan:{repo_name}")
+            for f in source_files
+        ]
+        added = add_topics(file_topics, project_dir)
+        click.echo(f"Queued {added} file(s) for exploration", err=True)
+
+    # Enqueue LLM-suggested topics (functions, generals appended after files)
+    _enqueue_topics(result, source=f"scan:{repo_name}", project_dir=project_dir)
 
     _emit(ctx, result)
 
@@ -1334,7 +1348,6 @@ def walk_commits(ctx, since, since_commit, since_last, dry_run):
         sys.exit(1)
 
     # Build topics for each unique file
-    from .topics import Topic
     timeout = ctx.obj["timeout"]
     parallel = ctx.obj["parallel"]
     all_topics = []
@@ -3785,7 +3798,6 @@ def research(ctx, review_file, limit, dry_run):
         return
 
     # Step 6: Build topics and explore
-    from .topics import Topic
     topics = [
         Topic(
             title=f"Research: evidence for {', '.join(belief_ids)}",
diff --git a/ftl_code_expert/git_utils.py b/ftl_code_expert/git_utils.py
index a0bccae..51f3ecf 100644
--- a/ftl_code_expert/git_utils.py
+++ b/ftl_code_expert/git_utils.py
@@ -185,6 +185,40 @@ def get_file_content(path: str) -> str | None:
         return None
 
 
+BINARY_EXTENSIONS = frozenset({
+    ".pyc", ".pyo", ".so", ".o", ".a", ".dylib",
+    ".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg", ".webp",
+    ".woff", ".woff2", ".ttf", ".eot",
+    ".mp3", ".mp4", ".wav", ".avi", ".mov",
+    ".zip", ".tar", ".gz", ".bz2", ".xz", ".7z",
+    ".jar", ".war", ".class", ".exe", ".dll",
+    ".bin", ".dat", ".db", ".sqlite", ".sqlite3", ".pdf",
+})
+
+
+def list_source_files(repo_path: str) -> list[str]:
+    """List all tracked source files using git ls-files.
+
+    Filters out binary files by extension. Returns paths relative to repo root.
+    """
+    result = subprocess.run(
+        ["git", "ls-files"],
+        capture_output=True, text=True, cwd=repo_path,
+    )
+    if result.returncode != 0:
+        return []
+    files = []
+    for line in result.stdout.splitlines():
+        line = line.strip()
+        if not line:
+            continue
+        ext = os.path.splitext(line)[1].lower()
+        if ext not in BINARY_EXTENSIONS:
+            files.append(line)
+    files.sort()
+    return files
+
+
 def get_repo_structure(repo_path: str, max_depth: int = 4) -> str:
     """
     Get filtered directory tree of a repository.

```

## 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:
