diff --git a/contextbench/evaluate.py b/contextbench/evaluate.py
index 224c964..e6f3de6 100644
--- a/contextbench/evaluate.py
+++ b/contextbench/evaluate.py
@@ -167,10 +167,16 @@ def evaluate_instance(instance_id: str, gold, pred_data: dict, cache_dir: str) -
     final_files = set(final_step.files)
     final_spans = _step_spans(final_step, repo_dir)
     final_lines = _step_lines(final_step)
-    if getattr(final_step, "symbols", None):
-        final_symbols = extract_def_set_from_symbol_names(final_step.symbols, repo_dir)
-    else:
-        final_symbols = extract_def_set_in_spans(final_spans, repo_dir)
+    # FIR-1181/1183/1185: symbol credit is derived from BOUNDED predicted spans for
+    # BOTH arms, ALWAYS — never branch on pred_symbols. A declared file with no bounded
+    # span gets file-level credit only (extract_def_set_in_spans credits nothing for it,
+    # and there is no whole-file fallback). pred_symbols (names) is kept ONLY as a
+    # separate diagnostic below, never the headline structural metric.
+    final_symbols = extract_def_set_in_spans(final_spans, repo_dir)
+    final_symbols_named_diag = (
+        extract_def_set_from_symbol_names(final_step.symbols, repo_dir)
+        if getattr(final_step, "symbols", None) else set()
+    )
     
     # Detailed per-instance breakdown (stderr)
     symbol_detail_max = int(os.environ.get("SYMBOL_DETAIL_MAX", "50"))
@@ -222,6 +228,12 @@ def evaluate_instance(instance_id: str, gold, pred_data: dict, cache_dir: str) -
         )
     }
     
+    # Diagnostic ONLY (never the headline structural metric): the name-based symbol
+    # resolution, for visibility into how much the bounded-span rule narrows vs the old
+    # asymmetric name path. The scored `symbol` metric above is span-derived for both arms.
+    results["final"]["symbol_named_diag"] = len(final_symbols_named_diag)
+    results["final"]["symbol_span_credited"] = len(final_symbols)
+
     # Compute trajectory metrics
     results["trajectory"] = compute_trajectory_metrics(
         traj_steps, gold_files, gold_symbols, gold_spans, repo_dir,
@@ -232,9 +244,12 @@ def evaluate_instance(instance_id: str, gold, pred_data: dict, cache_dir: str) -
     # init_ctx contains context, so we check if pred edits fall within init_ctx ranges
     # For EditLoc, we only care about deleted lines (-), not added lines (+)
     model_patch = pred_data.get("model_patch", "") or ""
-    if not model_patch:
-        # Fallback: use gold patch if present (parquet provides it as `patch`)
-        model_patch = getattr(gold, "_data", {}).get("patch", "") or ""
+    # EditLoc is computed ONLY from a real predicted patch. The previous gold-patch
+    # fallback scored the gold patch against the gold ranges (gold-vs-gold) whenever
+    # the prediction carried no patch -- which is ALWAYS for context-retrieval arms
+    # (they declare context, they do not emit edits). With no model_patch we emit no
+    # editloc metric, so a meaningless gold-vs-gold number can never be produced or
+    # cited.
     if model_patch:
         from .parsers.diff import parse_diff_lines
         
@@ -266,19 +281,20 @@ def evaluate_instance(instance_id: str, gold, pred_data: dict, cache_dir: str) -
         
         pred_line_count = len(pred_lines)
         hit_count = len(pred_lines_in_gold)
-        
-        # Recall and Precision: both measure how many pred edits are within gold ranges
-        # Since init_ctx is context, we want to know: are the edits in the right area?
-        recall = hit_count / pred_line_count if pred_line_count > 0 else 0.0
-        precision = hit_count / pred_line_count if pred_line_count > 0 else 0.0
-        
-        # Gold size: total lines in init_ctx ranges (for reference)
+
+        # Gold size: total lines in the gold edit ranges.
         gold_line_count = sum(
             end - start + 1
             for intervals in gold_line_spans.values()
             for start, end in intervals
         )
-        
+
+        # Precision = predicted edit lines that land in a gold range / predicted lines.
+        # Recall    = gold edit lines covered / gold size. The prior recall==precision
+        # form ignored gold_size and was not a recall at all.
+        precision = hit_count / pred_line_count if pred_line_count > 0 else 0.0
+        recall = hit_count / gold_line_count if gold_line_count > 0 else 0.0
+
         results["editloc"] = {
             "recall": recall,
             "precision": precision,
