diff --git a/docs/spec/05-qa-review.md b/docs/spec/05-qa-review.md new file mode 100644 index 0000000..f53b6a8 --- /dev/null +++ b/docs/spec/05-qa-review.md @@ -0,0 +1,195 @@ +# 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 ועקיבוּת-מקור.