Files
legal-ai/docs/spec/02-data-model.md
Chaim 37c56ff22a docs(spec): cycle-2 — 8 application-surface domains (X6–X10) + ui-audit + GAP-24..62/FU-9..15
Extends the system spec beyond the core pipeline to the 8 surfaces outside it:
- X6 UI↔API contract + design rules (INV-UI1..6)
- X7 Paperclip client & connection params (INV-INT4..8)
- X8 field-population & extraction provenance (INV-FP1..5)
- X9 MCP tool contract — 71 tools (INV-TOOL1..6)
- X10 deploy/env/secrets (INV-ENV1..5)
- ui-audit.md — page-by-page UI audit (13 pages)
- 02-data-model: derived-entity invariants (INV-DM4..6)
- X4-agents: tool-grant map + INV-AG3
- gap-audit: GAP-24..62 → FU-9..15; cycle-1 (FU-1..8b) marked done
- constitution §7 + README index (X1..X10)

Planning/spec artifacts only — no application code. All engineering invariants
backed by ≥3 sources; every finding carries verified file:line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 16:21:27 +00:00

18 KiB
Raw Permalink Blame History

02 — מודל-הנתונים (Data Model & Completeness Contract)

קובץ-תחום זה כפוף ל-חוקת המערכת ומגדיר את מודל-הנתונים הקנוני (TARGET) של עוזר משפטי — הישויות, שדות-המפתח, והיכן יושב כל פריט מואנדקס. הוא אוכף את G1 (מזהה קנוני יחיד), G4 (חוזה-שלמות) ו- G6 (re-index בשינוי-תוכן).

TARGET, לא תיאור-מצב. המודל כאן הוא היעד הקנוני. כל מקום שבו ה-schema בפועל (mcp-server/src/legal_mcp/services/db.py) סוטה ממנו — מתועד כ-audit-finding (§4), תסמין לאיחוד, לא התנהגות תקינה. כל טענה על ה-schema הקיים מצוטטת file:line.


1. הישויות הקנוניות

הטבלה מונה את ישויות-הליבה. "מזהה-קנוני" = השדה היחיד המזהה רשומה (G1).

ישות תפקיד מזהה-קנוני שדות-מפתח (מאומתים db.py)
cases תיק ערר חי (1xxx/8xxx/9xxx) case_number + proceeding_type title, status, practice_area, appeal_subtype, proceeding_type, chair_name (db.py:74-91,182-189,747,912)
documents מסמך-מקור משויך לתיק id (UUID); FK→cases doc_type, title, file_path, extracted_text, extraction_status, page_count (db.py:93-104)
document_chunks chunk של מסמך-תיק + embedding id; FK→documents/cases chunk_index, content, section_type, embedding vector(1024), page_number (db.py:106-116)
case_law קורפוס פסיקה — חיצוני וגם החלטות-ועדה ראה §2 + INV-DM2 case_name, court, practice_area, source_kind, proceeding_type, source_type, headnote, summary, subject_tags, extraction_status, halacha_extraction_status (db.py:366-378,522-526,599-611,883,907)
precedent_chunks chunk של פסק-דין מואנדקס (source_kind='external_upload'/internal_committee) id; FK→case_law chunk_index, content, section_type, page_number, embedding vector(1024), content_tsv (db.py:624-634,776)
halachot הלכה מחולצת — כלל + ציטוט מילולי id; FK→case_law rule_statement, supporting_quote, rule_type, practice_areas, subject_tags, confidence, quote_verified, review_status, embedding, rule_tsv (db.py:644-666,780)
decisions החלטת-תיק מנוסחת (גרסה) id; UNIQUE(case_id, version) version, status, outcome, outcome_summary (db.py:299-314)
decision_blocks בלוק (12) של החלטה id; UNIQUE(decision_id, block_id) block_id, block_index, content, status (db.py:317-334)
claims טענת-צד (בלוק ז) id; FK→cases party_role, claim_text, source_document, claim_type, claim_handling (db.py:349-359,506-512)
chair_feedback הערת-יו"ר על טיוטה id; FK→cases block_id, feedback_text, category, lesson_extracted, resolved (db.py:452-462)
missing_precedents תקדים חסר שהתבקש ולא נמצא id (db.py:806) — backlog ל-quality-at-source
style_corpus קורפוס-סגנון של דפנה (אימון) id; FK→documents decision_number, full_text, practice_area, appeal_subtype (db.py:118-131)

שכבות-עזר נוספות (document_image_embeddings, precedent_image_embeddings — multimodal, db.py:707,726; case_law_relations — שרשרת-תיק, db.py:754; precedent_internal_citations — גרף-ציטוטים, db.py:937) הן נגזרות (G2): משוחזרות מהמקור, לא מקור-אמת עצמאי.


2. חוזה-שלמות לכל ישות (Completeness Contract)

G4 דורש: רשומה אינה "שמישה / ניתנת-לחיפוש" עד ששדות-החובה שלה מולאו ואומתו מול spec מפורש. כל ישות מגדירה שתי רמות — usable (קיימת ומזוהה) ו-searchable (חשופה לאחזור). רשומה שנכשלת בחוזה מסומנת ומדווחת — לא מתקבלת בשקט (חוקה §6, "אין בליעה שקטה").

2א. case_law — החוזה הקונקרטי

המזהה הקנוני אינו case_number לבדו: case_law נושאת שני unique partial indexes לפי source_kind (db.py:904-909) — חיצוני: UNIQUE(case_number); פנימי: UNIQUE(case_number, proceeding_type). לכן המזהה הקנוני הוא (case_number מנורמל, source_kind, proceeding_type).

רמת usable (רשומה לגיטימית):

  • case_number קנוני מנורמל-בכתיבה (INV-DM2לא ציטוט-מלא)
  • case_name לא-ריק (לא fallback לציטוט/למספר)
  • court לא-ריק
  • practice_area ∈ {rishuy_uvniya, betterment_levy, compensation_197} (אכוף ב-CHECK, db.py:614-617)
  • source_kind מהמילון (external_upload / cited_only / internal_committee / nevo_seed) (db.py:599-601, internal_decisions.py:4)
  • proceeding_type ∈ {ערר, בל"מ} כשפנימי (אכוף ב-CHECK, db.py:897-899)

רמת searchable (חשוף לאחזור — מעבר ל-usable):

  • ≥1 precedent_chunk עם embedding לא-NULL (אחרת אין מה לאחזר סמנטית)
  • metadata לא-ריק: לפחות אחד מ-headnote / summary / subject_tags מלא — אלו השדות ש-search מציג ומסנן לפיהם
  • extraction_status = completed (מטא-דאטה הושלם, db.py:603)

אכיפה מפורשת: רשומה שעוברת usable אך נכשלת ב-searchable — מסומנת searchable=false ולא מוחזרת מ-search, ומופיעה ב-health-check כ-backlog. היא אינה מתקבלת בשקט כ"זמינה".

2ב. חוזה תמציתי לישויות נוספות

  • documents → usable: file_path+doc_type; searchable: extraction_status=completed ו-extracted_text לא-ריק ו-≥1 document_chunk עם embedding.
  • halachot → usable: rule_statement+supporting_quote; searchable: review_status ∈ {approved, published} בלבדpending_review/rejected מוסתרות מ-search_precedent_library (שער-הלכה ידני, db.py:644-660, G10).
  • decision_blocks → usable: block_id∈12-הבלוקים; "מוכן": status=final ו-content לא-ריק.
  • chair_feedback → usable: feedback_text+category מהמילון; "פתוח" עד resolved=true.

2ג. ישויות-נגזרות (אחסון-ניתוחים)

מעבר לישויות-המקור, המערכת שומרת ניתוחים נגזרים — תוצרי-חילוץ של LLM/קוד. אלו כפופים לכללי ה-provenance של X8 ולשערי G10:

ישות-נגזרת מקור-מילוי שער-אישור קישור-מקור
claims OPUS (extract_claims) source_document (string, לא-FK)
legal_arguments (+legal_argument_propositions) OPUS (aggregate_claims_to_arguments) חסר (בניגוד ל-halachot) cited_precedents TEXT[] (לא-FK)
appraiser_facts OPUS (extract_appraiser_facts) document_id (FK); appraiser_side default ''
halachot OPUS (halacha_extractor) review_status case_law_id (FK); quote_verified
decision_blocks / decision_paragraphs Opus/script (write_block) status model_used + audit-event provenance (FU-7); citations JSONB ללא-FK

3. Invariants של התחום

INV-DM1: searchable רק כשחוזה-השלמות מתקיים

כלל: רשומת case_law נחשבת searchable אך ורק כשחוזה-השלמות של §2א מתקיים במלואו (מזהה קנוני · case_name/court/practice_area/source_kind · ≥1 chunk עם embedding · metadata לא-ריק). רשומה שנכשלת מסומנת searchable=false ומדווחת ל-health-check — לא מוחזרת מ-search ולא מתקבלת בשקט. מקורות: ISO 8000 (completeness) · DAMA-UK Six Primary Dimensions for Data Quality (2013, completeness) · ISO 15489-1:2016 (records reliability/usability) | סטטוס: verified אכיפה: ולידציית-כתיבה בנקודת-הקליטה (01-ingest.md צעד 8) + בדיקת-בריאות תקופתית שמסמנת backlog; הסינון נאכף בשכבת-החיפוש (03-retrieval.md). אוכף את G4. הפרה ידועה: ערן סופר 8046/24 אונדקס כ-searchable עם headnote/summary/subject_tags ריקים — המסלול הפנימי לא תיזמן חילוץ-מטא-דאטה (01-ingest INV-ING3, internal_decisions.py:208) → ממצא ל-audit.

INV-DM2: מזהה קנוני יחיד לכל ישות

כלל: לכל ישות מזהה קנוני אחד, מנורמל בכתיבה. אסור ששדה-המזהה יאחסן ציטוט-מלא — case_number הוא מספר-תיק מנורמל (8126-03-25), לא מחרוזת-ציטוט (ערר 8126/24 פלוני נ' הוועדה (נבו...)). הציטוט המלא חי בשדה ייעודי נפרד (citation_formatted, db.py:1070), לא במזהה. מקורות: SSOT (Single Source of Truth — normalization) · E.F. Codd, First Normal Form (CACM 13(6), 1970) · Martin Kleppmann, Designing Data-Intensive Applications (O'Reilly, 2017) | סטטוס: verified אכיפה: unique partial indexes על המזהה הקנוני (db.py:904-909) + נרמול-בכתיבה (X1-identifiers.md); ציטוט-מלא ב-citation_formatted בלבד. אוכף את G1. הפרה ידועה: החלטות "סופר" נקלטו עם ציטוט-מלא כ-case_number (שדה-המזהה של רשומה מכיל את מחרוזת-הציטוט במקום מספר-תיק מנורמל) — חיפוש מול 8126-03-25 נכשל, ו-_normalize_case_number (db.py:1196-1211) רק מטליא בקריאה (סלחני, לא קנוני), בניגוד ל-G1 → ממצא ל-audit.

INV-DM3: שינוי-תוכן ⇒ re-index

כלל: כל שינוי בתוכן-המקור של ישות מואנדקסת (content של chunk, rule_statement/supporting_quote של הלכה, full_text/extracted_text של מסמך) מפעיל re-index של ה-embedding ושל ה-tsvector הנגזרים. אין embedding או content_tsv/rule_tsv/meta_tsv מיושנים מול התוכן. מקורות: Pinecone (index freshness / data sync) · Weaviate (re-vectorization on update) · RAG freshness (Lewis et al., 2020, NeurIPS) | סטטוס: verified אכיפה: טריגר re-embed בנקודת-העדכון + בדיקת-בריאות לגילוי drift; ה-tsvectors GENERATED ALWAYS … STORED (db.py:776-788,1083-1090) מתעדכנים אוטומטית, אך ה-embedding אינו generated — הוא תלוי-טריגר. מפורט ב-03-retrieval.md. אוכף את G6. הפרה ידועה:

INV-DM4: לכל ישות-נגזרת — provenance מוצהר

כלל: כל ישות-נגזרת (claims, legal_arguments, appraiser_facts, decision_blocks, halachot) נושאת provenance — מי/מה הפיק (מודל, גרסה, זמן) ולאילו chunks/מקורות היא קשורה. מופע של G9; מקביל ל-X8 INV-FP1. מקורות: ISO 8000-110 (data lineage) · DAMA-DMBOK2 (lineage) · ISO 15489-1:2016 (records authenticity) | סטטוס: verified אכיפה: עמודות-provenance + קישור block→source (חלקית דרך audit-event ב-FU-7/GAP-19; ל-legal_arguments טרם). הפרה ידועה: legal_arguments ללא provenance; embedding ללא model/version (gap-audit GAP-42).

INV-DM5: פלט-ניתוח של LLM נכנס בשער-אישור (כמו halachot)

כלל: ישות-נגזרת שמוּלאת ע"י LLM ומשפיעה על ההחלטה נכנסת לא-מאושרת עד אישור-יו"ר — אותו שער כמו halachot.review_status. מופע של G10; תואם X8 INV-FP3. מקור-סמכות: דפוס halachot.review_status (db.py:659); 05-qa-review.md. (פרויקטלי-תפעולי — משרת G10.) אכיפה: שדה-סטטוס-אישור על ישויות-נגזרות מהותיות. הפרה ידועה: legal_arguments חסר שער-אישור — נכתב ומשמש ללא בקרת-יו"ר (gap-audit GAP-39).

INV-DM6: ולידציה — CHECK-enums, FK לציטוטים, ללא טבלאות-מקבילות

כלל: ערכי-enum נאכפים ב-CHECK (לא TEXT חופשי); ציטוט-מקור נשמר כ-FK (לא string/array חופשי); אין שתי טבלאות לאותה ישות. מופע של G4 ו-G2. הנדסי. מקורות: E.F. Codd (referential integrity, CACM 1970) · ISO 8000 (validity) · Kleppmann DDIA | סטטוס: verified אכיפה: CHECK על enums; FK על cited_precedents/decision_paragraphs.citations; איחוד case_precedentscase_law. הפרה ידועה: 20+ enums כ-TEXT חופשי; legal_arguments.cited_precedents TEXT[] ללא-FK (הזיות-LLM נבלעות); case_precedents מול case_law מקבילות (gap-audit GAP-40/42/43).


4. מצב קיים מול יעד — audit-findings

ההבדלים בין ה-schema בפועל ל-TARGET. אלו תסמינים, לא התנהגויות תקינות. כל פריט אומת מול db.py.

  • case_law כפולת-תפקיד ללא מזהה מודע-סוג בכתיבה. טבלה אחת משרתת פסיקה חיצונית וגם החלטות-ועדה, מובדלות ב-source_kind (db.py:599). המזהה הקנוני האמיתי הוא טריפלט (case_number, source_kind, proceeding_type, db.py:904-909), אך השדה case_number TEXT UNIQUE NOT NULL המקורי (db.py:368) הוסר רק ב-V15 (db.py:902-903) — מורשת שאפשרה את הפרת INV-DM2. יעד: נרמול-בכתיבה אכוף + ציטוט-מלא רק ב-citation_formatted.
  • summary קיים על case_law אך לא בחוזה-הקליטה הפנימי. העמודה קיימת (db.py:373) אך המסלול הפנימי אינו ממלא אותה (כפועל-יוצא מהיעדר חילוץ-מטא-דאטה, INV-ING3). יעד: searchable מותנה ב-metadata לא-ריק (INV-DM1).
  • שני שדות-סטטוס-חילוץ נפרדים, ללא דגל-searchable מפורש. extraction_status + halacha_extraction_status (db.py:603-605) מתארים תהליך, אך אין שדה יחיד שמסמן "עבר חוזה-שלמות → searchable". יעד: דגל/view נגזר ש-search מסנן לפיו, מגובה health-check.
  • embedding אינו GENERATED (בניגוד ל-tsvector). ה-tsvectors מסונכרנים אוטומטית (db.py:776,780,1083), אך ה-embedding vector(1024) תלוי-טריגר חיצוני — נקודת-drift אפשרית ל-INV-DM3. יעד: טריגר re-embed מובטח + health-check ל-drift.
  • halachot.review_status כשער-searchable ללא נראות-backlog. הסינון תקין (pending_review מוסתר, db.py:659), אך אין נראות כמה ממתינות — תואם את ההפרה הידועה ב-G10 (10/19 מאושרות, התגלה במקרה). יעד: health-check חושף backlog-הלכות.

5. הפניות-אחיות

  • 00-constitution.md — invariants גלובליים (G1, G4, G6) + כללי-הנדסה.
  • 01-ingest.md — חוזה-הקליטה שמייצר את הרשומות; חוזה-השלמות כאן אוכף את תוצריו.
  • 03-retrieval.md — שכבת-האחזור שאוכפת את הסינון searchable + re-index.
  • X1-identifiers.md — נרמול המזהה הקנוני בכתיבה (בסיס ל-INV-DM2).
  • X5-audit-provenance.md — שלמות-רשומה + עקיבוּת-מקור.
  • X8-field-provenance.md — מקור-מילוי השדות (בסיס ל-INV-DM4/DM5).
  • X9-mcp-tool-contract.md — הכלים שמייצרים את הישויות-הנגזרות.