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

קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומגדיר את **מודל-הנתונים הקנוני (TARGET)**
של עוזר משפטי — הישויות, שדות-המפתח, והיכן יושב כל פריט מואנדקס. הוא אוכף את
[G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה) (מזהה קנוני יחיד),
[G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) (חוזה-שלמות) ו-
[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן) (re-index בשינוי-תוכן).

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

---

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

הטבלה מונה את ישויות-הליבה. "מזהה-קנוני" = השדה היחיד המזהה רשומה ([G1](00-constitution.md#inv-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`, `is_primary` (**נגזר** מ-`doc_type`, ראה [§2ג](#2ג-מסמך-עיקרי-primary-document)), `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](#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`) |
| `plans` | מרשם-תכניות — זהות+תוקף של תב"ע, שימוש חוזר בין תיקים (V38) | `plan_number` מנורמל (`UNIQUE`) | `display_name`, `aliases`, `plan_type`, `gazette_date`, `yalkut_number`, `purpose`, `citation_formatted`, `review_status`, provenance (`source_case_number`/`source_document_id`/`model_used`), `discrepancies` (SCHEMA_V38) |

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

---

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

[G4](00-constitution.md#inv-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](#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.
  > **עיצוב-מחדש זרימת-העבודה (WS2, #200):** `documents` מקבל `is_primary`/`doc_category` (רשימה
  > קנונית: ערר/תשובה/תגובה/פרוטוקול/שומה/החלטת-ועדה) + דגל-ניתוח per-מסמך (`claims_extraction_status`),
  > כדי לחשב "יש מסמך-עיקרי שלא-נכלל-בניתוח". השדות בבעלות #200; ניתוח-החילוץ עצמו ב-[04-analysis-writing](04-analysis-writing.md) §1.
- `halachot` → usable: `rule_statement`+`supporting_quote`; **searchable: `review_status ∈ {approved, published}` בלבד** — `pending_review`/`rejected` מוסתרות מ-`search_precedent_library` (שער-הלכה ידני, `db.py:644-660`, [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant)).
- `decision_blocks` → usable: `block_id`∈12-הבלוקים; "מוכן": `status=final` ו-`content` לא-ריק.
- `chair_feedback` → usable: `feedback_text`+`category` מהמילון; "פתוח" עד `resolved=true`.

### 2ג. מסמך-עיקרי (primary document)

WS2 (#200) מבחין בין **מסמך-עיקרי** (מסמך-מהות שהיו"ר עוקבת אחריו לניתוח) לבין מסמך-משני.
**הרשימה הקנונית של doc_types עיקריים** (מאושרת-יו"ר): `appeal` (כתב-ערר) · `response`
(תשובה/תגובה) · `objection` (התנגדות) · `protocol` (פרוטוקול-דיון) · `appraisal` (שומה) ·
`decision` (החלטת-ועדה). **כל doc_type אחר משני** (`plan`/`permit`/`court_decision`/
`exhibit`/`reference`).

**`is_primary` נגזר, לא נכתב** ([G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה)/[G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים)/
[INV-DM7](#inv-dm7-סיווג-הלכה--סמכות-נגזרת--תפקיד-כלל-מסווג-שני-צירים-לא-enum-אחד)): מקור-האמת היחיד הוא
`doc_type` + הרשימה הקנונית `PRIMARY_DOC_TYPES` (`db.py`). העמודה
`documents.is_primary BOOLEAN GENERATED ALWAYS AS (doc_type = ANY(PRIMARY_DOC_TYPES)) STORED`
(SCHEMA_V47) מחושבת ע"י Postgres — **אין מסלול-כתיבה מקביל ולא יכולה לסטות** מ-`doc_type`,
בדיוק כמו ה-tsvectors ב-[INV-DM3](#inv-dm3-שינוי-תוכן--re-index). `_row_to_doc` גוזר גם בקריאה
(לרשומות טרום-מיגרציה) ומוסיף `doc_category ∈ {primary, secondary}` לפלט. נחשף דרך
`document_list` / `case_get` / API-המסמכים. אינדקס חלקי `idx_documents_primary(case_id) WHERE
is_primary` משרת את שאילתת "מסמכים-עיקריים שטרם-נותחו" (#201).

**דגל "לא-נותח" — per-מסמך (#201, SCHEMA_V49).** שני שדות מתעדים את *אירוע* חילוץ-הטענות
של המסמך (לא נגזרים מ-`doc_type`): `claims_extracted_at TIMESTAMPTZ` (נחתם ע"י
`claims_extractor` **אחרי** ששמרנו את טענות המסמך) ו-`claims_extraction_status TEXT`
(`pending`/`completed`/`no_claims`/`failed`). `_row_to_doc` חושף `claims_analyzed`
(`claims_extracted_at IS NOT NULL`). מקור-האמת היחיד ל"האם המסמך נכלל בניתוח" הוא
`claims_extracted_at` — לא משוחזר ממקום אחר. אינדקס חלקי `idx_documents_claims_pending(case_id)
WHERE is_primary AND claims_extracted_at IS NULL` (יחד עם `is_primary` של V47) הופך את
"מסמך-עיקרי שנוסף-אחרי-ניתוח ולא-נכלל" (`db.primary_docs_not_analyzed`) לשאילתה זולה.
נחשף ב-`workflow_status` (`primary_docs_not_analyzed` + next-step). הניתוח-מחדש המאחד
(`reanalyze_claims`, [04-analysis-writing §1.3](04-analysis-writing.md)) מחלץ-מחדש רק את
המסמכים-העיקריים החדשים/לא-נותחו, **מאחד** עם הטענות הקיימות, ומריץ צבירה-מחדש (G2 — מסלול
קנוני אחד, לא fork).

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

מעבר לישויות-המקור, המערכת **שומרת ניתוחים נגזרים** — תוצרי-חילוץ של LLM/קוד. אלו כפופים לכללי
ה-provenance של [X8](X8-field-provenance.md) ולשערי [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant):

| ישות-נגזרת | מקור-מילוי | שער-אישור | קישור-מקור |
|------------|------------|-----------|------------|
| `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` |
| `plans` | claude-local (`plans_extractor`) / ידני-יו"ר (`plan_upsert`) | **`review_status`** ✓ (כמו halachot, INV-DM5/G10) | `source_document_id` (FK); `source_case_number`; `discrepancies` (סתירת-תוקף מול שורה מאושרת — לא-דורס, §6) |
| `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א](#2א-case_law--החוזה-הקונקרטי)
מתקיים במלואו (מזהה קנוני · `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](01-ingest.md) צעד 8) + בדיקת-בריאות
תקופתית שמסמנת backlog; הסינון נאכף בשכבת-החיפוש ([03-retrieval.md](03-retrieval.md)). אוכף את
[G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש).
**הפרה ידועה:** ערן סופר 8046/24 אונדקס כ-searchable עם `headnote`/`summary`/`subject_tags`
ריקים — המסלול הפנימי לא תיזמן חילוץ-מטא-דאטה ([01-ingest INV-ING3](01-ingest.md#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג),
`internal_decisions.py:208`) → ממצא ל-[audit](../audit-report.md).

### 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](X1-identifiers.md)); ציטוט-מלא ב-`citation_formatted` בלבד. אוכף את
[G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה).
**הפרה ידועה:** החלטות "סופר" נקלטו עם **ציטוט-מלא כ-`case_number`** (שדה-המזהה של רשומה מכיל את
מחרוזת-הציטוט במקום מספר-תיק מנורמל) — חיפוש מול `8126-03-25` נכשל, ו-`_normalize_case_number`
(`db.py:1196-1211`) רק **מטליא בקריאה** (סלחני, לא קנוני), בניגוד ל-[G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה)
→ ממצא ל-[audit](../audit-report.md).

### 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](03-retrieval.md). אוכף את
[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן).
**הפרה ידועה:** —

### INV-DM4: לכל ישות-נגזרת — provenance מוצהר
**כלל:** כל ישות-נגזרת (claims, legal_arguments, appraiser_facts, decision_blocks, halachot) נושאת
**provenance** — מי/מה הפיק (מודל, גרסה, זמן) ולאילו chunks/מקורות היא קשורה. מופע של
[G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai); מקביל ל-[X8 INV-FP1](X8-field-provenance.md).
**מקורות:** 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](gap-audit.md)).

### INV-DM5: פלט-ניתוח של LLM נכנס בשער-אישור (כמו halachot)
**כלל:** ישות-נגזרת שמוּלאת ע"י LLM ומשפיעה על ההחלטה נכנסת **לא-מאושרת** עד אישור-יו"ר — אותו שער כמו
`halachot.review_status`. מופע של [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant); תואם [X8 INV-FP3](X8-field-provenance.md).
**מקור-סמכות:** דפוס `halachot.review_status` (`db.py:659`); [05-qa-review.md](05-qa-review.md). (פרויקטלי-תפעולי — משרת G10.)
**אכיפה:** שדה-סטטוס-אישור על ישויות-נגזרות מהותיות.
**הפרה ידועה:** `legal_arguments` **חסר** שער-אישור — נכתב ומשמש ללא בקרת-יו"ר ([gap-audit GAP-39](gap-audit.md)).

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

### INV-DM7: סיווג-הלכה — סמכות (נגזרת) ⊥ תפקיד-כלל (מסווג). שני צירים, לא enum אחד
**כלל:** ל-`halachot` שני צירי-סיווג **אורתוגונליים** שאסור לערבב בשדה אחד:
- **סמכות (`authority`) — נגזרת בלבד, לא מאוחסנת, לא מנוחשת ע"י LLM.** `binding` (מקור מחייב את הוועדה: עליון/מנהלי) מול `persuasive` (מקור משכנע: ועדת-ערר אחרת). נגזרת דטרמיניסטית מ-`case_law.precedent_level` (`עליון`/`מנהלי`→binding; `ועדת_ערר_מחוזית`→persuasive). מקור-אמת יחיד — מחושבת בקריאה, אין עמודה כפולה ([G1](00-constitution.md#inv-g1-נרמול-במקור-לא-תיקון-בקריאה)/[G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים)).
- **תפקיד-כלל (`rule_type`/rule_role) — מסווג ע"י ה-LLM.** `holding` (עיקרון מהותי הכרחי להכרעה — ratio/Wambaugh) · `interpretive` (פרשנות חוק/מונח/תכנית) · `procedural` (סדר-דין: סמכות/מועדים/נטל) · `application` (החלה תלוית-עובדות — לרוב לא-הלכה) · `obiter` (אמרת-אגב). **`binding`/`persuasive` אינם ערכי תפקיד** — הם סמכות-מקור.
**הנדסי.** מופע של [G1](00-constitution.md#inv-g1-נרמול-במקור-לא-תיקון-בקריאה) (נרמול במקור: המחלץ מסווג תפקיד, לא ממציא סמכות נגזירה) ו-[G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים).
**מקורות:** OASIS LegalRuleML v1.0 (`appliesAuthority`/`Strength` כ-metadata אורתוגונלי, נפרד מלוגיקת-הכלל) · SemEval-2023 Task 6 LegalEval (rhetorical-roles לפי תפקיד, סמכות נשמרת בנפרד) · Bluebook signals (משקל-סמכות = ציר נפרד מהפרופוזיציה) | סטטוס: verified (≥3 מקורות).
**ההפרה שתוקנה:** `halacha_extractor` סיווג `rule_type` לפי bindingness-של-המקור (`_coerce_halacha(is_binding)`, ברירת-מחדל `binding`/`persuasive`, guard binding→persuasive) — כלומר חישב **סמכות** במסווה של **תפקיד**. אומת אמפירית על מדגם-הזהב: `binding` שימש 19/19 פסקים חיצוניים ו-0 ועדות; `persuasive` 13/13 ועדות ו-0 חיצוניים → סיווג-לפי-מקור, התאמה לתיוג-אנושי 58% בלבד. התיקון מעביר סמכות לציר-נגזר ומשחרר את ה-LLM לסווג תפקיד נטו.

---

## 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](#inv-dm2-מזהה-קנוני-יחיד-לכל-ישות). **יעד:** נרמול-בכתיבה אכוף + ציטוט-מלא רק ב-`citation_formatted`.
- **`summary` קיים על `case_law` אך לא בחוזה-הקליטה הפנימי — ✅ נסגר (FU-1, GAP-02).** העמודה
  קיימת (`db.py:373`), והמסלול הפנימי לא מילא אותה בעבר (היעדר חילוץ-מטא-דאטה). **נסגר:** שני
  המסלולים זורמים דרך `ingest.ingest_document` הקנוני שמתזמן metadata לשני הסוגים
  ([01-ingest INV-ING3](01-ingest.md#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג)); `searchable`
  מותנה ב-metadata לא-ריק ([INV-DM1](#inv-dm1-searchable-רק-כשחוזה-השלמות-מתקיים)).
- **דגל-`searchable` מפורש — ✅ קיים.** מעבר ל-`extraction_status` + `halacha_extraction_status`
  (`db.py:603-605`, תהליך), קיימת כעת עמודת `case_law.searchable` המסמנת "עבר חוזה-שלמות"; שכבת-
  החיפוש מסננת לפיה (`cl.searchable = true`, `db.py:7517,7519`) ו-`recompute_searchable_flag`
  מתחזק אותה. אוכף את [INV-DM1](#inv-dm1-searchable-רק-כשחוזה-השלמות-מתקיים) בשכבת-האחזור.
- **`embedding` אינו `GENERATED` (בניגוד ל-tsvector).** ה-tsvectors מסונכרנים אוטומטית
  (`db.py:776,780,1083`), אך ה-`embedding vector(1024)` תלוי-טריגר חיצוני — נקודת-drift אפשרית
  ל-[INV-DM3](#inv-dm3-שינוי-תוכן--re-index). **יעד:** טריגר re-embed מובטח + health-check ל-drift.
- **נראות backlog-הלכות — ✅ נסגר (FU-5, GAP-14).** הסינון תקין (`pending_review` מוסתר,
  `db.py:659`); ההפרה הישנה היתה היעדר-נראות (10/19 מאושרות, התגלה במקרה). **נסגר:** ה-health-
  check חושף `halacha_backlog` (`web/app.py:2352-2364`, `metrics_service.halacha_backlog`),
  והכלים `extraction_status` + `halachot_pending` חושפים את עומק-התור — שער-היו"ר ([G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant)) הופך נראה.

---

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

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