fix(discover): resolve cited_id via shared resolver (audit C-1)
discover.py compared the raw OpenAlex cited_id against papers.id (a DOI/arXiv id), so an ingested paper cited by its OpenAlex id was mis-reported as a discovery lead. graph.py already resolved this via papers.openalex_id, but the fix was never propagated — two 'dangling' implementations disagreed. Extract the resolution into one shared constant RESOLVED_CITATIONS_SQL in graph.py; build_citation_graph and all four discover.py queries (discovery_leads / citing_papers / cited_by / cocited_papers) now go through it, so the 'what counts as ingested' rule cannot drift again. Live-DB validated: before, 13 ingested papers leaked into discovery_leads; after, the real discovery_leads() returns 0 ingested papers among its leads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,23 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
_MIN_PAGERANK_NODES = 5
|
||||
|
||||
# Single source of truth for resolving ``citations.cited_id`` to a canonical
|
||||
# paper id. ``cited_id`` stores OpenAlex IDs while ``papers.id`` stores DOIs /
|
||||
# arXiv ids, so an in-KB reference cited by its OpenAlex id does not match
|
||||
# ``papers.id`` directly. This resolves it to the canonical ``papers.id`` (via
|
||||
# ``papers.openalex_id``, then the ``paper_identifiers`` alias table); dangling
|
||||
# references keep their raw id. Both :func:`build_citation_graph` and
|
||||
# :mod:`codex.discover` query through this so the "what counts as ingested" rule
|
||||
# cannot drift between them again (audit C-1). Trusted constant — no user input.
|
||||
RESOLVED_CITATIONS_SQL = """
|
||||
SELECT c.citing_id,
|
||||
COALESCE(p.id, pi.paper_id, c.cited_id) AS cited_id
|
||||
FROM citations c
|
||||
LEFT JOIN papers p ON p.openalex_id = c.cited_id
|
||||
LEFT JOIN paper_identifiers pi
|
||||
ON pi.openalex_id = c.cited_id AND p.id IS NULL
|
||||
"""
|
||||
|
||||
|
||||
def build_citation_graph(conn: Any) -> nx.DiGraph:
|
||||
"""Load the ``citations`` table into a directed graph.
|
||||
@@ -45,15 +62,7 @@ def build_citation_graph(conn: Any) -> nx.DiGraph:
|
||||
-------
|
||||
nx.DiGraph with every (citing_id, cited_id) pair as an edge.
|
||||
"""
|
||||
rows = conn.execute(
|
||||
"""
|
||||
SELECT c.citing_id, COALESCE(p.id, pi.paper_id, c.cited_id) AS cited_id
|
||||
FROM citations c
|
||||
LEFT JOIN papers p ON p.openalex_id = c.cited_id
|
||||
LEFT JOIN paper_identifiers pi
|
||||
ON pi.openalex_id = c.cited_id AND p.id IS NULL
|
||||
"""
|
||||
).fetchall()
|
||||
rows = conn.execute(RESOLVED_CITATIONS_SQL).fetchall()
|
||||
g: nx.DiGraph = nx.DiGraph()
|
||||
for row in rows:
|
||||
g.add_edge(row["citing_id"], row["cited_id"])
|
||||
|
||||
Reference in New Issue
Block a user