WS7 of the workflow-redesign initiative required closing 7 known leaks along the way. Cross-code audit verified ALL 7 were already closed in prior cycles (FU-1/4/5/6/7 + IA waves); the spec still carried stale "known violation" text. This PR aligns the spec to actual code state and verifies each closure with a test. No new code path created — this is a spec-alignment audit, not a re-solve. 7 leaks → status (all closed/gated, each with a test): 1. cross-corpus halachot (#56, GAP-10/FU-4) — cl.source_kind in halacha_filters (db.py:7516,7519); test_precedent_corpus_isolation.py 2. invisible halacha backlog (GAP-14/FU-5) — health halacha_backlog (app.py:2352-2364) + extraction_status/halachot_pending 3. no eval harness (GAP-11/FU-5, G8) — scripts/eval_retrieval.py (P/R/MRR/nDCG vs gold-set+baseline); --self-test ALL PASS 4. export gate not hard-block (GAP-15/FU-6, INV-EX3) — export_docx checks qa_run_exists + get_critical_qa_failures before exporter (drafting.py:462-494); test_export_qa_gate.py 5. metadata on internal path (GAP-02/FU-1, INV-ING3) — unified ingest.ingest_document queues metadata+halacha together (ingest.py:233-234); test_unified_ingest.py 6. DOCX creep to source-of-truth (GAP-17/FU-7, INV-EX1) — active_draft_path is revision-anchor only; drift caught by cases.blocks_stale flag (V22) 7. UI cache-invalidation (GAP-33/FU-10) — qc.invalidateQueries on mutations Also added forward-pointer notes for the workflow-redesign additions owned by sibling tasks (02 documents is_primary/doc_category → #200; 04 re-analysis / summarize_party_claims / analyze_protocol → #200/#202/#203), kept additive to avoid line conflicts. block-schema.md interim/template edits deferred to #204/#205. Invariants: maintains G2 (single source of truth, no parallel path — all leak closures extend the canonical path), G8 (eval harness now exists), G10 (human gates visible: halacha backlog + hard export gate), INV-ING3 (unified metadata queue), INV-EX1 (DOCX is derived; blocks_stale drift flag), INV-RET1/G5 (#56 cross-corpus halacha isolation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
152 lines
13 KiB
Markdown
152 lines
13 KiB
Markdown
# 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)).
|
||
**הפרה ידועה — ✅ נסגרה (FU-1, GAP-02):** המסלול הפנימי דילג בעבר על חילוץ-המטא-דאטה
|
||
(`internal_decisions.py:208` תיזמן רק `request_halacha_extraction`) → ערן סופר 8046/24 נקלטה
|
||
בלי metadata. **נסגר:** שני מסלולי-הפסיקה זורמים כעת דרך `ingest.ingest_document` הקנוני, שמתזמן
|
||
**את שני התורים יחד** בנקודה אחת (`ingest.py:233-234`) — תיזמון-המטא אינו מותנה במסלול עוד.
|
||
מאומת ב-`test_unified_ingest.py` (9/9). תיעוד-מקור: [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
|
||
|
||
הסעיף מתעד את ההבדלים בין שני המסלולים הקיימים. **אלו תסמינים לאיחוד תחת המסלול הקנוני,
|
||
לא התנהגויות תקינות.** כל פריט אומת מול הקוד בפועל.
|
||
|
||
- **חילוץ מטא-דאטה חסר במסלול הפנימי.** ראה [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) — שלמות-רשומה + עקיבוּת-מקור של פריט נקלט.
|