feat(precedents): איחוד cited_only↔missing_precedents — גזירת פסיקה-חסרה (#143, G2)
שתי מערכות מקבילות לאותו מושג ("פסיקה מצוטטת שטקסטה לא נקלט"): טבלת
missing_precedents (תור-רכישה ידני של היו"ר) מול case_law source_kind='cited_only'
(stubs מגרף-הציטוטים/X11). חפיפה≈0 → 31 ה-stubs לא הופיעו ב-/missing-precedents.
הכרעה (G2): missing_precedents = SoT-לתור-יחיד; cited_only = מקור-גילוי נגזר (כמו
יומונים מזינים radar). גוזרים רשומת missing_precedents 'open' לכל stub.
תיקון:
- court_citation.citation_dedup_key — מפתח-dedup **designator-aware**
(`{designator}|{docket}`). **מתקן פגם בתוכנית-הניתוח:** dedup על מספר-בלבד היה
ממזג בטעות אותו docket בערכאות שונות (בג"ץ 389/87 ≠ ע"א 389/87; 18 כאלה בקיים).
- סכמה V40: missing_precedents מקבל citation_norm (מפתח-dedup) + discovery_source
(manual|cited_only|digest|writer) + index. **בלי UNIQUE** — הקורפוס מחזיק
לגיטימית אותו docket בערכאות שונות; ייחודיות נאכפת designator-aware בנתיב-היצירה.
- create_missing_precedent: מחשב citation_norm בכתיבה (G1), מקבל discovery_source
+ linked_case_law_id. find_missing_precedent_by_citation: dedup דרך citation_norm
(fallback ל-citation גולמי כשאין מספר).
- scripts/derive_missing_from_cited_only.py: backfill citation_norm ל-291 +
גזירת 31 (dry-run: 31 ייווצרו, 0 deduped). linked_case_law_id=stub, status=open
→ promote-in-place בהעלאת-טקסט דרך ON CONFLICT הקיים. אידמפוטנטי.
תלוי-הקשר: #140 (הגדרת cited_only). מתואם עם #136 (digest→MP — אותו citation_norm
+ create path). תיקון-נתון יורץ אחרי הפריסה.
בדיקות: test_dedup_key_is_designator_aware (בג"ץ≠ע"א, ערר≠בל"מ, גרסאות-format
מתמזגות). כל 356 עוברות. guards נקיים.
Invariants: G2 (SoT-לתור יחיד, cited_only נגזר), G1 (citation_norm מנורמל בכתיבה),
G3 (idempotent upsert), G10 (שער-העלאה ידני נשמר), G12.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -134,6 +134,32 @@ def case_number_from_citation(citation: str) -> str:
|
||||
return classify(citation).case_number_norm
|
||||
|
||||
|
||||
def _norm_designator(prefix: str) -> str:
|
||||
"""Collapse a court/proceeding prefix to a comparison token.
|
||||
|
||||
Strips gershayim variants and whitespace so ``עע"מ`` / ``עע״מ`` / ``עעמ``
|
||||
all map to the same token, while DISTINCT courts stay distinct.
|
||||
"""
|
||||
return re.sub(r"[\s\"״׳']", "", prefix or "")
|
||||
|
||||
|
||||
def citation_dedup_key(citation: str) -> str:
|
||||
"""Designator-aware dedup key for a citation, or ``''`` if no number.
|
||||
|
||||
Returns ``f"{designator}|{case_number_norm}"`` — e.g.
|
||||
``בג"ץ 389/87`` → ``בגץ|389-87`` and ``ע"א 389/87`` → ``עא|389-87`` are
|
||||
DISTINCT keys, even though both share docket ``389-87``. This is the safe
|
||||
dedup key for ``missing_precedents`` (#143/#136): deduping on the bare number
|
||||
alone (``case_number_from_citation``) would WRONGLY MERGE the same docket
|
||||
across different courts (18 such collisions already exist in the corpus). A
|
||||
prefix-less citation (bare נט-format / unknown court) yields ``|<number>``.
|
||||
"""
|
||||
cit = classify(citation)
|
||||
if not cit.case_number_norm:
|
||||
return ""
|
||||
return f"{_norm_designator(cit.court_prefix)}|{cit.case_number_norm}"
|
||||
|
||||
|
||||
def _split_filed(num_norm: str) -> tuple[str, str, str] | None:
|
||||
"""Split a normalized NNNNN-MM-YY number into (file, month, year).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user