fix(citations): extract internal citations from discussion sections only (G1/G2)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s

extract_and_store read the WHOLE full_text, so a ruling a party cited in its
own arguments (block ז / appellant_claims / parties_claims) was recorded as the
deciding committee citing it — and surfaced as "צוטט ע״י דפנה". Example: ערר
130/10 appears only in the appellant_claims chunk of decision 1200-12-25, yet
was attributed to the chair.

Fix: build the extraction text from the discussion/ruling chunks via the
halacha extractor's _select_extractable_chunks (G2 — one definition of
"reasoning sections": legal_analysis/ruling/conclusion + discussion-anchor,
excluding facts/claims/intro). Falls back to full_text only for un-chunked rows.
A citation now reflects the deciding body's reliance, not a party's argument.

Existing data reconciled via a one-off (backup
data/audit/citation-edges-backup-*.csv): per source decision, dropped edges
whose cited number is absent from the discussion-only set — 82 of 398 removed
(incl. ערר 130/10 ↔ 1200-12-25); 316 genuine edges kept.

Invariants: G1 (correct at the source) · G2 (single reasoning-section selector,
shared with halacha extraction) · INV-LRN2 (quality-at-source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 13:29:14 +00:00
parent 25b41af6a3
commit f63bd4df0f

View File

@@ -236,7 +236,18 @@ async def extract_and_store(case_law_id: UUID) -> dict:
if not row:
return {"extracted": 0, "linked": 0, "new": 0, "skipped": 0, "error": "not_found"}
text = row["full_text"] or ""
# A citation counts as the DECIDING body's reliance ONLY when it appears in
# the discussion/ruling sections — never where a party cites a ruling in its
# own arguments (block ז). Reuse the halacha extractor's section selection
# (G2: a single definition of "reasoning sections" — legal_analysis/ruling/
# conclusion + the discussion-anchor, excluding facts/claims/intro) so a
# ruling cited only in appellant_claims/parties_claims is never attributed to
# the chair. Fall back to full_text only for un-chunked rows.
from legal_mcp.services import halacha_extractor as _hx
disc_chunks, _used_fallback = await _hx._select_extractable_chunks(case_law_id)
text = "\n".join((c.get("content") or "") for c in disc_chunks).strip()
if not text:
text = row["full_text"] or ""
own_norm = _normalize_case_number(row["case_number"] or "")
extracted = 0