# 01 — קליטה מאוחדת (Unified Ingest Contract) קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומפרט את **חוזה הקליטה** של כל סוגי ה-intake. הוא אוכף את [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) (מקור-אמת יחיד, אין מסלולים מקבילים) ואת [G3](00-constitution.md#inv-g3-ingest-אחיד-ו-idempotent) (ingest אחיד ו-idempotent), ונשען על [G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) ו-[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן). כשל-השורש שהקובץ מייבש: **שני מסלולי ingest לישויות-אחיות שמתפצלים** — `ingest_precedent` (פסיקה חיצונית) מול `ingest_internal_decision` (החלטות-ועדה). מסלולים מקבילים גוררים drift: פריט שנקלט במסלול אחד מקבל טיפול שונה מפריט במסלול האחר, והפער מתגלה רק כשרשומה חסרה metadata או לא נמצאת בחיפוש. החוזה כאן מגדיר **מסלול קנוני אחד** ש-3 סוגי ה-intake עוברים בו. --- ## 1. שלושת סוגי ה-intake | סוג-intake | מזהה-קנוני | קורפוס-יעד | מאפיין ייחודי | |------------|------------|------------|----------------| | מסמכי-תיק (case documents) | `case_number` + מזהה-מסמך | תיק ערר פעיל | משויך לתיק, מסווג לפי סוג-מסמך | | פסיקה חיצונית (external precedent) | `citation` (קנוני) | `case_law` (external) | staging לפי `source_type`, ולידציית-enums, citation guard, multimodal | | החלטות-ועדה (internal-committee) | `case_number` (קנוני) | `case_law` (internal_committee) | staging לפי district, `chair_name` חובה, גזירת district/proceeding_type | שלושתם הם **ישויות-אחיות**: אותו טיפוס-עיבוד (קובץ → טקסט → chunks → embeddings → metadata → הלכות), נבדלים בפרמטרים בלבד — לא במסלול-קוד. זוהי משמעות "סימטריה" (חוקה §6). --- ## 2. המסלול הקנוני (Canonical Pipeline) צעדי-העיבוד, **בסדר מחייב**. כל סוג-intake עובר את אותם צעדים; ההבדל הוא אילו פרמטרים מוזרקים בקלט, לא אילו צעדים מורצים. 1. **Stage file** — העתקה דטרמיניסטית לאחסון המתמיד. נתיב-ה-staging הוא פרמטר (`source_type` לפסיקה חיצונית, district להחלטות-ועדה), לא ענף-קוד נפרד. 2. **Extract text** — `extractor.extract_text` → `(text, page_count, page_offsets)`. טקסט ריק = כשל מדווח (לא בליעה שקטה; חוקה §6). 3. **Strip Nevo preamble** — `extractor.strip_nevo_preamble` להסרת עטיפת-Nevo. **אחיד לכל סוג.** 4. **Chunk** — היררכי (`chunk_document_hierarchical`) אם `PARENT_DOC_RETRIEVAL_ENABLED`, אחרת שטוח (`chunk_document`). **אותו ענף-flag בדיוק לכל סוג** — בורר הצ'אנקינג נגזר מ-config, לא מסוג-ה-intake. 5. **Embed** — `embeddings.embed_texts(..., input_type="document")` ל-children (היררכי) או לכל ה-chunks (שטוח). 6. **Store chunks** — `store_precedent_chunks_hierarchical` או `store_precedent_chunks`. 7. **Page-image embed (multimodal)** — אם `MULTIMODAL_ENABLED` **וגם** הקובץ PDF **וגם** `page_count>0`: הטמעת עמודי-תמונה (`_embed_precedent_pages`). non-fatal: מסלול-הטקסט כבר הצליח. **התנאי אחיד** — הפעלה תלויה ב-flag+סוג-קובץ, לא בסוג-ה-intake. 8. **Queue metadata extraction** — `request_metadata_extraction(case_law_id)`. נדרש לכל סוג שתומך במטא-דאטה (ראה [INV-ING3](#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג)). 9. **Queue halacha extraction** — `request_halacha_extraction(case_law_id)`. 10. **Set statuses** — `extraction_status=completed`, `halacha_status=pending`. החילוץ ה-LLM-י (metadata + הלכות) רץ בנפרד מ-Claude Code המקומי (`precedent_process_pending`), כי `claude` CLI אינו זמין בקונטיינר. > **צעדים שחייבים להיות אחידים בכל סוג (תיקון האסימטריה):** 2 (extract), 3 (strip-Nevo), > 4 (בורר-chunk לפי flag), 5–6 (embed+store), **7 (multimodal — לפי flag+PDF, לא לפי > סוג)**, **8–9 (תיזמון שני החילוצים)**, 10 (statuses). מה ש**רשאי** להשתנות לפי סוג: > נתיב-ה-staging (צעד 1), ולידציות-קלט ספציפיות, וגזירת-שדות (district/proceeding_type) > — אלו פרמטרים של אותו מסלול, לא מסלול נפרד. --- ## 3. Invariants של התחום ### INV-ING1: מסלול-קליטה קנוני יחיד **כלל:** כל סוגי ה-intake (מסמכי-תיק / פסיקה חיצונית / החלטות-ועדה) זורמים דרך **פונקציית- קליטה קנונית אחת**. סוג-intake חדש מורחב דרך **פרמטרים** של אותה פונקציה — לעולם לא דרך פונקציה מקבילה. נתון-נגזר (district, proceeding_type) מחושב בתוך המסלול, לא בענף נפרד. **מקורות:** Martin Kleppmann, *DDIA* (O'Reilly, 2017 — system of record יחיד) · Martin Fowler (*Canonical Data Model*) · SSOT (Single Source of Truth) | סטטוס: verified **אכיפה:** ביקורת-ארכיטקטורה + כלל-הנדסה "סימטריה" (חוקה §6); הקליטה מתנקזת לפונקציה אחת שמקבלת פרמטרי-סוג. אוכף את [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים). **הפרה ידועה:** היום קיימים **שני** מסלולים — `ingest_precedent` (`precedent_library.py:88`) ו-`ingest_internal_decision` (`internal_decisions.py:73`) — שמשכפלים את צעדי 2–10 ומתפצלים בפרטים → ממצא ל-[audit](../audit-report.md). ### INV-ING2: קליטה idempotent על המזהה הקנוני **כלל:** הקליטה היא **idempotent על המזהה הקנוני** (`citation` לפסיקה חיצונית, `case_number` להחלטות-ועדה ולמסמכי-תיק). קליטה חוזרת של אותו פריט = **upsert** — אין רשומה כפולה ואין chunks כפולים; התוצאה זהה. **מקורות:** Martin Kleppmann, *DDIA* (idempotence & exactly-once) · Stripe / CDC idempotency-key pattern · ISO 8000 (Data quality) | סטטוס: verified **אכיפה:** מפתח-upsert דטרמיניסטי על המזהה הקנוני בנקודת-הקליטה (`create_external_case_law` / `create_internal_committee_decision`) + ולידציית-כתיבה; קשור ל- [X1-identifiers.md](X1-identifiers.md) (נרמול בכתיבה). אוכף את [G3](00-constitution.md#inv-g3-ingest-אחיד-ו-idempotent). **הפרה ידועה:** 3 החלטות "סופר" נקלטו ב-3 פורמטים (`8126/24`, ציטוט-מלא כ-`case_number`) — היעדר מפתח-upsert דטרמיניסטי גרר רשומות-כפל במקום עדכון → ממצא ל-[audit](../audit-report.md). ### INV-ING3: תור חילוץ מטא-דאטה + הלכות לכל סוג **כלל:** חילוץ-מטא-דאטה **וגם** חילוץ-הלכות מתוזמנים (queue) עבור **כל** סוג-intake שתומך בהם — תיזמון אחיד, **לא** מותנה במסלול. שני התורים נפתחים יחד בסיום העיבוד הלא-LLM-י. **מקורות:** ISO 8000 (completeness) · DAMA-UK *Six Primary Dimensions for Data Quality* (2013, completeness) · Martin Fowler (quality-at-source) | סטטוס: verified **אכיפה:** קריאה ל-`request_metadata_extraction` **ו**-`request_halacha_extraction` בנקודת-סיום-הקליטה, לכל סוג; חוזה-שלמות יסמן רשומה ללא מטא-דאטה כלא-שמישה ([G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש), מפורט ב- [02-data-model.md](02-data-model.md)). **הפרה ידועה:** המסלול הפנימי (`internal_decisions.py:208`) מתזמן **רק** `request_halacha_extraction` ואינו קורא ל-`request_metadata_extraction` (בניגוד ל-`precedent_library.py:292-293` שקורא לשניהם) → ערן סופר 8046/24 נקלטה **בלי metadata** (headnote/summary/tags ריקים) → ממצא ל-[audit](../audit-report.md). ### INV-ING4: re-index בקליטה-חוזרת (upsert ⇒ re-embed) **כלל:** קליטה-חוזרת ששינתה את תוכן-הפריט מפעילה **re-index** — chunks ו-embeddings ישנים נמחקים ונבנים מחדש מהתוכן החדש. אין embeddings מיושנים אחרי upsert. **מקורות:** Pinecone (index freshness / data sync) · Weaviate (re-vectorization on update) · RAG freshness (Lewis et al., 2020, NeurIPS) | סטטוס: verified **אכיפה:** טריגר re-embed בנתיב ה-upsert של הקליטה + בדיקת-בריאות לגילוי drift; מפורט ב-[02-data-model.md](02-data-model.md) ו-[03-retrieval.md](03-retrieval.md). אוכף את [G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן). **הפרה ידועה:** — --- ## 4. מצב קיים מול יעד — audit-findings הסעיף מתעד את ההבדלים בין שני המסלולים הקיימים. **אלו תסמינים לאיחוד תחת המסלול הקנוני, לא התנהגויות תקינות.** כל פריט אומת מול הקוד בפועל. - **חילוץ מטא-דאטה חסר במסלול הפנימי.** `ingest_precedent` קורא ל- `request_metadata_extraction` **ו**-`request_halacha_extraction` (`precedent_library.py:292-293`); `ingest_internal_decision` קורא **רק** ל- `request_halacha_extraction` (`internal_decisions.py:208`) — אין `request_metadata_extraction`. זו ההפרה הקונקרטית של [INV-ING3](#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג) (ערן סופר 8046/24). **יעד:** צעד 8 אחיד לשני הסוגים. - **ולידציית-enums א-סימטרית.** המסלול החיצוני מוודא `practice_area`/`source_type` מול רשימות חוקיות (`precedent_library.py:131-134`); המסלול הפנימי **אינו** מוודא enums. **יעד:** ולידציה אחידה בנקודת-הקליטה (חוזה-שלמות, [G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש)). - **staging מפוצל.** החיצוני עושה stage לפי `source_type` (`precedent_library.py:138`); הפנימי עושה stage לפי district (`internal_decisions.py:113-115`). **יעד:** נתיב-staging כפרמטר של המסלול הקנוני (צעד 1), לא ענף-קוד. - **גזירת-שדות רק במסלול הפנימי.** הפנימי גוזר district מ-court (`:104`) ו-proceeding_type מ-appeal_subtype/case_name (`:105`), ודורש `chair_name` (`:134`). החיצוני אינו גוזר אלו. **יעד:** גזירה כפרמטר אופציונלי של המסלול הקנוני (שדות-סוג, לא מסלול-סוג). - **citation guard רק במסלול החיצוני.** החיצוני חוסם ציטוט שמתחיל ב-`ערר`/`בל"מ` ומפנה למסלול הפנימי (`precedent_library.py:124-130`). היעד שומר על השער הזה כניתוב-סוג בתוך המסלול הקנוני, לא כהפרדת-פונקציות. - **multimodal page-image embed רק במסלול החיצוני.** החיצוני מטמיע עמודי-תמונה כש- `MULTIMODAL_ENABLED` + PDF (`precedent_library.py:272-278`); הפנימי **אינו** מטמיע עמודי-תמונה. **יעד:** צעד 7 אחיד — מותנה ב-flag+סוג-קובץ בלבד. - **fallback `case_name→citation` רק במסלול החיצוני.** החיצוני נופל ל-`citation` כשם כשחסר `case_name` (`precedent_library.py:158`); הפנימי נופל ל-`case_number` (`internal_decisions.py:130`). **יעד:** מדיניות-fallback אחת לשם-תצוגה במסלול הקנוני. --- ## 5. הפניות-אחיות - [00-constitution.md](00-constitution.md) — invariants גלובליים + כללי-הנדסה. - [02-data-model.md](02-data-model.md) — סכמת-האחסון + חוזה-שלמות שאוכף את תוצרי הקליטה. - [03-retrieval.md](03-retrieval.md) — אחזור, re-index, eval — היעד של ה-chunks הנקלטים. - [X1-identifiers.md](X1-identifiers.md) — נרמול המזהה הקנוני בכתיבה (בסיס ל-INV-ING2). - [X5-audit-provenance.md](X5-audit-provenance.md) — שלמות-רשומה + עקיבוּת-מקור של פריט נקלט.