diff --git a/arborist/qa/evidence.py b/arborist/qa/evidence.py index 4b1b9d2..60a0041 100644 --- a/arborist/qa/evidence.py +++ b/arborist/qa/evidence.py @@ -437,19 +437,30 @@ def _spotlight_excerpt(claim_text: str, span: str, *, window: int) -> str: suffix = "..." if new_end < len(span) else "" return f"{span[new_start:new_end]}{suffix}" - # Density rank: for each candidate position, count distinct - # tokens whose match positions fall within ±half. Pick the - # position with max distinct count; tie-break on smallest idx - # (deterministic, reproducible). - best_score = -1 + # Density × rarity rank. For each candidate position, count + # distinct tokens whose match positions fall within ±half AND + # weight by 1/freq(anchor_token) in the span. A rare anchor + # (year, named entity, version number) appearing once in a + # Sonic-article-length chunk wins over a 20×-repeated "sonic" + # — the rare token marks the load-bearing slice for the claim. + # Without this, "Sonic the Hedgehog was created in 1990" cited + # to the franchise-timeline chunk spotlit a Sonic-Team paragraph + # instead of the "1990" year entry (2026-06-01 fox report). + # Tie-break on smallest idx (deterministic, reproducible). + freq: dict[str, int] = {} + for tok in tokens: + # Count overlapping occurrences using positions we already + # collected — same definition the density count uses. + freq[tok] = sum(1 for t, _ in positions if t == tok) or 1 + best_score = -1.0 match_idx = positions[0][1] - for _tok, idx in positions: + for tok, idx in positions: distinct: set[str] = set() lo, hi = idx - half, idx + half for tok2, idx2 in positions: if lo <= idx2 <= hi: distinct.add(tok2) - score = len(distinct) + score = len(distinct) / freq[tok] if score > best_score or (score == best_score and idx < match_idx): best_score = score match_idx = idx