Files
legal-ai/docs/spec/gap-audit.md

126 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gap-Audit — פערים בין המערכת הקיימת ל-spec
מסמך זה הוא **מפת-הפערים הקנונית** בין המערכת הקיימת (קוד ב-`web/`, `mcp-server/`,
`scripts/`) לבין ה-invariants שב-[`docs/spec/`](README.md). הוא תוצר של תת-פרויקט 2
(מיפוי-פערים), ומובחן מ-[`docs/audit-report.md`](../audit-report.md) הישן: ה-audit הוא
דוח-מצב נקודתי, וזה ה-gap-map שמקשר כל ממצא ל-invariant מופר וליחידת-תיקון.
**איך הופק:** סקירה חוצת-קבצים של כל קבצי-הספ (00 + 0107 + X1X5) מול הקוד הקיים,
30.5.2026. כל ממצא נושא: `invariant מופר` (ה-G*/INV-* שהוא סותר), הערכת-`severity`,
`קבצים מושפעים` (file:line), ו-`תיקון מוצע`.
**הערה על severity/priority:** דירוג ה-severity להלן הוא הערכה הנדסית (לפי סיכון
לשלמות-נתונים, דליפה חוצת-קורפוס, ועקיפת שער אנושי). **קביעת ה-priority בפועל —
מה לתקן ראשון — היא של היו"ר.** ה-severity מנמק; הוא אינו מכריע.
---
## 23 הממצאים
| ID | כותרת | invariant מופר | severity | קבצים מושפעים (file:line) | תיקון מוצע |
|----|-------|----------------|----------|---------------------------|------------|
| GAP-01 | שני מסלולי ingest מקבילים שמתפצלים | INV-ING1, G2 | High | `precedent_library.py:88`, `internal_decisions.py:73` | מסלול-קליטה קנוני יחיד; ישויות-אחיות חולקות פייפליין |
| GAP-02 | ingest פנימי מדלג על חילוץ metadata | INV-ING3, DM1, RET2 | Critical | `internal_decisions.py:208` | להוסיף `request_metadata_extraction` לכל סוג; חוסם indexing ריק |
| GAP-03 | אין upsert דטרמיניסטי על מזהה קנוני | INV-ING2, G3 | Critical | `precedent_library.py`, `internal_decisions.py` | upsert על מפתח קנוני — קליטה חוזרת = update לא duplicate |
| GAP-04 | ולידציית-enum א-סימטרית | INV-G4 | Medium | `precedent_library.py:131-134` | להחיל אותה ולידציית practice_area/source_type בשני המסלולים |
| GAP-05 | staging/derivation/citation-guard/multimodal/fallback א-סימטריים | INV-ING1, G2 | High | `01-ingest §4` (שני המסלולים) | מיזוג כל שלבי-העיבוד למסלול הקנוני האחד |
| GAP-06 | case_number מנורמל בקריאה בלבד | INV-G1, ID1 | High | `db.py:1196-1211` | נרמול בנקודת-הכתיבה; `8126-25`→canonical |
| GAP-07 | מספרי-תיק מעורבים (חודש/חסר) — reconciliation חד-פעמי | INV-ID1 | High | data (cases, case_law) | מיגרציה: canonical = הצורה הרשמית שהוקצתה [chair-confirmed] |
| GAP-08 | ציטוט-מלא נשמר כ-case_number | INV-DM2, ID2 | Medium | data (legacy pre-V15) | ניקוי: ציטוט = שדה-תצוגה נגזר, לא מזהה |
| GAP-09 | `embedding` אינו GENERATED (בניגוד ל-tsvectors) | INV-DM3, RET, G6 | High | schema (chunks/case_law) | re-index באכיפה — טריגר או GENERATED-equivalent בשינוי תוכן |
| GAP-10 | דליפת הלכה חוצת-קורפוס | INV-RET1, G5 | Critical | `db.py:3168`, `db.py:3401`, JOINs `3236-3238`/`3475-3477` | להוסיף `cl.source_kind` ל-halacha_filters |
| GAP-11 | אין eval harness / gold-set מתויג | INV-RET4, G8 | High | `telemetry.log_search_bg` (היחיד) | להקים eval harness + gold-set; precision/recall נמדד |
| GAP-12 | search_decisions מזהיר אך לא חוסם practice_area חסר | INV-RET, G5 | High | `search.py:45-49`, `search.py:172-176` | לחסום query בלי practice_area — ערבוב-תחום אסור |
| GAP-13 | אין דגל `searchable` מפורש | INV-DM1 | Medium | schema (case_law, chunks) | דגל `searchable` שמסומן רק כשחוזה-השלמות מתקיים |
| GAP-14 | backlog הלכות סמוי | INV-QA1, G10 | Medium | (אין health-check) | לחשוף `pending_review` ב-health-check / dashboard |
| GAP-15 | שער-ייצוא נאכף-זרימה ולא נאכף-קוד | INV-QA3, EX3 | Critical | `drafting.py:384` | `export_docx` קורא `validate_decision` + בודק `export_blocked` |
| GAP-16 | neutral_background קריטי-אך-עובר | INV-QA3 (`05 §1.2`) | High | `qa_validator.py:70` | בלוק-ו ריק/חסר = passed=False; חוסם ייצוא |
| GAP-17 | active_draft_path נגזר זוחל ל-source-of-truth | INV-EX1, AUD2 | High | `db.py:189` | DOCX = נגזר; re-sync בלוקים אחרי revise/apply_user_edit |
| GAP-18 | audit_log כמעט לא נכתב | INV-AUD1 | High | `cases.py:203` (היחיד) | כתיבת audit על upload/extract/write_block/export |
| GAP-19 | אין קישור block→source-chunks | INV-AUD1 | High | `decision_blocks` (model_used בלבד) | לתעד אילו chunks/precedents הזינו כל בלוק |
| GAP-20 | citation→corpus לא נאכף אוטומטית | INV-AUD3 | Medium | `decision_paragraphs.citations` | ולידציה שכל ציטוט בטקסט פתיר לקורפוס |
| GAP-21 | cross-company sync ידני ולא-נאכף | INV-MC1 | Medium | `sync_agents_across_companies.py:387-389` | אכיפת `--apply` אחרי שינוי-Master; להרעיש על דילוג adapter_type |
| GAP-22 | אינטגרציית-Paperclip על נוהל ולא מחסום-קוד | INV-INT1, INT3 | Medium | schema / lint (אין) | אילוץ-schema נגד DB-insert; linter נגד httpx/curl גולמי |
| GAP-23 | הספ עדיין לא מחובר לסוכנים | INV-AG1 | High | `.claude/agents/HEARTBEAT.md`, agent files | חובת קריאת 00-constitution + ספ-תחום לפני פעולה |
---
## יחידות-תיקון מוצעות (Proposed Fix-Units)
23 הממצאים מקובצים ל-8 יחידות-עבודה קוהרנטיות. הקיבוץ נגזר מהעיקרון שרבים מהממצאים
נפתרים יחד (כל פערי ה-ingest-asymmetry → יחידה אחת). זהו זרע למשימות TaskMaster
ולתת-פרויקט 3 (שכבת-שלמות).
### FU-1 — איחוד מסלול-הקליטה (Unify ingest path)
- **מכסה:** GAP-01, GAP-02, GAP-04, GAP-05
- **מספק invariants:** INV-ING1, INV-ING3, INV-G2, INV-G4; (תורם ל-DM1/RET2 דרך GAP-02)
- **effort:** L
- **תלויות:** — (יסוד — FU-2/FU-3 נשענים עליה)
- **סוג:** pure-code
### FU-2 — קליטה idempotent + מזהים קנוניים
- **מכסה:** GAP-03, GAP-06, GAP-07, GAP-08, GAP-13
- **מספק invariants:** INV-ING2, INV-G3, INV-G1, INV-ID1, INV-ID2, INV-DM2, INV-DM1
- **effort:** L
- **תלויות:** FU-1 (מסלול אחד לפני upsert אחיד)
- **סוג:** **data-migration** — GAP-07 reconciliation של case_number מעורב (chair-confirmed),
GAP-08 ניקוי ציטוט-כ-מזהה; + code (upsert key, write-time normalize, דגל searchable)
### FU-3 — re-index באכיפה בשינוי-תוכן
- **מכסה:** GAP-09
- **מספק invariants:** INV-DM3, INV-G6, INV-RET (freshness)
- **effort:** M
- **תלויות:** FU-1 (re-embed יושב בקליטה הקנונית)
- **סוג:** **data-migration** — re-chunk/re-embed של רשומות קיימות + טריגר/אכיפה קדימה
### FU-4 — הפרדת-קורפוס נאכפת בכל query
- **מכסה:** GAP-10, GAP-12
- **מספק invariants:** INV-RET1, INV-G5
- **effort:** M
- **תלויות:** — (עצמאי; דחוף — Critical leak)
- **סוג:** pure-code
### FU-5 — eval harness + נראות-בריאות
- **מכסה:** GAP-11, GAP-14
- **מספק invariants:** INV-RET4, INV-G8, INV-QA1, INV-G10 (נראות backlog)
- **effort:** M
- **תלויות:** FU-2 (gold-set יציב דורש מזהים קנוניים)
- **סוג:** pure-code + **chair-decision** — הגדרת gold-set מתויג דורשת אישור היו"ר
(מה "תוצאה נכונה" לכל query)
### FU-6 — שערי-QA נאכפים-קוד (Code-enforced gates)
- **מכסה:** GAP-15, GAP-16
- **מספק invariants:** INV-QA3, INV-EX3, INV-G10
- **effort:** S
- **תלויות:** — (עצמאי; חוסם עקיפת-ייצוא)
- **סוג:** pure-code
### FU-7 — Audit-trail + provenance (זרע תת-פרויקט 3)
- **מכסה:** GAP-17, GAP-18, GAP-19, GAP-20
- **מספק invariants:** INV-AUD1, INV-AUD2, INV-AUD3, INV-EX1, INV-G9
- **effort:** L
- **תלויות:** FU-1 (provenance נלכד בקליטה/כתיבה הקנונית)
- **סוג:** pure-code (schema-additive) — חלק מ-GAP-17 דורש **data-backfill** קל
לסנכרון בלוקים↔DOCX קיימים
### FU-8 — מחסומי-תהליך הופכים למחסומי-קוד
- **מכסה:** GAP-21, GAP-22, GAP-23
- **מספק invariants:** INV-MC1, INV-INT1, INV-INT3, INV-AG1
- **effort:** M
- **תלויות:** ה-spec גמור (GAP-23 דורש קבצי-ספ יציבים לחבר לסוכנים)
- **סוג:** pure-code + **chair-decision** — GAP-23 (חיבור ספ לסוכני-Paperclip) הוא
prerequisite לתת-פרויקט 5 ומשנה התנהגות-סוכן בייצור
---
## סיכום סיווג לפי סוג-עבודה
- **pure-code (ללא מיגרציה):** FU-1, FU-4, FU-6; הליבה של FU-7, FU-8.
- **דורש data-migration:** FU-2 (case_number reconciliation, ניקוי ציטוטים), FU-3
(re-chunk/re-embed), backfill קל ב-FU-7 (סנכרון בלוקים↔DOCX).
- **דורש chair-decision:** FU-5 (הגדרת gold-set), FU-8/GAP-23 (חיבור ספ לסוכנים);
GAP-07 כבר chair-confirmed (canonical = הצורה הרשמית שהוקצתה).
**רצף מומלץ (תלויות):** FU-1 → FU-2 → FU-3; FU-4 ו-FU-6 במקביל (עצמאיים, Critical);
FU-7 אחרי FU-1; FU-5 אחרי FU-2; FU-8 אחרי ייצוב-הספ.