diff --git a/.taskmaster/tasks/tasks.json b/.taskmaster/tasks/tasks.json index 1d60df4..74680ac 100644 --- a/.taskmaster/tasks/tasks.json +++ b/.taskmaster/tasks/tasks.json @@ -2546,13 +2546,381 @@ "priority": "low", "subtasks": [], "updatedAt": "2026-06-03T00:00:00.000Z" + }, + { + "id": "81", + "title": "איכות חילוץ הלכות — לוודא שמה שמחולץ הוא הלכה אמיתית ולא ציטוט/אמרת-אגב", + "description": "מנוע חילוץ ההלכות מפיק כיום פריטים שאינם 'הלכות' במובן המהותי: אמרות-אגב שהערכאה לא הכריעה בהן, יישומים ספציפיים-לתיק (rule_type=application), ציטוטים חתוכים, ופירוק-יתר (עד 351 'הלכות' מפסק אחד). המשימה: לחדד את ה-prompt ולהוסיף ולידטורים אוטומטיים כך שרק עיקרון משפטי בר-הכללה ובר-הסתמכות ייכנס למאגר. מבוססת מחקר מקצועי (ratio decidendi מול obiter dictum, holding-extraction בספרות legal-NLP, קריטריונים לאיכות rule_statement). תתי-המשימות יוגדרו לאחר המחקר.", + "details": "רקע מבצעי (ניקוי 2026-06-03): נמחקו 196 רשומות מתוך 1650 (165 כפילויות תוכן + 31 Tier B/C). גיבוי: data/audit/halacha-cleanup-backup-20260603T101747Z.sql ; מניפסט: data/audit/halacha-cleanup-manifest-20260603T101747Z.csv . קוד רלוונטי: mcp-server/src/legal_mcp/services/halacha_extractor.py (prompts BINDING/PERSUASIVE, _coerce_halacha, אימות ציטוט). תחומי בדיקה ידועים: (א) חסימת quote_verified=false מהתור; (ב) הוצאת rule_type=application מהגדרת 'הלכה'; (ג) שמירה על דחיית dicta שלא הוכרעו; (ד) תקרת כמות/גרנולריות לפסק; (ה) הגנה מפני ציטוט חתוך (truncation guard). מפרט מאומת: docs/halacha-strict-rubric.md (הרובריקה האגרסיבית שהנחיתה ניקוי קורפוס 1454→534 ב-2026-06-03, שפר 51→22). להטמיע אותה במחלץ + dedup-on-insert (#82).", + "testStrategy": "Gold-set ידני של ~30 פריטים מתויגים הלכה/לא-הלכה ע\"י דפנה/חיים; מדידת precision/recall של המחלץ המעודכן מולו. בדיקת רגרסיה: הרצת המחלץ החדש על 3-5 פסקים שכבר נוקו והשוואת הפריטים.", + "status": "in-progress", + "dependencies": [], + "priority": "high", + "subtasks": [ + { + "id": 1, + "title": "מבחן ההיפוך (Wambaugh) + גלאי אי-הכרעה בפרומפט", + "description": "הטמעת מבחן ההיפוך של Wambaugh ולקסיקון ביטויי אי-הכרעה עבריים בשני וריאנטי הפרומפט; ניסוח עיקרון, שלילתו, ובדיקה אם תוצאת הוועדה הייתה משתנה — אם לא → obiter.", + "details": "מקור: Wambaugh inversion test; Goodhart material-facts. לקסיקון: 'אין צורך להכריע','מבלי לקבוע מסמרות','ניתן להניח','לכאורה','למעלה מן הצורך','אגב אורחא' → מאלצים rule_type=obiter ו-confidence<0.80. קוד: halacha_extractor.py prompts BINDING/PERSUASIVE.", + "status": "done", + "dependencies": [], + "testStrategy": "על מדגם 30 רשומות obiter שזוהו ידנית באודיט, ≥90% מסווגות obiter ולא מאושרות אוטומטית.", + "parentId": "81", + "updatedAt": "2026-06-03T12:32:01.304Z" + }, + { + "id": 2, + "title": "ולידטור שלמות ציטוט (truncation guard, V1)", + "description": "בדיקה ש-supporting_quote מתחיל בגבול משפט ומסתיים בפיסוק סופי (./?/:/מרכאה סוגרת) ואינו קטוע באמצע משפט; דגלון לציטוט קטוע.", + "details": "מקור: span-completeness / grounding-score. כיום הבדיקה מאמתת נוכחות verbatim אך לא שלמות. דוגמת אודיט: ציטוט '...ראוי כי תהיה השפעה על ה' (חתוך).", + "status": "done", + "dependencies": [], + "testStrategy": "על 50 ציטוטים קטועים ידועים ≥95% מסומנים; false-positive <5% על ציטוטים תקינים.", + "parentId": "81", + "updatedAt": "2026-06-03T12:32:01.318Z" + }, + { + "id": 3, + "title": "ולידטור entailment (NLI) בין rule ל-quote (V2)", + "description": "מודל NLI מאמת ש-rule_statement נובע (entailed) מ-supporting_quote; neutral/contradiction → מתחת לסף אישור אוטומטי.", + "details": "מקור: NLI-faithfulness (DeBERTa-NLI / atomic-fact verification). בודק שהכלל אכן נתמך בציטוט ולא הוזה/הורחב מעבר למקור.", + "status": "pending", + "dependencies": [], + "testStrategy": "על gold-set מתויג, NLI-reject מתאם עם פסילה אנושית ב-AUC ≥0.80.", + "parentId": "81" + }, + { + "id": 4, + "title": "ולידטור הפשטה + סיווג application (V3)", + "description": "פסילת restatement דק (token-overlap גבוה מול הציטוט) וסיווג כללים תלויי-עובדות/סכומים/צדדים-ספציפיים כ-application (מוחרג מאישור binding).", + "details": "מקור: rule-synthesis (Mangan) — broad holding מפשיט עובדות ספציפיות. כיום application סותם את התור (16/55 pending נמחקו באודיט).", + "status": "in-progress", + "dependencies": [], + "testStrategy": "על מדגם 40 פריטי application מהאודיט, ≥85% מזוהים ולא מאושרים כ-binding/interpretive.", + "parentId": "81", + "updatedAt": "2026-06-03T12:32:19.701Z" + }, + { + "id": 5, + "title": "בקרת over-extraction: clustering + תקרה (V5)", + "description": "embedding+clustering של ה-rule_statements בתוך פסק-דין, נציג אחד לאשכול, ותקרה לכמות לפי מספר מקטעי reasoning/decision (לא לפי מספר משפטים).", + "details": "מקור: SemDeDup + claim-clustering (MultiClaimNet). דוגמה: פסק 403-17 ייצר 351 'הלכות' — over-decomposition.", + "status": "pending", + "dependencies": [], + "testStrategy": "פסק עם 351 פריטים יורד ל-≤ מספר מקטעי ההנמקה; ירידה כוללת ≥40% בכפילויות.", + "parentId": "81" + }, + { + "id": 6, + "title": "סינון לפי תפקיד רטורי (rhetorical-role pre-filter)", + "description": "רק מקטעי Reasoning/Decision מועמדים לחילוץ; Facts/Arguments מוחרגים מראש.", + "details": "מקור: LegalSeg / rhetorical-role labeling (Bhattacharya). בלבול Facts↔Reasoning הוא מחלקת השגיאה הדומיננטית — סינון מקדים מעלה precision.", + "status": "pending", + "dependencies": [], + "testStrategy": "precision של מועמדים שעוברים סינון עולה ≥10 נק' מול baseline על gold-set.", + "parentId": "81" + }, + { + "id": 7, + "title": "בניית gold-set + מבחן אנונימיזציית ציטוטים", + "description": "תיוג ידני של ~150 רשומות (binding/interpretive/obiter/application + שלמות-ציטוט) ע\"י חיים/דפנה, ומבחן אנונימיזציה (שמות-תיק בדויים) לזיהוי שינון מול הנמקה אמיתית.", + "details": "מקור: CaseHOLD/RegLab (macro-F1≈0.72-0.74 לזיהוי holdings — לא 'פתור') + מבחן anonymization (arXiv:2505.02172). בסיס למדידת כל הוולידטורים.", + "status": "pending", + "dependencies": [], + "testStrategy": "gold-set מתויג עם κ≥0.6 בין שני מתייגים; pipeline מודד P/R/F1 מולו.", + "parentId": "81" + }, + { + "id": 8, + "title": "כיול מחדש של סף האישור האוטומטי", + "description": "אחרי V1-V5, כיול סף ה-0.80; שקילת החלפת confidence עצמי בציון משולב (confidence × validators).", + "details": "מקור: CaseHOLD מראה ש-self-confidence 0.80 אופטימי מדי בלי אימות חיצוני. תלוי ב-gold-set (81.7).", + "status": "pending", + "dependencies": [ + 7 + ], + "testStrategy": "precision של פריטים מאושרים-אוטומטית ≥0.90 על gold-set, עם recall מתועד ל-trade-off.", + "parentId": "81" + } + ], + "updatedAt": "2026-06-03T12:32:19.701Z" + }, + { + "id": "82", + "title": "Dedup בזמן הכנסה — מניעת הלכות כמעט-זהות בכתיבה (semantic dedup on insert)", + "description": "store_halachot_for_chunk מבצע INSERT עיוור ללא בדיקת כפילות, ולכן נצברו עשרות הלכות 'אותו עיקרון במילים אחרות'. המשימה: לפני שמירה, לבדוק דמיון סמנטי (embedding) מול הלכות קיימות באותו פסק ולדלג/למזג near-duplicates, וכן לזהות ציטוט-תומך זהה. מבוססת מחקר (ספי near-duplicate ב-embedding-IR, MinHash/LSH מול cosine, בחירת צורה קנונית, merge מול skip). תתי-המשימות יוגדרו לאחר המחקר.", + "details": "ממצא מהניקוי: סף cosine ≥0.90 תוך-פסק זיהה כפילויות אמיתיות בוודאות גבוהה (הרצועה 0.90-0.95 הייתה כמעט כולה 'אותו עיקרון בניסוח שונה'); ≥0.95 = ודאי. ציטוט-תומך זהה = איתות ודאי. להחליט: סף מבצעי, התנהגות (skip/merge/flag-for-review), ובחירת השורד (approved>pending, ביטחון גבוה, quote_verified). קוד: db.store_halachot_for_chunk; קיים idx_halachot_vec (pgvector).", + "testStrategy": "הרצה חוזרת של חילוץ על פסק שכבר חולץ — אפס כפילויות חדשות. בדיקת יחידה: הזנת שתי הלכות מנוסחות-שונה של אותו עיקרון → רק אחת נשמרת. בדיקה שלילית: שתי הלכות שונות ≥0.85 אך מובחנות → שתיהן נשמרות.", + "status": "in-progress", + "dependencies": [], + "priority": "high", + "subtasks": [ + { + "id": 1, + "title": "כיול ספים מתוך ה-audit (calibration harness)", + "description": "בניית set מתויג מזוגות האודיט שכבר נבדקו ידנית; sweep של cosine × (Jaccard/Levenshtein) ודיווח precision/recall לבחירת נקודת עבודה.", + "details": "מקור: SemDeDup tunes ε ע\"י דגימת ~10% ואינטרפולציה ליעד — לכייל, לא לנחש. הנתונים קיימים במניפסט הניקוי data/audit/halacha-cleanup-manifest-*.csv.", + "status": "pending", + "dependencies": [], + "testStrategy": "הסקריפט מפיק (cosine_hi, band_lo, lexical_lo, jaccard_τ, levenshtein_δ) עם ≥0.95 precision נגד מיזוג-שווא של כללים מובחנים.", + "parentId": "82" + }, + { + "id": 2, + "title": "שכבת בדיקה אקזקטית בתוך precedent (exact-cosine probe)", + "description": "בהכנסה: שאילתת halachot קיימות מסוננת ב-precedent_id עם cosine מדויק (לא ANN) + התאמת supporting_quote מנורמל.", + "details": "מקור: pgvector ANN (HNSW/IVFFlat) מאבד recall → false-negative בדדופ. מועמדים בתוך פסק בודד = set קטן → exact scan בטוח. שמירה על HNSW לנתיב הקריאה.", + "status": "done", + "dependencies": [], + "testStrategy": "בדיקת יחידה: dup מנוסח-שונה (cosine 0.92) + dup ציטוט-verbatim שניהם מזוהים; כלל מובחן (0.7) לא; EXPLAIN מראה filtered scan ולא ANN.", + "parentId": "82", + "updatedAt": "2026-06-03T12:32:01.334Z" + }, + { + "id": 3, + "title": "זנב מתחת ל-0.90 — אות לקסיקלי משני", + "description": "הוספת Jaccard-on-shingles + Levenshtein מנורמל על rule_statement; שער לרצועה 0.83-0.90 (cosine נמוך אך חפיפה לקסיקלית גבוהה → flag).", + "details": "מקור: hybrid lexical+semantic עדיף על כל אחד לבד (arXiv:1805.11611, WED arXiv:1810.10752). תופס את הזנב שדמיון-וקטורי לבד מפספס בלי להוריד סף גלובלי.", + "status": "pending", + "dependencies": [], + "testStrategy": "זוג מנוסח-שונה ב-cosine~0.87 עם חפיפה גבוהה → flag; זוג בחפיפה נמוכה ב-0.87 → נשמר כחדש.", + "parentId": "82" + }, + { + "id": 4, + "title": "התנהגות בהתאמה: skip/merge/flag + מיזוג provenance", + "description": "מימוש החלטה מדורגת (ציטוט-זהה/≥0.95→skip+merge; 0.90-0.95→merge; 0.83-0.90+לקסיקלי→flag). במיזוג: איחוד citations/corroboration/source-refs; בחירת קנוני לפי provenance עשיר ביותר, שובר-שוויון ציטוט verbatim ארוך.", + "details": "מקור: entity-resolution — לכללים יקרי-ערך flag+merge-with-provenance, לא drop עיוור (ThingSolver/ScrapingAnt). בחירת נציג לפי כלל-תוכן (SemDeDup: גאומטריה שולית).", + "status": "in-progress", + "dependencies": [], + "testStrategy": "מיזוג שתי שורות → שורה אחת עם איחוד קישורי citation/corroboration; שורות flag נוחתות בטבלת תור-ביקורת, לא נמחקות ולא ממוזגות אוטומטית.", + "parentId": "82", + "updatedAt": "2026-06-03T12:32:19.710Z" + }, + { + "id": 5, + "title": "אידמפוטנטיות ומפתח ייחוד", + "description": "אילוץ UNIQUE על (precedent_id, normalized supporting_quote) + נתיב ON CONFLICT כך שהרצת חילוץ חוזרת אינרטית.", + "details": "מקור: 'dedup=suppression, idempotency=identity' — שער fuzzy חייב מפתח-זהות מדויק לצדו. מתואם עם #83.", + "status": "in-progress", + "dependencies": [], + "testStrategy": "הרצת store_halachot_for_chunk פעמיים על אותו chunk → אפס שורות נטו חדשות.", + "parentId": "82", + "updatedAt": "2026-06-03T12:32:19.721Z" + }, + { + "id": 6, + "title": "הגנה מפני over-merge טרנזיטיבי", + "description": "מיזוג בהכנסה רק pairwise מול שורות קנוניות קיימות; ללא connected-components closure בהכנסה — קריסות רב-שורתיות נדחות לתור-ביקורת.", + "details": "מקור: over-merge דרך CC הוא הסיכון המרכזי (A~B~C קורס גם כש-A,C מובחנים) — בכלל משפטי = אובדן תקדים.", + "status": "pending", + "dependencies": [], + "testStrategy": "שרשרת סינתטית A~B~C כש-A,C מתחת לסף זה-לזה אינה קורסת לשורה אחת בהכנסה; לכל היותר מיזוג pairwise + flag.", + "parentId": "82" + }, + { + "id": 7, + "title": "(אופציונלי) batch reconciliation חוצה-פסקים", + "description": "job offline שמרני (סף ≥0.95) לדדופ חוצה-פסקים, נפרד מנתיב ההכנסה, כותב לאותו תור-ביקורת.", + "details": "מקור: cross-precedent שמרני יותר מ-within-precedent (האודיט הראה ש-≥0.90 אמין רק תוך-פסק). תחת scripts/ + עדכון SCRIPTS.md.", + "status": "pending", + "dependencies": [ + 1 + ], + "testStrategy": "dry-run מפיק דוח מועמדים חוצי-פסקים עם cosine+lexical; כלום לא מוחל בלי flag.", + "parentId": "82" + } + ], + "updatedAt": "2026-06-03T12:32:19.721Z" + }, + { + "id": "83", + "title": "חוסן pipeline החילוץ — re-run אידמפוטנטי + אינדוקס תקין (תיקון באגים)", + "description": "התגלו שני באגים: (1) halacha_index מוקצה per-chunk ולכן אינו ייחודי לפסק — שני עקרונות שונים מקבלים אותו מספר (לא כפילות, אך שובר dedup/מיון מבוסס-אינדקס); (2) חילוץ רץ פי-2/3 על אותו פסק (למשל 85026-17 שלוש ריצות תוך דקתיים) ומוסיף append במקום להחליף — ה-advisory lock לא מנע. המשימה: אינדוקס ייחודי לפסק, force=True שמוחק לפני re-extract, וחיזוק ה-lock/אידמפוטנטיות. מחקר קצר: דפוסי idempotency/exactly-once ב-pipelines.", + "details": "קוד: halacha_extractor.py (global advisory lock, per-chunk checkpoints ב-precedent_chunks.halacha_extracted_at, force flag), db.store_halachot_for_chunk (הקצאת halacha_index). לשקול unique constraint (case_law_id, halacha_index) אחרי תיקון ההקצאה.", + "testStrategy": "הרצת חילוץ פעמיים ברצף על אותו פסק → ספירה זהה, אפס אינדקסים כפולים. הרצת force=True → המאגר מוחלף ולא מצטבר. בדיקת מרוץ: שתי הרצות במקביל → רק אחת מבצעת (lock).", + "status": "pending", + "dependencies": [], + "priority": "medium", + "subtasks": [ + { + "id": 1, + "title": "נעילה גלובלית אמינה (advisory lock על חיבור ייעודי)", + "description": "העברת ה-pg_advisory_lock מחיבור pooled לחיבור ייעודי שאינו ממוחזר לאורך כל ה-job (או pg_advisory_xact_lock בדפוס מתאים), עם finally שמשחרר תמיד; תיעוד איסור transaction-pooler לפני החיבור.", + "details": "מקור: PG docs — session advisory lock לא בטוח תחת transaction-pooling (PgBouncer ממליץ נגדו). השורש האמיתי של 3 ריצות תוך 2 דקות: הנעילה לא החזיקה. קוד: halacha_extractor.py:372-394.", + "status": "pending", + "dependencies": [], + "testStrategy": "שתי הרצות extract במקביל על אותו precedent — השנייה מחזירה busy ולא רצה (נבדק עם 2 תהליכים נפרדים).", + "parentId": "83" + }, + { + "id": 2, + "title": "אילוץ UNIQUE (case_law_id, halacha_index)", + "description": "migration ל-halachot עם UNIQUE (case_law_id, halacha_index) כרשת ביטחון נגד התנגשויות מספור.", + "details": "מקור: FireHydrant/OneUptime — per-scope ordinal דורש UNIQUE(scope,number) כערובת התקינות; הנעילה היא אופטימיזציה. כיום ההקצאה MAX+1 מוגנת רק ב-asyncio.Lock תוך-תהליכי. db.py:645-669.", + "status": "pending", + "dependencies": [], + "testStrategy": "INSERT ידני של index כפול לאותו precedent נכשל ב-DB; query GROUP BY case_law_id,halacha_index HAVING count>1 מחזיר 0 בקורפוס.", + "parentId": "83" + }, + { + "id": 3, + "title": "דה-דופ לפי תוכן (content_hash + ON CONFLICT DO NOTHING)", + "description": "עמודת content_hash (md5 של rule_statement+supporting_quote) + UNIQUE(case_law_id, content_hash); שינוי ה-INSERT ל-ON CONFLICT DO NOTHING.", + "details": "מקור: upsert/replace הם פרימיטיב האידמפוטנטיות. משלים את שער ה-fuzzy של #82 במפתח-זהות מדויק. db.py:3325-3374.", + "status": "pending", + "dependencies": [], + "testStrategy": "הרצת extract(force=False) פעמיים ברצף על precedent שהושלם → מספר ה-halachot לא גדל.", + "parentId": "83" + }, + { + "id": 4, + "title": "מספור עמיד למרוץ ב-store_halachot_for_chunk", + "description": "retry-on-unique-violation סביב read-MAX→insert (או חישוב index מ-sequence עם RETURNING) — ללא הסתמכות על asyncio.Lock בלבד לבטיחות חוצת-תהליכים.", + "details": "מקור: race condition handling ב-PG. כיום MAX+1 מוגן רק תוך-תהליכית. db.py:3341-3344.", + "status": "pending", + "dependencies": [ + 2 + ], + "testStrategy": "בדיקה שמדמה שני קוראי-MAX מקבילים — שניהם נשמרים עם indexים שונים רצופים, אפס DuplicateKey לא-מטופל.", + "parentId": "83" + }, + { + "id": 5, + "title": "semantics של force = replace אטומי + עמידות לקריסה", + "description": "ודאות ש-force=True מבצע delete+checkpoint-clear בטרנזקציה אחת (קיים ב-reset_halacha_extraction), וש-resume אחרי קריסה אמצע re-extract לא מכפיל (הודות לאילוצי 83.2/83.3).", + "details": "מקור: delete-before-insert atomic / replace-partition. per-chunk commits נשמרים ל-resumability. db.py:3299-3304.", + "status": "pending", + "dependencies": [ + 2, + 3 + ], + "testStrategy": "הזרקת קריסה אחרי חלק מהצ'אנקים → resume משלים ללא כפילויות; count(halachot) תואם הרצה נקייה.", + "parentId": "83" + }, + { + "id": 6, + "title": "ניקוי נתונים היסטוריים לפני החלת אילוצים", + "description": "סקריפט חד-פעמי (scripts/ + SCRIPTS.md) שמזהה precedents עם indexים מתנגשים, ממספר מחדש רציף, ומכין את הקורפוס להחלת UNIQUE של 83.2/83.3.", + "details": "הניקוי של 2026-06-03 טיפל בכפילויות תוכן אך לא במספור; אילוצי UNIQUE ייכשלו אם יש index כפול שריר. גיבוי קיים: data/audit/halacha-cleanup-backup-*.sql.", + "status": "pending", + "dependencies": [ + 2 + ], + "testStrategy": "אחרי הרצה, אילוצי 83.2/83.3 נוצרים ללא שגיאה; דוח CSV ב-data/audit/ מפרט כמה תוקנו לכל precedent.", + "parentId": "83" + } + ], + "updatedAt": "2026-06-03T00:00:00.000Z" + }, + { + "id": "84", + "title": "טריאז' תור אישור ההלכות — אישור יעיל ולא מתיש", + "description": "אישור ההלכות ידני ומתיש: קריאת עקרונות כמעט-זהים שוב ושוב, ללא תיעדוף או קיבוץ. המשימה: לייעל את חוויית האישור — מיון לפי ביטחון/corroboration, קיבוץ near-duplicates יחד, auto-defer/הסתרה של פריטים באיכות נמוכה, ופעולות batch (אישור/דחייה מרובים). מבוססת מחקר (human-in-the-loop review UX, active-learning prioritization, triage queues). תתי-המשימות לאחר המחקר.", + "details": "הקשר: הדחייה כמעט לא בשימוש (1/1650) — התור הוא 'אשר-או-השאר-תלוי'. כלים קיימים: halachot_pending, halacha_review (MCP), דף ביקורת ב-UI. לשלב עם פלט #81 (איכות) ו-#82 (dedup) כדי שהתור יציג רק מועמדים אמיתיים ומקובצים.", + "testStrategy": "מדידת זמן/קליקים לאישור N הלכות לפני/אחרי. בדיקה: פריטים כמעט-זהים מוצגים כקבוצה אחת; פריטי איכות-נמוכה אינם מופיעים כברירת-מחדל בתור.", + "status": "pending", + "dependencies": [ + "81", + "82" + ], + "priority": "medium", + "subtasks": [ + { + "id": 1, + "title": "סינון מועמדים אמיתיים בלבד בתור (quality gating מ-#81)", + "description": "הסתרת פריטים שסומנו low-quality (quote_verified=false, rule_type=application, truncated) מתצוגת ברירת-המחדל של halachot_pending; ניתובם ל-bucket 'דורש תיקון-חילוץ'.", + "details": "מקור: default-defer/auto-archive של איכות-נמוכה (Prodigy/content-moderation). צורך פלט #81. כלי: halachot_pending.", + "status": "pending", + "dependencies": [], + "testStrategy": "תור ברירת-מחדל מחזיר 0 פריטים עם דגלי low-quality; פרמטר include_low_quality=true עדיין חושף אותם.", + "parentId": "84" + }, + { + "id": 2, + "title": "קיבוץ near-duplicates לכרטיס ביקורת אחד (מ-#82)", + "description": "halachot_pending מחזיר near-duplicates (cosine ≥0.90) מקובצים בכרטיס אחד: נציג קנוני + מונה וריאנטים + רשימת וריאנטים.", + "details": "מקור: similarity-clustering + review-by-cluster (Labelbox/Label Studio). צורך פלט #82.", + "status": "pending", + "dependencies": [], + "testStrategy": "קבוצת כפילויות ידועה (מהניקוי 2026-06-03) חוזרת ככרטיס מקובץ אחד ולא N שורות; הוריאנטים ניתנים למנייה.", + "parentId": "84" + }, + { + "id": 3, + "title": "תיעדוף התור לפי ציון משוקלל (uncertainty + impact)", + "description": "החלפת FIFO בציון עדיפות משולב: ביטחון באזור-אפור קודם, מוגבר ע\"י corroboration של ציטוט-בודד ותחומי-עיסוק בכיסוי דליל; דיכוי כפילויות של פריט-הראש.", + "details": "מקור: active learning — least-confidence first + diversity/impact weighting (Encord/Label Studio/greip). uncertainty לבד מדגים יתר-על-המידה near-dups.", + "status": "pending", + "dependencies": [], + "testStrategy": "בהינתן set מתוכנן, ראש התור הוא הפריט בעל הציון המשולב הגבוה; שתי כפילויות לא מופיעות יחד ב-top-5.", + "parentId": "84" + }, + { + "id": 4, + "title": "פעולות batch: אישור/דחייה לכל הקבוצה", + "description": "הרחבת halacha_review לקבלת מזהה-קבוצה והחלת approve/reject על כל הוריאנטים בקריאה אחת, עם אפשרות override ידני של וריאנט לפני commit.", + "details": "מקור: propagate-one-decision-to-group + checkpoint אנושי על bulk (Labelbox). סיכון: bulk-apply עיוור מפיץ שגיאה — חובה הצגת וריאנטים לפני אישור.", + "status": "pending", + "dependencies": [], + "testStrategy": "אישור קבוצת 5 וריאנטים מסמן את כולם approved בפעולה אחת; דחייה מסמנת את כולם rejected; override של וריאנט בודד אפשרי.", + "parentId": "84" + }, + { + "id": 5, + "title": "דחייה/השהיה זולה + סמנטיקת reject נכונה", + "description": "הוספת outcomes מפורשים reject ו-defer ל-halacha_review; reject שומר אות שלילי מתמשך (מזין חזרה ל-#81), defer משאיר pending ומחזיר לסוף התור.", + "details": "מקור: Prodigy accept/reject/ignore — הבחנה סמנטית היא הפתרון ל-1/1650. כיום אין פועל 'זבל' זול ולכן זבל מצטבר כ-pending.", + "status": "pending", + "dependencies": [], + "testStrategy": "reject קובע status rejected מתמשך + סיבה (queryable למשוב מחלץ); defer משאיר pending אך מוריד עדיפות.", + "parentId": "84" + }, + { + "id": 6, + "title": "UI: ביקורת keyboard-first עם 4 מקשים (a/r/space/e)", + "description": "עיצוב-מחדש של דף הביקורת ב-Next.js לכרטיס-בכל-פעם, מונע-מקלדת (Approve a / Reject r / Defer space / Edit e), עם הקשר-קבוצה וציטוט-מקור inline.", + "details": "מקור: keyboard-first מעלה throughput 20-30% בלי פגיעה באיכות (CleverX); one-card-at-a-time מפחית עומס קוגניטיבי (Hick's law). דף: web-ui /feedback או דף ביקורת ייעודי.", + "status": "pending", + "dependencies": [ + 4, + 5 + ], + "testStrategy": "מבקר יכול approve/reject/defer/edit-then-approve כולו במקלדת בלי עכבר; הכרטיס מציג מונה-וריאנטים וציטוט-מקור.", + "parentId": "84" + }, + { + "id": 7, + "title": "מדדי תור: throughput + איכות", + "description": "אינסטרומנטציה וחשיפה: time-per-item, decisions-per-session, יחס approve/reject/defer/edit, pending-count + גיל-הישן-ביותר, ומדגם spot-check post-hoc של פריטים מאושרים.", + "details": "מקור: Prodigy metrics + IAA (Krippendorff α / Gwet AC2). מבקר-יחיד לא נמדד ב-IAA → spot-check error rate חיוני.", + "status": "pending", + "dependencies": [ + 5 + ], + "testStrategy": "endpoint/דף מדדים מדווח את הנ\"ל; נמדד before/after של קליקים-וזמן לביקורת N=20 הלכות.", + "parentId": "84" + } + ], + "updatedAt": "2026-06-03T00:00:00.000Z" + }, + { + "id": "85", + "title": "CEO MCP instance: nested claude -p exits 1 in write_interim_draft", + "description": "write_interim_draft נכשל לכל 5 הבלוקים מתוך session ה-CEO עם 'Claude CLI failed (exit 1): unknown error'. אומת: claude CLI תקין מ-bash (exit 0), PATH+HOME של תהליך ה-MCP תקינים (/home/chaim/.local/bin/claude), אין ANTHROPIC_API_KEY ב-.env, הבלוקים נכתבים סדרתית (לא concurrency). סוכני משנה (proofreader/analyst CMPA-73..76) הריצו claude -p בהצלחה באותו יום. ⇒ כשל ספציפי ל-MCP server instance של ה-CEO. עוקף: האצלה ל-writer agent. דרוש: לבדוק מדוע nested claude -p נכשל מ-instance זה (אולי session lock / env stale); שקול restart ל-CEO agent session.", + "details": "", + "testStrategy": "", + "status": "pending", + "dependencies": [], + "priority": "high", + "subtasks": [] } ], "metadata": { "version": "1.0.0", - "lastModified": "2026-06-03T08:10:57.844Z", - "taskCount": 79, - "completedCount": 72, + "lastModified": "2026-06-03T12:32:19.721Z", + "taskCount": 85, + "completedCount": 76, "tags": [ "legal-ai" ] diff --git a/mcp-server/src/legal_mcp/services/db.py b/mcp-server/src/legal_mcp/services/db.py index 05cceed..3fd9889 100644 --- a/mcp-server/src/legal_mcp/services/db.py +++ b/mcp-server/src/legal_mcp/services/db.py @@ -676,6 +676,15 @@ CREATE INDEX IF NOT EXISTS idx_halachot_practice ON halachot USING gin(practice_ CREATE INDEX IF NOT EXISTS idx_halachot_tags ON halachot USING gin(subject_tags); CREATE INDEX IF NOT EXISTS idx_halachot_vec ON halachot USING ivfflat (embedding vector_cosine_ops) WITH (lists = 50); +-- #83: halacha_index must be unique per precedent. The extractor assigns it as +-- MAX(halacha_index)+1 under an in-process store-lock + a cross-process advisory +-- lock, so collisions shouldn't occur — but per FireHydrant/OneUptime the +-- constraint is the actual correctness guarantee (the lock is the optimization). +-- A racing/double run now fails LOUDLY instead of silently appending duplicates +-- (the 2026-05/06 over-extraction root cause). Requires clean data first (see +-- scripts: the 6 colliding precedents were renumbered 2026-06-03). +CREATE UNIQUE INDEX IF NOT EXISTS idx_halachot_unique_index + ON halachot(case_law_id, halacha_index); """