fix(graph): normalize case_law subject_tags to underscore convention at write chokepoint #311

Merged
chaim merged 1 commits from worktree-subject-tag-normalize into main 2026-06-20 12:39:51 +00:00
Owner

רקע

צמתי-הנושא ב-/graph נבנים מ-case_law.subject_tags. נמצאו תגים שנראים ככפילות בגרף, שנבדלים רק בקיום/היעדר מקף-תחתון בין המילים (למשל היטל השבחה מול היטל_השבחה). חקירה העלתה:

  • מוסכמת המקף-תחתון מכוונת ומתועדת — דוגמאות בחוזה-הכלי (precedent_library: קווי_בניין, מועד_קביעת_שומה), ו-892 מתוך 1011 התגים הייחודיים (~99%) פועלים לפיה.
  • תיק אחד בלבד (8126-03-25, יעקב עמיאל) תויג ברווחים, וזה מקור 2 הכפילויות האמיתיות בקורפוס (היטל השבחההיטל_השבחה, הארכת מועדהארכת_מועד).
  • ה"כפילות" הנוספת בגרף — צומת-התחום pa:betterment_levy (תווית "היטל השבחה" עם רווח) מול צומת-הנושא tag:היטל_השבחהאינה באג: אלו שתי שכבות-גרף שונות (תחום מול נושא). לא נגענו בה.

הנתונים נורמלו בנפרד (תיק יעקב עמיאל → מקף-תחתון). PR זה אוכף את המוסכמה במקור כדי שאף נתיב-כתיבה לא יחזיר את הפיצול.

השינוי

_normalize_subject_tags() חדש ב-db.py, נקרא בשלוש (וכל) נקודות-הכתיבה של case_law.subject_tags:

  • create_external_case_law
  • create_internal_committee_decision
  • update_case_law

כך הכלל לא ניתן-לעקיפה מאף נתיב. הכלל שמרני: רק ביטוי עברי פשוט רב-מילים ([א-ת]+ מופרד ברווח) מומר למקף-תחתון; תגים עם פיסוק/מספרים/מקפים (פטור מותנה — סעיף 19(ג)) נשארים. בנוסף — dedup לאחר הנרמול.

digests.subject_tags (TEXT[], שכבת-גרף אחרת) ו-canonical_halachot — מחוץ להיקף בכוונה.

Invariants

  • G1 — נרמול במקור (נתיב-כתיבה), לא תיקון בקריאה/בהיטל-הגרף. נדחתה במפורש האפשרות לטלאי-תצוגה ב-graph_api (היה מסתיר את אי-העקביות במסך אחד בעוד החיפוש מציג מקף → drift).
  • G2graph_api נשאר היטל-קריאה טהור; אין נרמול מקביל שם.

בדיקות

  • py_compile עובר.
  • בדיקת-יחידה ללוגיקת הנרמול: המרת ביטוי-רווח, שמירת תגי-סעיף, strip, dedup-לאחר-נרמול, None — כולן עוברות.

🤖 Generated with Claude Code

## רקע צמתי-הנושא ב-`/graph` נבנים מ-`case_law.subject_tags`. נמצאו תגים שנראים ככפילות בגרף, שנבדלים רק בקיום/היעדר מקף-תחתון בין המילים (למשל `היטל השבחה` מול `היטל_השבחה`). חקירה העלתה: - **מוסכמת המקף-תחתון מכוונת ומתועדת** — דוגמאות בחוזה-הכלי (`precedent_library`: `קווי_בניין`, `מועד_קביעת_שומה`), ו-892 מתוך 1011 התגים הייחודיים (~99%) פועלים לפיה. - תיק **אחד** בלבד (8126-03-25, יעקב עמיאל) תויג ברווחים, וזה מקור 2 הכפילויות האמיתיות בקורפוס (`היטל השבחה`↔`היטל_השבחה`, `הארכת מועד`↔`הארכת_מועד`). - ה"כפילות" הנוספת בגרף — צומת-התחום `pa:betterment_levy` (תווית "היטל השבחה" עם רווח) מול צומת-הנושא `tag:היטל_השבחה` — **אינה באג**: אלו שתי שכבות-גרף שונות (תחום מול נושא). לא נגענו בה. הנתונים נורמלו בנפרד (תיק יעקב עמיאל → מקף-תחתון). PR זה אוכף את המוסכמה **במקור** כדי שאף נתיב-כתיבה לא יחזיר את הפיצול. ## השינוי `_normalize_subject_tags()` חדש ב-`db.py`, נקרא בשלוש (וכל) נקודות-הכתיבה של `case_law.subject_tags`: - `create_external_case_law` - `create_internal_committee_decision` - `update_case_law` כך הכלל **לא ניתן-לעקיפה** מאף נתיב. הכלל שמרני: רק ביטוי עברי פשוט רב-מילים (`[א-ת]+` מופרד ברווח) מומר למקף-תחתון; תגים עם פיסוק/מספרים/מקפים (`פטור מותנה — סעיף 19(ג)`) נשארים. בנוסף — dedup לאחר הנרמול. `digests.subject_tags` (TEXT[], שכבת-גרף אחרת) ו-`canonical_halachot` — מחוץ להיקף בכוונה. ## Invariants - **G1** — נרמול **במקור** (נתיב-כתיבה), לא תיקון בקריאה/בהיטל-הגרף. נדחתה במפורש האפשרות לטלאי-תצוגה ב-`graph_api` (היה מסתיר את אי-העקביות במסך אחד בעוד החיפוש מציג מקף → drift). - **G2** — `graph_api` נשאר היטל-קריאה טהור; אין נרמול מקביל שם. ## בדיקות - `py_compile` עובר. - בדיקת-יחידה ללוגיקת הנרמול: המרת ביטוי-רווח, שמירת תגי-סעיף, strip, dedup-לאחר-נרמול, `None` — כולן עוברות. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chaim added 1 commit 2026-06-20 12:39:44 +00:00
fix(graph): normalize case_law subject_tags to underscore convention at write chokepoint
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
Lint — undefined names / undefined-names (pull_request) Successful in 11s
a401197204
Topic hubs in /graph are built from case_law.subject_tags. The documented
extraction contract (precedent_library tool examples: קווי_בניין,
מועד_קביעת_שומה) and ~99% of the corpus store plain multi-word Hebrew tags
with underscores between words ("היטל_השבחה"). A single case (8126-03-25,
יעקב עמיאל) was tagged with spaces ("היטל השבחה"), which the graph renders as
a SECOND, distinct topic hub — a duplicate of the underscore form. The data
was normalized separately; this enforces the convention at the source so no
write path can re-introduce the split.

_normalize_subject_tags() is applied at the three (and only) case_law write
chokepoints in db.py — create_external_case_law, create_internal_committee_decision,
update_case_law — so the rule cannot be bypassed (G1: normalize at source, not
in the read/graph path). Tags carrying punctuation/digits/dashes
(e.g. "פטור מותנה — סעיף 19(ג)") are left untouched; only plain Hebrew word
phrases ([א-ת]+ separated by spaces) are converted. Also dedups post-normalize.

digests.subject_tags (TEXT[], a different graph layer) and canonical_halachot
are intentionally out of scope.

Invariants: maintains G1 (fix at source, not in the read projection),
G2 (graph_api stays a pure read projection — no parallel normalization there).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chaim merged commit eb0182ecf8 into main 2026-06-20 12:39:51 +00:00
chaim deleted branch worktree-subject-tag-normalize 2026-06-20 12:39:51 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#311