# 05 — בקרת איכות ושערים אנושיים (QA & Human Review) קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומפרט את שלב **הביקורת** לפני ייצוא: (1) **שערי-QA אוטומטיים** (`validate_decision` — 6 בדיקות) ו-(2) **שערים אנושיים** (אישור הלכה, בחירת תוצאה, פידבק היו"ר). הוא אוכף את [INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) (שערים אנושיים) ואת [INV-G11](00-constitution.md#inv-g11-תוכן-החלטה-מנומקת) (תוכן מנומק). > **⚠ קובץ מעורב — שני מודלי-סמכות.** לפי החוקה (§3, §5): > - **שערי-הממשל** (שערים אנושיים, שער-הייצוא) הם **invariants הנדסיים** במודל > הממשל-שיפוטי → נושאים `מקורות:` (NCSC/JTC · CEPEJ 2018 · FJC) + `סטטוס: verified`. > - **מכניקת בדיקות-התוכן** (מה הבדיקה האוטומטית בוחנת בפועל — רקע ניטרלי, ללא כפילות, > כיסוי-טענות) היא **תוכן-משפטי** → נושאת `מקור-סמכות:` (היו"ר + מסמכי-הפרויקט + > [04-analysis-writing.md](04-analysis-writing.md)), **ללא** מקורות חיצוניים וללא סטטוס. --- ## 1. שערי-QA אוטומטיים — `validate_decision` `validate_decision(case_number)` (wrapper ב-`tools/drafting.py:363`, נחשף ב-`server.py:551`) טוען את בלוקי-ההחלטה והטענות מה-DB ומריץ **6 בדיקות**, אז כותב את התוצאות לטבלת `qa_results` ומחזיר `passed` / `critical_failures` / `export_blocked`. הליבה: `services/qa_validator.py:292` (`validate_decision`). כל בדיקה מחזירה `{name, passed, errors, severity}`; `severity ∈ {critical, warning}`. > **חישוב החסימה:** `critical_failures = Σ(not passed ∧ severity=="critical")` > (`qa_validator.py:338`), ו-`export_blocked = critical_failures > 0` > (`qa_validator.py:362`). בדיקת `warning` שנכשלת מורידה `passed=False` אך **אינה** חוסמת > ייצוא. ראה [§3 / INV-QA3](#inv-qa3-החלטה-לא-מיוצאת-עם-כשל-קריטי-governance--g10). ### 1.1 ששת השערים | # | בדיקה | מה בוחנת | severity | פונקציה (file:line) | |---|-------|----------|----------|---------------------| | 1 | `neutral_background` | רקע (בלוק ו) ללא מילות-שיפוט (`VALUE_WORDS`) וללא ציטוט-צד (`QUOTE_INDICATORS`) | **warning** | `check_neutral_background` — `qa_validator.py:66` | | 2 | `claims_coverage` | כל טענה מבלוק ז נענתה בבלוק י (בדיקה סמנטית דרך Claude) | **critical** | `check_claims_coverage` — `qa_validator.py:107` | | 3 | `weight_compliance` | משקל-מילים של כל בלוק בטווח לפי סוג-ערר (`WEIGHT_RANGES`) | **warning** | `check_weight_compliance` — `qa_validator.py:177` | | 4 | `structural_integrity` | בלוקי-חובה קיימים (ה, ז, י, יא) + בלוק י הוא הכבד ביותר | **critical** | `check_structural_integrity` — `qa_validator.py:206` | | 5 | `no_duplication` | אין משפט מבלוק ו (>30 תווים) שחוזר מילה-במילה בבלוק י | **warning** | `check_no_duplication` — `qa_validator.py:235` | | 6 | `sequential_numbering` | מספור-סעיפים רציף בכל הבלוקים, מתחיל ב-1, ללא פערים | **warning** | `check_sequential_numbering` — `qa_validator.py:261` | ### 1.2 דקויות חשובות (אל תניח — מהקוד) - **רק 2 שערים קריטיים** חוסמים ייצוא: `claims_coverage` ו-`structural_integrity`. שאר הארבעה הם `warning` (כולל `neutral_background`!) — `qa_validator.py:86, 202, 240, 286`. - **`claims_coverage` סובלני ל-20%:** עובר אם `len(missing) ≤ total*0.2` (`qa_validator.py:170`). מסנן לטענות `appellant`/`respondent` שאינן מבלוק-ז (`qa_validator.py:120-129`), כי טענות `committee`/`permit_applicant` הן עמדות-הגנה ולא דורשות מענה. כשל-פענוח של Claude → fallback `passed=True` כדי לא לחסום ייצוא על תקלת-LLM (`qa_validator.py:148-152`). - **`neutral_background` ריק = עובר:** בלוק ו ריק/חסר מחזיר `passed=True` (`qa_validator.py:69`). הבדיקה היא lexical (רשימת-מילים + regex), לא סמנטית. - **`no_duplication` תופס רק חזרה מילה-במילה** (substring) — לא פרפרזה. - כל ריצה **מנקה** את `qa_results` הקודמות של התיק ואז כותבת מחדש (`qa_validator.py:344-357`). ### 1.3 שערי-התוכן מתפעלים את WR1–WR3 שלוש מ-6 הבדיקות הן ההפעלה האוטומטית (חלקית) של ה-invariants של התוכן ב- [04-analysis-writing.md](04-analysis-writing.md): | שער QA | invariant-תוכן | פער (אוטומטי מול הגדרה) | |--------|----------------|--------------------------| | `neutral_background` | [INV-WR1](04-analysis-writing.md#inv-wr1-רקע-ניטרלי-בלוק-ו--עובדות-בלבד) | lexical בלבד — לא תופס שיפוט עקיף; warning, לא critical | | `no_duplication` | [INV-WR2](04-analysis-writing.md#inv-wr2-ללא-כפילות-בלוק-י-מפנה-לא-חוזר) | מילה-במילה בלבד — לא תופס כפילות מנוסחת-מחדש | | `claims_coverage` | [INV-WR3](04-analysis-writing.md#inv-wr3-מענה-לכל-טענה-של-הצד-המפסיד) | סמנטי (Claude), סובלני ל-20% חוסר | ראה [INV-QA4](#inv-qa4-שערי-התוכן-האוטומטיים-אוכפים-את-wr1wr3-content--g11). WR4 (טענות מקוריות) ו-WR5 ("מבחן-השופט") **אינם** מכוסים על-ידי `validate_decision` — WR4 נאכף בנקודת-החילוץ (`extract_claims`), WR5 הוא שער-איכות אנושי/agent. הסוכן `legal-qa` (ראה [X4-agents.md](X4-agents.md)) מוסיף שערים ידניים מעבר ל-6 הקוד-יים (קול-דפנה, שאילתות-קורפוס, צירוף-פסיקה) — `.claude/agents/legal-qa.md`. --- ## 2. שערים אנושיים — היו"ר מכריעה המערכת מסייעת; ההכרעה היא של היו"ר. שלושה שערים אנושיים מובנים בקוד-הזרימה ואינם ניתנים לעקיפה אוטומטית (זהו [INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant)). ### 2.1 אישור הלכה (halacha approval) הלכות מחולצות אוטומטית מפסיקה (`halacha_extractor.py`), אך **נכנסות כ-`pending_review` ובלתי-נראות לחיפוש** עד אישור היו"ר: - **כתיבה:** `db.add_halacha` קובע `review_status = "approved" if auto_approve else "pending_review"` (`db.py:3003`), כאשר `auto_approve` נגזר מסף-ביטחון `HALACHA_AUTO_APPROVE_THRESHOLD` (ברירת-מחדל `0.80`, `config.py:111`). הלכות מתחת לסף נשארות `pending_review`. - **שער-האישור:** `halacha_review(halacha_id, status, reviewer="דפנה", …)` (`tools/precedent_library.py:291`, נחשף ב-`server.py:298`) — היו"ר מאשרת/דוחה/עורכת. `status ∈ {pending_review, approved, rejected, published}` (`precedent_library.py:311`). - **תור-ההמתנה:** `halachot_pending(limit=100)` (`precedent_library.py:335`) מחזיר את `review_status='pending_review'`. - **חשיפה רק לאחר אישור:** החיפוש מסנן `h.review_status IN ('approved','published')` (`db.py:3168` ו-`db.py:3401`) — הלכה שלא אושרה **לעולם** לא עולה בתוצאות. ### 2.2 בחירת תוצאה (outcome selection) `set_outcome(case_number, outcome, reasoning="")` (`tools/workflow.py:145`, `server.py:646`) — היו"ר קובעת `outcome ∈ {rejected, accepted, partial}` (`workflow.py:163`). זוהי **הכרעה משפטית**: היא קודמת לכתיבת-הטיוטה וקובעת את מסלול-הדיון (ראה [04-analysis-writing.md](04-analysis-writing.md) §3). אין נתיב שבו המערכת בוחרת תוצאה לבד — אם לא סופק נימוק, המערכת מציעה כיווני-נימוק (`brainstorm`), אך הבחירה נשארת אנושית. ### 2.3 פידבק היו"ר (chair feedback) - `record_chair_feedback(case_number, feedback_text, block_id, category, …)` (`tools/workflow.py:348`, `server.py:896`) — מתעד הערת-דפנה; `category` מתוך `{missing_content, wrong_tone, wrong_structure, factual_error, style, other}` (`workflow.py:367`). - `list_chair_feedback(case_number, category, unresolved_only=True)` (`tools/workflow.py:393`, `server.py:910`) — שליפה לסקירה. הפידבק מזין את לולאת-הלמידה ([07-learning.md](07-learning.md)) ואת [legal-decision-lessons.md](../legal-decision-lessons.md). זהו שיפוט-אנושי על איכות — לעולם לא מוסק או מוחל אוטומטית. --- ## 3. Invariants של התחום ### INV-QA1: אישור הלכה הוא שער אנושי (governance →G10) **כלל:** אישור הלכה הוא **הכרעה ידנית של היו"ר**. הלכות שחולצו אוטומטית הן `pending_review` עד שהיו"ר מאשרת; **רק הלכות מאושרות** (`approved`/`published`) עולות בחיפוש. תור-ההמתנה חייב להיות **נראה** (`halachot_pending`) כדי שאישור-חסר לא יישאר סמוי. **מקורות:** NCSC/JTC — *Principles & Practices for AI Use in Courts* (human-in-the-loop) · Council of Europe / CEPEJ (2018, under user control) · Federal Judicial Center — *Judicial Writing Manual* (2d ed.) | סטטוס: verified **אכיפה:** ברירת-מחדל `pending_review` בכתיבה (`db.py:3003`) + סינון `review_status IN ('approved','published')` בכל query (`db.py:3168`, `db.py:3401`) + שער-אישור `halacha_review` (`precedent_library.py:291`). **הפרה ידועה:** 10/19 הלכות מאושרות — שער-ידני שקוף בלי נראות-backlog; ההפרש התגלה במקרה → ממצא ל-[audit](../audit-report.md) (ראה גם [INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant)). ### INV-QA2: בחירת-תוצאה ופידבק הם שערים אנושיים (governance →G10) **כלל:** **בחירת התוצאה** (`set_outcome`) ו**פידבק-היו"ר** (`record_chair_feedback`) הם שערים אנושיים — **לעולם לא אוטומטיים**. המערכת מסייעת (מציעה כיווני-נימוק, מתעדת הערות), אך ההכרעה והשיפוט-על-האיכות הם של היו"ר. **מקורות:** NCSC/JTC — *Principles & Practices for AI Use in Courts* ("never replace human judgment") · Council of Europe / CEPEJ (2018, under user control) · Federal Judicial Center — *Judicial Writing Manual* (2d ed.) | סטטוס: verified **אכיפה:** `set_outcome` דורש `outcome` מפורש מהיו"ר (`workflow.py:145-165`); `record_chair_feedback`/`list_chair_feedback` מתעדים בלבד (`workflow.py:348, 393`) — אין מסלול-קוד שמסיק תוצאה או פידבק לבד. **הפרה ידועה:** — ### INV-QA3: החלטה לא מיוצאת עם כשל קריטי (governance →G10) **כלל:** החלטה **אינה ניתנת לייצוא** כל עוד שער-QA **קריטי** נכשל (`claims_coverage` או `structural_integrity`). `export_blocked` חייב להיבדק לפני ייצוא; ייצוא בכשל-קריטי הוא הפרה. שערי-`warning` שנכשלים מתועדים אך אינם חוסמים. **מקורות:** NCSC/JTC — *Principles & Practices for AI Use in Courts* (controlled, auditable AI output) · Council of Europe / CEPEJ (2018, under user control) · Federal Judicial Center — *Judicial Writing Manual* (2d ed.) | סטטוס: verified **אכיפה:** `export_blocked = critical_failures > 0` (`qa_validator.py:362`); נאכף בשער-הזרימה של הסוכן `legal-exporter` ("לעולם אל תייצא בלי `validate_decision` קודם", "בדוק שאין כשלים קריטיים" — `.claude/agents/legal-exporter.md:71, 149`). קושר ל-[06-export.md](06-export.md). **הפרה ידועה:** `export_docx` (`drafting.py:384`) **אינו** מריץ `validate_decision` בעצמו — החסימה היא ברמת-הזרימה/agent, לא hard-block בקוד-הייצוא. פער זה → ראה [§4](#4-current-vs-target--ממצאי-audit) (audit). ### INV-QA4: שערי-התוכן האוטומטיים אוכפים את WR1–WR3 (content →G11) **כלל:** שערי-התוכן האוטומטיים מתפעלים את invariants-התוכן: `neutral_background`↔ [WR1](04-analysis-writing.md#inv-wr1-רקע-ניטרלי-בלוק-ו--עובדות-בלבד) (רקע ניטרלי) · `no_duplication`↔[WR2](04-analysis-writing.md#inv-wr2-ללא-כפילות-בלוק-י-מפנה-לא-חוזר) (ללא כפילות) · `claims_coverage`↔[WR3](04-analysis-writing.md#inv-wr3-מענה-לכל-טענה-של-הצד-המפסיד) (מענה-לטענות). האכיפה האוטומטית היא **רצפה, לא תקרה** — WR4/WR5 וההבטים העדינים (שיפוט-עקיף, כפילות מנוסחת-מחדש) נשארים בשיקול-הדעת האנושי (INV-QA1–QA3). **מקור-סמכות:** היו"ר (עו"ד דפנה תמיר) + [04-analysis-writing.md](04-analysis-writing.md) (INV-WR1–WR3) + `mcp-server/src/legal_mcp/services/qa_validator.py` (הבדיקות בפועל). **אכיפה:** `check_neutral_background` (`qa_validator.py:66`), `check_no_duplication` (`qa_validator.py:235`), `check_claims_coverage` (`qa_validator.py:107`). **הפרה ידועה:** — --- ## 4. Current vs Target — ממצאי-audit - **Halacha backlog בלתי-נראה (INV-QA1):** 10/19 הלכות מאושרות; 9 נשארו `pending_review` ולא עלו בחיפוש. השער עבד כשורה — אך חוסר-נראות של ה-backlog הסתיר את הפער עד שהתגלה במקרה. **Target:** מדד-נראות (count `pending_review`) כחלק מבדיקת-בריאות, לא רק `halachot_pending` בדרישה. ראה [audit](../audit-report.md). - **שער-ייצוא אכוף-זרימה ולא אכוף-קוד (INV-QA3):** `export_docx` לא קורא ל-`validate_decision`; החסימה תלויה במשמעת הסוכן `legal-exporter`. **Target:** hard-block בתוך `export_docx` (בדיקת `qa_results`/`export_blocked` לפני כתיבת DOCX) כדי שלא יהיה ניתן לעקיפה. --- ## 5. הפניות-אחיות - [00-constitution.md](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) — INV-G10 (שערים אנושיים) + INV-G11 + הבחנת שתי-הסמכויות. - [04-analysis-writing.md](04-analysis-writing.md) — INV-WR1–WR5 שהשערים האוטומטיים מתפעלים. - [06-export.md](06-export.md) — ייצוא DOCX (השלב אחרי המעבר בשער הקריטי). - [07-learning.md](07-learning.md) — לולאת פידבק-היו"ר + Hermes שמעדכנת lessons/SKILL. - [X4-agents.md](X4-agents.md) — הסוכן `legal-qa` (שערים ידניים נוספים) ו-`legal-exporter`. - [X5-audit-provenance.md](X5-audit-provenance.md) — audit-trail לפלטי-AI ועקיבוּת-מקור.