feat(digests): case-contextual digest radar — surface unlinked digests as chair leads (X12)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
Lint — undefined names / undefined-names (pull_request) Successful in 14s

A digest pointing at a ruling we don't hold yet ("unlinked") was captured globally
(missing_precedents inbox) but never surfaced IN THE CONTEXT of the case being
decided — so a relevant ruling known only via a digest could fall through the cracks
at the moment it matters. Adds the case-contextual radar:

- db.search_digests_semantic: new `linked_only` filter (False = unlinked-only target set).
- digest_library.case_digest_radar(case_number): builds the case topic from title +
  appeal_subtype + the analyst's claims, embeds once, matches against UNLINKED digests,
  and returns leads enriched with the underlying ruling's gap status + suggested action
  (new_lead / gap_open / fetched / available_link).
- MCP tool `digest_radar` + endpoint GET /api/cases/{n}/digest-radar (for the agent and
  the future case-page lead).

INV-DIG1 preserved: radar only — every lead points at the underlying RULING (fetch /
upload / link), never cites the digest. Read-only.

Validated on 8124-09-24 (היטל השבחה): 5 on-topic unlinked digests, scores 0.64–0.72.
The visible case-page panel is a separate UI change → goes through the design gate.

Invariants: G2 (reuses the one digest semantic-search + gap/citation resolvers),
INV-DIG1 (no digest citation), INV-DIG3 (gap surfacing). No schema change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 16:45:14 +00:00
parent 4f45fa416b
commit 70f93c3bd4
5 changed files with 126 additions and 1 deletions

View File

@@ -170,3 +170,30 @@ async def digest_process_pending(limit: int = 20) -> str:
except Exception as e:
return _err(str(e))
return _ok(result)
async def digest_radar(case_number: str, limit: int = 5, min_score: float = 0.45) -> str:
"""רדאר-יומונים הקשרי-לתיק (X12) — "שים לב" ליו"ר.
מחזיר יומונים **לא-מקושרים** (פס"ד שעוד אין לנו בקורפוס) שהנושא שלהם קרוב
סמנטית לתיק הזה — כדי שפס"ד רלוונטי שמוכר רק דרך יומון לא ייפול בין הכיסאות
בזמן הכרעת התיק. נושא-התיק נבנה מ-title + appeal_subtype + טענות-הצדדים, ומותאם
מול יומונים לא-מקושרים. כל ליד נושא את מראה-המקום של הפס"ד המקורי, סטטוס-הפער
(new_lead / gap_open / fetched / available_link) ופעולה מוצעת.
INV-DIG1: זהו radar — היומון לעולם אינו מצוטט; הליד מצביע על **הפס"ד** (להזמין
משיכה / להעלות / לקשר), לא על היומון. read-only.
Args:
case_number: מספר התיק (למשל "8124-09-24").
limit: מספר לידים מקסימלי.
min_score: סף-דמיון סמנטי (0-1) לסינון רעש.
"""
if not case_number.strip():
return _err("case_number חובה")
try:
result = await digest_library.case_digest_radar(
case_number.strip(), limit=max(1, int(limit)), min_score=float(min_score))
except Exception as e:
return _err(str(e))
return _ok(result)