You are a senior code reviewer. Review the following code changes.

## Specification

No specification provided. Focus on correctness, tests, and integration.





## Code Changes

```diff
diff --git a/ftl_code_expert/cli.py b/ftl_code_expert/cli.py
index 97d34c9..0cebfd5 100644
--- a/ftl_code_expert/cli.py
+++ b/ftl_code_expert/cli.py
@@ -1648,6 +1648,13 @@ def _accept_proposals(matches: list[tuple[str, str, str]]) -> tuple[int, int, in
             if result.returncode == 0:
                 click.echo(f"  Added: {belief_id}")
                 added += 1
+                src_file = _extract_source_file(source.strip())
+                if src_file:
+                    subprocess.run(
+                        ["reasons", "set-metadata", belief_id,
+                         "source_file", src_file],
+                        capture_output=True, text=True,
+                    )
             else:
                 stderr = result.stderr.strip()
                 stdout = result.stdout.strip()
@@ -3005,7 +3012,9 @@ async def _gather_belief_context(
 
     parts: list[str] = []
 
-    src_file = _extract_source_file(source, project_dir)
+    src_file = (node.get("metadata") or {}).get("source_file")
+    if not src_file:
+        src_file = _extract_source_file(source, project_dir)
     if src_file:
         result = await read_file(src_file, repo_path, max_lines=300)
         if "content" in result:

```

## Observation Results

You previously requested observations. Here are the results:

```json
{
  "extract_source_file_body": {
    "function": "_extract_source_file",
    "file": "ftl_code_expert/cli.py",
    "start_line": 2985,
    "end_line": 2997,
    "source": "def _extract_source_file(entry_path: str, project_dir: str | None = None) -> str | None:\n    \"\"\"Extract source file path from an entry's header line (# File: ...).\"\"\"\n    base = project_dir or \".\"\n    full_path = Path(base) / entry_path\n    if not full_path.is_file():\n        return None\n    try:\n        for line in full_path.read_text().splitlines()[:5]:\n            if line.startswith(\"# File: \"):\n                return line[8:].strip()\n    except OSError:\n        pass\n    return None"
  },
  "accept_proposals_body": {
    "function": "_accept_proposals",
    "file": "ftl_code_expert/cli.py",
    "start_line": 1632,
    "end_line": 1708,
    "source": "def _accept_proposals(matches: list[tuple[str, str, str]]) -> tuple[int, int, int]:\n    \"\"\"Import belief proposals into the primary store.\n\n    Returns (added, skipped, failed) counts.\n    \"\"\"\n    if _has_reasons():\n        click.echo(\"Using reasons as primary store...\")\n        added = 0\n        failed = 0\n        skipped = 0\n        for belief_id, claim_text, source in matches:\n            result = subprocess.run(\n                [\"reasons\", \"add\", belief_id, claim_text.strip(),\n                 \"--source\", source.strip()],\n                capture_output=True, text=True,\n            )\n            if result.returncode == 0:\n                click.echo(f\"  Added: {belief_id}\")\n                added += 1\n                src_file = _extract_source_file(source.strip())\n                if src_file:\n                    subprocess.run(\n                        [\"reasons\", \"set-metadata\", belief_id,\n                         \"source_file\", src_file],\n                        capture_output=True, text=True,\n                    )\n            else:\n                stderr = result.stderr.strip()\n                stdout = result.stdout.strip()\n                if \"already exists\" in stderr or \"already exists\" in stdout:\n                    click.echo(f\"  EXISTS: {belief_id}\")\n                    skipped += 1\n                else:\n                    click.echo(f\"  FAIL: {belief_id}: {stderr or stdout}\")\n                    failed += 1\n\n        click.echo(f\"\\nAccepted {added} beliefs ({skipped} existing, {failed} failed)\")\n\n        if added > 0:\n            _reasons_export()\n        return added, skipped, failed\n\n    # Try beliefs batch mode\n    if _accept_batch(matches):\n        return len(matches), 0, 0\n\n    # Fall back to per-belief add via beliefs CLI\n    click.echo(\"Falling back to per-belief add...\")\n    added = 0\n    failed = 0\n    skipped = 0\n    for belief_id, claim_text, source in matches:\n        try:\n            result = subprocess.run(\n                [\"beliefs\", \"add\",\n                 \"--id\", belief_id,\n                 \"--text\", claim_text.strip(),\n                 \"--source\", source.strip()],\n                capture_output=True, text=True,\n            )\n            if result.returncode == 0:\n                click.echo(f\"  Added: {belief_id}\")\n                added += 1\n            else:\n                stderr = result.stderr.strip()\n                if \"already exists\" in stderr or \"already exists\" in result.stdout:\n                    click.echo(f\"  EXISTS: {belief_id}\")\n                    skipped += 1\n                else:\n                    click.echo(f\"  FAIL: {belief_id}: {stderr or result.stdout.strip()}\")\n                    failed += 1\n        except FileNotFoundError:\n            click.echo(\"ERROR: beliefs CLI not found. Install with: uv tool install beliefs\")\n            sys.exit(1)\n\n    click.echo(f\"\\nAccepted {added} beliefs ({skipped} existing, {failed} failed)\")\n    return added, skipped, failed"
  },
  "gather_belief_context_body": {
    "function": "_gather_belief_context",
    "file": "ftl_code_expert/cli.py",
    "start_line": 3000,
    "end_line": 3039,
    "source": "async def _gather_belief_context(\n    belief: dict,\n    nodes: dict,\n    repo_path: str,\n    project_dir: str | None = None,\n) -> tuple[str, str]:\n    \"\"\"Gather code context for a single belief. Returns (id, context_text).\"\"\"\n    from .observations import grep, read_file\n\n    bid = belief[\"id\"]\n    node = nodes.get(bid, {})\n    source = node.get(\"source\", \"\")\n\n    parts: list[str] = []\n\n    src_file = (node.get(\"metadata\") or {}).get(\"source_file\")\n    if not src_file:\n        src_file = _extract_source_file(source, project_dir)\n    if src_file:\n        result = await read_file(src_file, repo_path, max_lines=300)\n        if \"content\" in result:\n            content = result[\"content\"]\n            if len(content) > 4000:\n                content = content[:4000] + \"\\n... (truncated)\"\n            parts.append(f\"### {src_file}\\n```\\n{content}\\n```\")\n\n    terms = [t for t in bid.replace(\"-\", \" \").split() if len(t) > 3][:3]\n    grep_tasks = [grep(term, repo_path, glob=\"*\", max_results=5) for term in terms]\n    grep_results = await asyncio.gather(*grep_tasks, return_exceptions=True)\n    for term, result in zip(terms, grep_results):\n        if isinstance(result, Exception):\n            continue\n        if result.get(\"matches\"):\n            matches_text = \"\\n\".join(\n                f\"  {m['file']}:{m['line']}: {m['text']}\"\n                for m in result[\"matches\"][:5]\n            )\n            parts.append(f\"### grep '{term}'\\n{matches_text}\")\n\n    return bid, \"\\n\\n\".join(parts) if parts else \"(no code context found)\""
  },
  "extract_source_file_callers": {
    "symbol": "_extract_source_file",
    "production_callers": [
      {
        "file": "ftl_code_expert/cli.py",
        "line": 1651,
        "text": "src_file = _extract_source_file(source.strip())",
        "context_function": "_accept_proposals",
        "context_snippet": "   1648:             if result.returncode == 0:\n   1649:                 click.echo(f\"  Added: {belief_id}\")\n   1650:                 added += 1\n>> 1651:                 src_file = _extract_source_file(source.strip())\n   1652:                 if src_file:\n   1653:                     subprocess.run(\n   1654:                         [\"reasons\", \"set-metadata\", belief_id,"
      },
      {
        "file": "ftl_code_expert/cli.py",
        "line": 2985,
        "text": "def _extract_source_file(entry_path: str, project_dir: str | None = None) -> str | None:",
        "context_function": "_create_issue",
        "context_snippet": "   2982: {issues}\"\"\"\n   2983: \n   2984: \n>> 2985: def _extract_source_file(entry_path: str, project_dir: str | None = None) -> str | None:\n   2986:     \"\"\"Extract source file path from an entry's header line (# File: ...).\"\"\"\n   2987:     base = project_dir or \".\"\n   2988:     full_path = Path(base) / entry_path"
      },
      {
        "file": "ftl_code_expert/cli.py",
        "line": 3017,
        "text": "src_file = _extract_source_file(source, project_dir)",
        "context_function": "_gather_belief_context",
        "context_snippet": "   3014: \n   3015:     src_file = (node.get(\"metadata\") or {}).get(\"source_file\")\n   3016:     if not src_file:\n>> 3017:         src_file = _extract_source_file(source, project_dir)\n   3018:     if src_file:\n   3019:         result = await read_file(src_file, repo_path, max_lines=300)\n   3020:         if \"content\" in result:"
      }
    ],
    "test_callers": [],
    "production_count": 3,
    "test_count": 0,
    "total_count": 3
  },
  "accept_proposals_tests": {
    "source_file": "ftl_code_expert/cli.py",
    "test_files": [],
    "test_count": 0
  }
}
```

Use these results to inform your review. Do not request the same observations again.


## Instructions

For each significant change (new file, modified function, etc.), provide a structured verdict.

Use this exact format for each change:

### <file_path or file_path:function_name>
VERDICT: PASS | CONCERN | BLOCK
CORRECTNESS: VALID | QUESTIONABLE | BROKEN
SPEC_COMPLIANCE: MEETS | PARTIAL | VIOLATES | N/A
ISSUE_COMPLIANCE: ADDRESSES | PARTIAL | UNRELATED | N/A
BELIEF_COMPLIANCE: CONSISTENT | VIOLATES | N/A
TEST_COVERAGE: COVERED | PARTIAL | UNTESTED
INTEGRATION: WIRED | PARTIAL | MISSING
REASONING: <brief explanation of your assessment>
---

## Review Criteria

1. **CORRECTNESS**: Does the code do what it claims? Is the logic sound?
   - VALID: Logic is correct, no bugs apparent
   - QUESTIONABLE: Logic may have edge cases or unclear behavior
   - BROKEN: Clear bugs or incorrect behavior

2. **SPEC_COMPLIANCE**: Does it meet MUST requirements from the spec?
   - MEETS: All relevant spec requirements satisfied
   - PARTIAL: Some requirements met, others missing or incomplete
   - VIOLATES: Contradicts spec requirements
   - N/A: No spec provided or not applicable

3. **ISSUE_COMPLIANCE** (only when an issue is provided): Do the changes address the problem or feature described in the issue?
   - ADDRESSES: Changes directly solve the issue's stated problem or implement the requested feature
   - PARTIAL: Changes partially address the issue but leave some aspects unresolved
   - UNRELATED: Changes do not appear related to the issue
   - N/A: No issue provided

4. **TEST_COVERAGE**: Are there tests for the new/changed code?
   - COVERED: Tests exist and cover the changes
   - PARTIAL: Some tests exist but coverage is incomplete
   - UNTESTED: No tests for the changes

5. **INTEGRATION**: Are callers updated? Is the feature usable end-to-end?
   - WIRED: Feature is fully integrated and usable
   - PARTIAL: Interface exists but callers not updated, or integration incomplete
   - MISSING: No integration with existing code

6. **BELIEF_COMPLIANCE** (only when beliefs are provided): Do the changes respect known architectural invariants, contracts, and rules?
   - CONSISTENT: Changes align with or reinforce known beliefs
   - VIOLATES: Changes contradict a specific belief — cite the belief ID
   - N/A: No beliefs provided or no relevant beliefs apply

## Verdict Guidelines

- **BLOCK**: Security issues, broken functionality, spec violations, or missing critical integration
- **CONCERN**: Missing tests, partial integration, questionable patterns, or unclear logic
- **PASS**: Correct, tested, well-integrated code

## Important

- Full function bodies for modified functions may be available in the observations section — use them to verify the complete logic, not just the diff hunks
- Related test files (prefixed with ``related_test:``) may be included in observations — check whether existing test assertions still match modified return types, signatures, or behavior. Flag any test that would break due to the changes
- If duplicate test coverage is detected (multiple test files covering the same source), note it in your review
- Focus on actual issues, not style preferences
- If a method signature is added but callers aren't updated, that's PARTIAL integration
- Be specific in reasoning - reference line numbers or function names
- When in doubt, use CONCERN rather than PASS

## Self-Review

After completing your review, add a brief self-assessment:

### SELF_REVIEW
LIMITATIONS: <what context were you missing that affected review quality?>
---

Examples of limitations:
- "Could not see full class to verify no other methods access the modified field"
- "Test file not included in diff - cannot verify coverage claims"
- "Spec file referenced but not provided"


## Feature Requests

If this review tool could be improved to help you do a better job, suggest features:

### FEATURE_REQUESTS
- <suggestion 1>
- <suggestion 2>
---

Examples:
- "Include full file context for modified functions, not just diff hunks"
- "Show callers of modified methods to verify integration"
- "Include test file alongside implementation changes"

Only include this section if you have specific suggestions. Skip if none.
