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/expert_build/propose.py b/expert_build/propose.py
index 49a37b7..3b86042 100644
--- a/expert_build/propose.py
+++ b/expert_build/propose.py
@@ -68,15 +68,14 @@ def _load_processed(path: Path) -> dict[str, str]:
     return {}
 
 
-def _save_processed(path: Path, entries: list[Path], existing: dict[str, str]):
-    """Record entries as processed by content hash."""
-    updated = dict(existing)
-    for entry_path in entries:
+def _save_processed(path: Path, new_entries: list[Path], existing: dict[str, str]):
+    """Record new entries as processed by content hash and write to disk."""
+    for entry_path in new_entries:
         content = entry_path.read_text()
         content_hash = hashlib.sha256(content.encode()).hexdigest()[:16]
-        updated[str(entry_path)] = content_hash
+        existing[str(entry_path)] = content_hash
     path.parent.mkdir(parents=True, exist_ok=True)
-    path.write_text(json.dumps(updated, indent=2) + "\n")
+    path.write_text(json.dumps(existing, indent=2) + "\n")
 
 
 def _filter_unprocessed(entries: list[Path], processed: dict[str, str]) -> list[Path]:
@@ -378,8 +377,13 @@ def cmd_propose_beliefs(args):
             f.write(f"**Source:** {source_desc}\n")
             f.write(f"**Model:** {args.model}\n\n")
 
+    total_skipped = 0
+    successful_entries = []
+    write_lock = asyncio.Lock()
+
     async def _process_batch(i, batch_text, semaphore):
-        """Process one batch under concurrency limit. Returns (filtered, paths) or None."""
+        """Process one batch and write results immediately."""
+        nonlocal total_skipped
         async with semaphore:
             print(f"  Batch {i + 1}/{len(batches)}...")
             existing_context = _build_dedup_context(
@@ -391,7 +395,7 @@ async def _process_batch(i, batch_text, semaphore):
                 result = await invoke(prompt, model=args.model, timeout=600)
             except Exception as e:
                 print(f"  ERROR: {e}")
-                return None
+                return
 
             beliefs = extract_json(result)
             if not isinstance(beliefs, list):
@@ -406,7 +410,7 @@ async def _process_batch(i, batch_text, semaphore):
                     pass
             if not isinstance(beliefs, list):
                 print("    WARN: could not parse beliefs JSON, skipping batch", file=sys.stderr)
-                return None
+                return
 
             filtered = []
             skipped = 0
@@ -416,39 +420,33 @@ async def _process_batch(i, batch_text, semaphore):
                     skipped += 1
                     continue
                 filtered.append(b)
-            return filtered, batch_paths[i], skipped
+
+        async with write_lock:
+            total_skipped += skipped
+            with output.open("a") as f:
+                for b in filtered:
+                    bid = b.get("id", "unknown")
+                    claim = b.get("claim", "")
+                    source = b.get("source", "")
+                    source_url = b.get("source_url", "")
+                    verdict = "[ACCEPT]" if b.get("accept", True) else "[REJECT]"
+                    f.write(f"### {verdict} {bid}\n")
+                    f.write(f"{claim}\n")
+                    f.write(f"- Source: {source}\n")
+                    f.write(f"- Source URL: {source_url or 'none'}\n\n")
+
+            batch_entries = [Path(p) for p in batch_paths[i]]
+            successful_entries.extend(batch_entries)
+            _save_processed(processed_path, batch_entries, processed)
 
     parallel = max(1, getattr(args, "parallel", 1))
     semaphore = asyncio.Semaphore(parallel)
 
     async def run_batches():
         tasks = [_process_batch(i, bt, semaphore) for i, bt in enumerate(batches)]
-        return await asyncio.gather(*tasks)
-
-    batch_results = asyncio.run(run_batches())
+        await asyncio.gather(*tasks)
 
-    total_skipped = 0
-    successful_entries = []
-    for result in batch_results:
-        if result is None:
-            continue
-        filtered, paths, skipped = result
-        total_skipped += skipped
-
-        with output.open("a") as f:
-            for b in filtered:
-                bid = b.get("id", "unknown")
-                claim = b.get("claim", "")
-                source = b.get("source", "")
-                source_url = b.get("source_url", "")
-                verdict = "[ACCEPT]" if b.get("accept", True) else "[REJECT]"
-                f.write(f"### {verdict} {bid}\n")
-                f.write(f"{claim}\n")
-                f.write(f"- Source: {source}\n")
-                f.write(f"- Source URL: {source_url or 'none'}\n\n")
-
-        successful_entries.extend(Path(p) for p in paths)
-        _save_processed(processed_path, successful_entries, processed)
+    asyncio.run(run_batches())
 
     if total_skipped:
         print(f"  Filtered {total_skipped} already-accepted beliefs")

```

## Observation Results

You previously requested observations. Here are the results:

```json
{
  "full_process_batch": {
    "function": "_process_batch",
    "file": "expert_build/propose.py",
    "start_line": 384,
    "end_line": 440,
    "source": "    async def _process_batch(i, batch_text, semaphore):\n        \"\"\"Process one batch and write results immediately.\"\"\"\n        nonlocal total_skipped\n        async with semaphore:\n            print(f\"  Batch {i + 1}/{len(batches)}...\")\n            existing_context = _build_dedup_context(\n                existing_beliefs, batch_paths[i], batch_text,\n                belief_vectors=belief_vectors,\n            )\n            prompt = PROPOSE_BELIEFS.format(entries=batch_text) + existing_context\n            try:\n                result = await invoke(prompt, model=args.model, timeout=600)\n            except Exception as e:\n                print(f\"  ERROR: {e}\")\n                return\n\n            beliefs = extract_json(result)\n            if not isinstance(beliefs, list):\n                print(\"    WARN: response not valid JSON, retrying...\", file=sys.stderr)\n                try:\n                    retry_response = await invoke(\n                        prompt + \"\\n\\n\" + result + \"\\n\\n\" + RETRY_JSON,\n                        model=args.model, timeout=600,\n                    )\n                    beliefs = extract_json(retry_response)\n                except Exception:\n                    pass\n            if not isinstance(beliefs, list):\n                print(\"    WARN: could not parse beliefs JSON, skipping batch\", file=sys.stderr)\n                return\n\n            filtered = []\n            skipped = 0\n            for b in beliefs:\n                bid = b.get(\"id\", \"\")\n                if bid in existing_ids:\n                    skipped += 1\n                    continue\n                filtered.append(b)\n\n        async with write_lock:\n            total_skipped += skipped\n            with output.open(\"a\") as f:\n                for b in filtered:\n                    bid = b.get(\"id\", \"unknown\")\n                    claim = b.get(\"claim\", \"\")\n                    source = b.get(\"source\", \"\")\n                    source_url = b.get(\"source_url\", \"\")\n                    verdict = \"[ACCEPT]\" if b.get(\"accept\", True) else \"[REJECT]\"\n                    f.write(f\"### {verdict} {bid}\\n\")\n                    f.write(f\"{claim}\\n\")\n                    f.write(f\"- Source: {source}\\n\")\n                    f.write(f\"- Source URL: {source_url or 'none'}\\n\\n\")\n\n            batch_entries = [Path(p) for p in batch_paths[i]]\n            successful_entries.extend(batch_entries)\n            _save_processed(processed_path, batch_entries, processed)"
  },
  "full_cmd_propose_beliefs": {
    "function": "cmd_propose_beliefs",
    "file": "expert_build/propose.py",
    "start_line": 262,
    "end_line": 457,
    "source": "def cmd_propose_beliefs(args):\n    \"\"\"Extract candidate beliefs from entries for human review.\"\"\"\n    from .caffeinate import hold as _caffeinate\n    _caffeinate()\n    input_dir = Path(args.input_dir)\n    if not input_dir.exists():\n        print(f\"Entries directory not found: {input_dir}\")\n        sys.exit(1)\n\n    if not check_model_available(args.model):\n        print(f\"Model not available: {args.model}\")\n        sys.exit(1)\n\n    # Collect entries\n    if hasattr(args, 'entry') and args.entry:\n        entries = [Path(p) for p in args.entry]\n    else:\n        entries = sorted(input_dir.rglob(\"*.md\"))\n\n    if not entries:\n        print(f\"No .md files found.\")\n        return\n\n    # Filter out already-processed entries (unless --all or --entry)\n    processed_path = Path(PROJECT_DIR) / \"proposed-entries.json\"\n    processed = _load_processed(processed_path)\n    process_all = getattr(args, 'all', False)\n    has_entry_flag = hasattr(args, 'entry') and args.entry\n\n    if not process_all and not has_entry_flag:\n        total = len(entries)\n        entries = _filter_unprocessed(entries, processed)\n        skipped = total - len(entries)\n        if skipped:\n            print(f\"Skipping {skipped} already-processed entries (use --all to reprocess)\")\n        if not entries:\n            print(\"No new entries to process.\")\n            return\n\n    # Load existing beliefs for dedup context\n    existing_beliefs = _load_existing_beliefs()\n    existing_ids = {b[\"id\"] for b in existing_beliefs}\n\n    if existing_ids:\n        print(f\"Found {len(existing_ids)} existing beliefs (will skip duplicates)\")\n\n    # Compute belief embeddings once (if fastembed available)\n    belief_vectors = None\n    if existing_beliefs and _has_embeddings():\n        print(\"Computing belief embeddings for semantic dedup...\")\n        cache_path = Path(PROJECT_DIR) / \"belief-vectors.json\"\n        belief_vectors = _get_belief_embeddings(existing_beliefs, cache_path)\n        print(f\"  {len(belief_vectors)} belief vectors ready\")\n    elif existing_beliefs:\n        print(\"(install fastembed for semantic dedup: uv pip install 'expert-agent-builder[embeddings]')\")\n\n    print(f\"Reading {len(entries)} entries...\")\n\n    # Batch entries \u2014 track paths per batch for relevance scoring\n    batches = []\n    batch_paths = []\n    current_batch = []\n    current_paths = []\n    for entry_path in entries:\n        content = entry_path.read_text()\n        if len(content) > 10000:\n            content = content[:10000] + \"\\n[Truncated]\"\n        source_url = \"\"\n        if content.startswith(\"---\"):\n            end = content.find(\"---\", 3)\n            if end != -1:\n                for line in content[3:end].splitlines():\n                    if line.startswith(\"source_url:\"):\n                        source_url = line.split(\":\", 1)[1].strip()\n                    elif line.startswith(\"source:\"):\n                        val = line.split(\":\", 1)[1].strip()\n                        if not source_url and val.startswith((\"http://\", \"https://\")):\n                            source_url = val\n        header = f\"--- FILE: {entry_path}\"\n        if source_url:\n            header += f\" | SOURCE_URL: {source_url}\"\n        header += \" ---\"\n        current_batch.append(f\"{header}\\n{content}\")\n        current_paths.append(str(entry_path))\n        if len(current_batch) >= args.batch_size:\n            batches.append(\"\\n\\n\".join(current_batch))\n            batch_paths.append(current_paths)\n            current_batch = []\n            current_paths = []\n    if current_batch:\n        batches.append(\"\\n\\n\".join(current_batch))\n        batch_paths.append(current_paths)\n\n    print(f\"Processing {len(batches)} batches (batch size: {args.batch_size})...\")\n\n    source_desc = (\", \".join(str(e) for e in entries)\n                   if has_entry_flag\n                   else f\"{len(entries)} entries from {input_dir}/\")\n    output = Path(args.output)\n\n    # Write header before first batch if starting a new file\n    appended = output.exists() and output.stat().st_size > 0\n    if not appended:\n        with output.open(\"w\") as f:\n            f.write(\"# Proposed Beliefs\\n\\n\")\n            f.write(\"Review each entry: change `[REJECT]` to `[ACCEPT]` to keep, or vice versa.\\n\")\n            f.write(\"Then run: `expert-build accept-beliefs`\\n\\n\")\n            f.write(\"---\\n\\n\")\n            f.write(f\"**Generated:** {date.today().isoformat()}\\n\")\n            f.write(f\"**Source:** {source_desc}\\n\")\n            f.write(f\"**Model:** {args.model}\\n\\n\")\n    else:\n        with output.open(\"a\") as f:\n            f.write(f\"\\n---\\n\\n\")\n            f.write(f\"**Generated:** {date.today().isoformat()}\\n\")\n            f.write(f\"**Source:** {source_desc}\\n\")\n            f.write(f\"**Model:** {args.model}\\n\\n\")\n\n    total_skipped = 0\n    successful_entries = []\n    write_lock = asyncio.Lock()\n\n    async def _process_batch(i, batch_text, semaphore):\n        \"\"\"Process one batch and write results immediately.\"\"\"\n        nonlocal total_skipped\n        async with semaphore:\n            print(f\"  Batch {i + 1}/{len(batches)}...\")\n            existing_context = _build_dedup_context(\n                existing_beliefs, batch_paths[i], batch_text,\n                belief_vectors=belief_vectors,\n            )\n            prompt = PROPOSE_BELIEFS.format(entries=batch_text) + existing_context\n            try:\n                result = await invoke(prompt, model=args.model, timeout=600)\n            except Exception as e:\n                print(f\"  ERROR: {e}\")\n                return\n\n            beliefs = extract_json(result)\n            if not isinstance(beliefs, list):\n                print(\"    WARN: response not valid JSON, retrying...\", file=sys.stderr)\n                try:\n                    retry_response = await invoke(\n                        prompt + \"\\n\\n\" + result + \"\\n\\n\" + RETRY_JSON,\n                        model=args.model, timeout=600,\n                    )\n                    beliefs = extract_json(retry_response)\n                except Exception:\n                    pass\n            if not isinstance(beliefs, list):\n                print(\"    WARN: could not parse beliefs JSON, skipping batch\", file=sys.stderr)\n                return\n\n            filtered = []\n            skipped = 0\n            for b in beliefs:\n                bid = b.get(\"id\", \"\")\n                if bid in existing_ids:\n                    skipped += 1\n                    continue\n                filtered.append(b)\n\n        async with write_lock:\n            total_skipped += skipped\n            with output.open(\"a\") as f:\n                for b in filtered:\n                    bid = b.get(\"id\", \"unknown\")\n                    claim = b.get(\"claim\", \"\")\n                    source = b.get(\"source\", \"\")\n                    source_url = b.get(\"source_url\", \"\")\n                    verdict = \"[ACCEPT]\" if b.get(\"accept\", True) else \"[REJECT]\"\n                    f.write(f\"### {verdict} {bid}\\n\")\n                    f.write(f\"{claim}\\n\")\n                    f.write(f\"- Source: {source}\\n\")\n                    f.write(f\"- Source URL: {source_url or 'none'}\\n\\n\")\n\n            batch_entries = [Path(p) for p in batch_paths[i]]\n            successful_entries.extend(batch_entries)\n            _save_processed(processed_path, batch_entries, processed)\n\n    parallel = max(1, getattr(args, \"parallel\", 1))\n    semaphore = asyncio.Semaphore(parallel)\n\n    async def run_batches():\n        tasks = [_process_batch(i, bt, semaphore) for i, bt in enumerate(batches)]\n        await asyncio.gather(*tasks)\n\n    asyncio.run(run_batches())\n\n    if total_skipped:\n        print(f\"  Filtered {total_skipped} already-accepted beliefs\")\n\n    print(f\"\\n{'Appended to' if appended else 'Wrote'} {output}\")\n\n    print(\"Review the file, mark entries as [ACCEPT] or [REJECT], then run:\")\n    print(\"  expert-build accept-beliefs\")"
  },
  "save_processed_callers": {
    "symbol": "_save_processed",
    "production_callers": [
      {
        "file": "expert_build/propose.py",
        "line": 71,
        "text": "def _save_processed(path: Path, new_entries: list[Path], existing: dict[str, str]):",
        "context_function": "_load_processed",
        "context_snippet": "   68:     return {}\n   69: \n   70: \n>> 71: def _save_processed(path: Path, new_entries: list[Path], existing: dict[str, str]):\n   72:     \"\"\"Record new entries as processed by content hash and write to disk.\"\"\"\n   73:     for entry_path in new_entries:\n   74:         content = entry_path.read_text()"
      },
      {
        "file": "expert_build/propose.py",
        "line": 440,
        "text": "_save_processed(processed_path, batch_entries, processed)",
        "context_function": "_process_batch",
        "context_snippet": "   437: \n   438:             batch_entries = [Path(p) for p in batch_paths[i]]\n   439:             successful_entries.extend(batch_entries)\n>> 440:             _save_processed(processed_path, batch_entries, processed)\n   441: \n   442:     parallel = max(1, getattr(args, \"parallel\", 1))\n   443:     semaphore = asyncio.Semaphore(parallel)"
      }
    ],
    "test_callers": [],
    "production_count": 2,
    "test_count": 0,
    "total_count": 2
  },
  "full_save_processed": {
    "function": "_save_processed",
    "file": "expert_build/propose.py",
    "start_line": 71,
    "end_line": 78,
    "source": "def _save_processed(path: Path, new_entries: list[Path], existing: dict[str, str]):\n    \"\"\"Record new entries as processed by content hash and write to disk.\"\"\"\n    for entry_path in new_entries:\n        content = entry_path.read_text()\n        content_hash = hashlib.sha256(content.encode()).hexdigest()[:16]\n        existing[str(entry_path)] = content_hash\n    path.parent.mkdir(parents=True, exist_ok=True)\n    path.write_text(json.dumps(existing, indent=2) + \"\\n\")"
  },
  "full_filter_unprocessed": {
    "function": "_filter_unprocessed",
    "file": "expert_build/propose.py",
    "start_line": 81,
    "end_line": 93,
    "source": "def _filter_unprocessed(entries: list[Path], processed: dict[str, str]) -> list[Path]:\n    \"\"\"Return entries that are new or modified since last propose.\"\"\"\n    unprocessed = []\n    for entry_path in entries:\n        key = str(entry_path)\n        if key not in processed:\n            unprocessed.append(entry_path)\n            continue\n        content = entry_path.read_text()\n        content_hash = hashlib.sha256(content.encode()).hexdigest()[:16]\n        if content_hash != processed[key]:\n            unprocessed.append(entry_path)\n    return unprocessed"
  },
  "full_load_processed": {
    "function": "_load_processed",
    "file": "expert_build/propose.py",
    "start_line": 61,
    "end_line": 68,
    "source": "def _load_processed(path: Path) -> dict[str, str]:\n    \"\"\"Load processed entries tracking {path: content_hash}.\"\"\"\n    if path.exists():\n        try:\n            return json.loads(path.read_text())\n        except (json.JSONDecodeError, ValueError):\n            return {}\n    return {}"
  },
  "full_build_dedup_context": {
    "function": "_build_dedup_context",
    "file": "expert_build/propose.py",
    "start_line": 208,
    "end_line": 247,
    "source": "def _build_dedup_context(\n    existing_beliefs: list[dict],\n    batch_entry_paths: list[str],\n    batch_text: str,\n    max_detailed: int = 50,\n    max_compact: int = 200,\n    belief_vectors: dict[str, list[float]] | None = None,\n) -> str:\n    \"\"\"Build per-batch dedup context: relevant beliefs with text, rest as compact IDs.\"\"\"\n    if not existing_beliefs:\n        return \"\"\n\n    if belief_vectors:\n        scored = _score_by_embedding(\n            existing_beliefs, belief_vectors, batch_text, batch_entry_paths,\n        )\n    else:\n        scored = _score_by_keywords(\n            existing_beliefs, batch_text, batch_entry_paths,\n        )\n\n    detailed = scored[:max_detailed]\n    compact = scored[max_detailed:max_detailed + max_compact]\n\n    parts = [\n        \"\\n\\n## Already Accepted Beliefs\\n\\n\"\n        \"The following beliefs already exist. Do NOT propose beliefs with these IDs \"\n        \"or that duplicate their meaning under different names.\\n\"\n    ]\n\n    if detailed:\n        parts.append(\"\\nRelevant existing beliefs:\")\n        for _, belief in detailed:\n            parts.append(f\"- `{belief['id']}`: {belief['text']}\")\n\n    if compact:\n        compact_ids = \", \".join(b[\"id\"] for _, b in compact)\n        parts.append(f\"\\nOther existing IDs: {compact_ids}\")\n\n    return \"\\n\".join(parts) + \"\\n\""
  },
  "propose_tests": {
    "source_file": "expert_build/propose.py",
    "test_files": [
      {
        "path": "tests/test_pipelinepy",
        "exists": false
      },
      {
        "path": "tests/test_propose.py",
        "exists": true,
        "line_count": 359
      },
      {
        "path": "tests/test_proposepy",
        "exists": false
      }
    ],
    "test_count": 3
  },
  "extract_json_raises": {
    "function": "extract_json",
    "file": "expert_build/propose.py",
    "explicit_raises": [],
    "calls": [],
    "error": "Function 'extract_json' not found"
  },
  "invoke_raises": {
    "function": "invoke",
    "file": "expert_build/propose.py",
    "explicit_raises": [],
    "calls": [],
    "error": "Function 'invoke' not found"
  }
}
```

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.
