Pulse macro attribution allows multiple stories to claim the same grounding chunk URL via keyword fallback. Observed 2026-07-13 am prod: story[0] 'Oil Prices Spike as U.S.-Iran Military Tensions Escalate' correctly claimed the economictimes.indiatimes.com oil article via quote_anchor (method=quote_anchor). Story[1] 'Massive AI Data Center Buildout Emerges as Inflation Threat' then claimed the SAME economictimes oil article via keyword fallback (method=keyword), because no other chunk matched the AI data center topic. Result: two stories share one URL, and the AI story links to an irrelevant oil price article. Non-user-facing this time (pair selection chose story[2] as lead), but a latent correctness issue — if pair selection had chosen story[1], the paragraph would have cited an oil article for an AI infrastructure claim.
Enforce 1:1 URL→story assignment in _attribute_story: once a URL is claimed by a higher-confidence method (quote_anchor), remove it from the candidate pool for keyword fallback on remaining stories. A story with no valid URL after deduplication should trigger DDG fallback or be dropped from macro_events (parallel to the existing linkless-macro-drop logic in commit 266601a9). This prevents keyword's weak matching from latching onto already-claimed URLs.