From 8c4b8cf19ef6d47c2ba782a05a8cc197bbf1de68 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 17:05:43 +0000 Subject: [PATCH] docs(spec): X5-audit-provenance Co-Authored-By: Claude Opus 4.8 --- docs/spec/X5-audit-provenance.md | 163 +++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 docs/spec/X5-audit-provenance.md diff --git a/docs/spec/X5-audit-provenance.md b/docs/spec/X5-audit-provenance.md new file mode 100644 index 0000000..93113dd --- /dev/null +++ b/docs/spec/X5-audit-provenance.md @@ -0,0 +1,163 @@ +# X5 — Audit-Trail ועקיבוּת-מקור (Provenance) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומגדיר את **חוזה העקיבוּת וה-audit-trail (TARGET)** +של עוזר משפטי: (א) כל **תוצר מסיוע-AI** (בלוק-טיוטה, תוצאת-אחזור, הצעת-curator) מתעד **מה הפיק אותו** +(מקורות/נתונים/מודל); (ב) כל **סמכות מצוטטת** בהחלטה **פתירה חזרה לקורפוס**; (ג) **שלמות-הרשומה +לאורך זמן** — החלטה/רשומה שלמה ובלתי-משתנה אלא דרך **שינויים עקיבים ומיוחסים** (היסטוריית git + +Track Changes). הקובץ אוכף את +[INV-G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai) (עקיבוּת + audit-trail) ואת +[INV-G5](00-constitution.md#inv-g5-metadata-מלא--הפרדת-קורפוס-נאכפת-בכל-query) (attribution באחזור). + +> **TARGET, לא תיאור-מצב.** היכן שהקוד בפועל סוטה מהיעד — מתועד כ-**audit-finding** ([§5](#5-current-vs-target--ממצאי-audit)), +> תסמין לתיקון, לא התנהגות תקינה. כל טענה על הקוד מצוטטת `file:line`. + +כשל-השורש שהקובץ מייבש: **קיימים רכיבי-עקיבוּת נקודתיים** (commit git לפלטים · `model_used` לכל בלוק · +`decision_paragraphs.citations` · גרף-ציטוטים · telemetry של חיפושים), אך **אין רשומת-provenance +מאוחדת מקצה-לקצה** שמקשרת בלוק-החלטה → קטעי-הקורפוס/הגנרציות שהפיקו אותו, ו**טבלת ה-`audit_log` +אינה מתועדת בפועל** לרוב פעולות ה-AI. + +--- + +## 1. שלוש שכבות העקיבוּת (TARGET) + +| שכבה | מה צריך להירשם | היכן (קיים / יעד) | +|------|-----------------|---------------------| +| **A — provenance של תוצר-AI** | לכל בלוק-טיוטה/תוצאת-אחזור/הצעת-curator: מודל, סוג-גנרציה, וקטעי-המקור (chunks/precedents) שהוזנו | קיים חלקית: `decision_blocks.model_used/generation_type/temperature` (`db.py:326-328`); **חסר** קישור בלוק→קטעי-מקור | +| **B — עקיבוּת ציטוט→קורפוס** | כל סמכות מצוטטת פתירה ל-`case_law_id`/`document_id` + locator | קיים: `decision_paragraphs.citations` JSONB `[{case_law_id,text,type}]` (`db.py:343`); גרף `precedent_internal_citations` (`db.py:937-947`) | +| **C — שלמות-רשומה לאורך זמן** | החלטה/מסמך שלם ובלתי-משתנה אלא דרך שינוי עקיב ומיוחס | קיים: commit git לכל פלט (`git_sync.commit_and_push`); Track Changes ב-revisions ([06-export §3](06-export.md#3-רישום-הגרסה--active_draft_path--git)) | + +--- + +## 2. רכיבי-העקיבוּת הקיימים (מאומת `file:line`) + +1. **קיבוע-פלט ב-git.** כל כתיבת-DOCX/עדכון-תיק מקובעת בהיסטוריית-git של תיקיית-התיק: + `export_docx` (`drafting.py:408`), `export_interim_draft` (`drafting.py:536`), + `apply_user_edit` (`drafting.py:582`), `revise_draft` (`drafting.py:695`), עדכון-תיק + (`cases.py:387`), הוספת-מסמך (`documents.py:86`) — כולם `git_sync.commit_and_push(...)` + (`git_sync.py:75`). זו שכבת ה-audit-trail של **שלמות-הפלט** (שכבה C). +2. **provenance של מודל לכל בלוק.** `decision_blocks` נושא `model_used` / `generation_type` / + `temperature` (`db.py:326-328`), הנכתבים ב-upsert של ה-block-writer + (`block_writer.py:1017-1034`, `_build_result` `:400-407`). מתעד **איזה מודל** הפיק את הבלוק + (שכבה A — חלקי). +3. **עקיבוּת ציטוט ברמת-סעיף.** `decision_paragraphs.citations` (`db.py:343`) שומר + `[{case_law_id, text, type}]` — כל ציטוט בסעיף מצביע ל-`case_law` (שכבה B). telemetry + ממנף זאת ל-"cited == relevant" (`telemetry.py:18-23`). +4. **גרף-ציטוטים פנימי.** `precedent_internal_citations` (`db.py:937-947`) רושם קשת + החלטה→החלטה מצוטטת (resolved ל-`case_law` או stub); נחשף דרך `extract_internal_citations` / + `list_internal_citations` / `list_incoming_citations` (`citations.py:40,81,112`). + ON CONFLICT DO NOTHING → idempotent (`citations.py:54`). +5. **locator פתיר בכל תוצאת-אחזור.** כל span מוחזר נושא מזהה-מקור + locator + ([03-retrieval INV-RET5](03-retrieval.md#inv-ret5-כל-span-מוחזר-עקיב-למקורו), `search.py:77-86,322-343`); + הלכות נושאות `supporting_quote` (`db.py:652`) + `page_number` (`db.py:631,711,729`). +6. **telemetry של חיפושים.** `telemetry.log_search` → `search_logs` + (`telemetry.py:105`, `search.py:62,118,190,271`) רושם query/practice_area/top_case_law_ids — + תצפית על מה נשלף, fire-and-forget (`telemetry.py:8-12,100-101`). +7. **לקחים ופידבק מיוחסים.** `decision_lessons.source` (`db.py:208`: manual/curator/chair/ + style_analyzer) ו-`chair_feedback.lesson_extracted`/`applied_to` (`db.py:458-459`) מתעדים את + **מקור** הלקח ([07-learning.md](07-learning.md)). +8. **טבלת `audit_log` (פעולה כללית).** `log_action(action, case_id, document_id, details, actor)` + (`audit.py:18-44`) → `audit_log` (`db.py:159-167`, אינדקסים `:168-170`). קיימת, אך נכתבת + כיום כמעט-ורק ב-`case_subtype_override` (`cases.py:203`) — ראה [§5](#5-current-vs-target--ממצאי-audit). + +--- + +## 3. Invariants של התחום + +### INV-AUD1: כל תוצר מסיוע-AI מתעד את ה-provenance שלו (→G9) +**כלל:** כל תוצר שנוצר בסיוע-AI — בלוק-טיוטה, תוצאת-אחזור, הצעת-curator — **רושם את מקורו**: +**איזה מודל** הפיק אותו, **באיזה סוג-גנרציה**, ו**אילו קטעי-מקור** (chunks/precedents/מסמכי-תיק) +הוזנו אליו. הרשומה ניתנת-לביקורת בדיעבד (מי/מתי/ממה). +**מקורות:** Council of Europe / CEPEJ — *European Ethical Charter on AI in judicial systems* +(2018, transparency/traceability + user-control) · NCSC/JTC — *Principles & Practices for AI Use +in Courts* (auditable AI output) · ISO 15489-1:2016 (records authenticity — metadata about +creation) | סטטוס: verified +**אכיפה:** `decision_blocks.model_used/generation_type/temperature` בכל upsert של בלוק +(`block_writer.py:1017-1034`); telemetry על כל חיפוש (`telemetry.py:105`); **יעד נוסף:** קישור +מפורש בלוק→קטעי-מקור (provenance edges) + כתיבת `audit_log.log_action` לכל גנרציה. אוכף את +[G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai). +**הפרה ידועה (GAP):** ה-provenance קיים **חלקית** — `model_used` נרשם לכל בלוק, וה-commit ב-git +מקבע פלטים, אך **אין רשומה מאוחדת** שמקשרת בלוק-החלטה לקטעי-הקורפוס/הגנרציות שהזינו אותו, וטבלת +`audit_log` כמעט-ולא נכתבת לפעולות-AI (רק `case_subtype_override`, `cases.py:203`) → יעד +([§5](#5-current-vs-target--ממצאי-audit)). + +### INV-AUD2: רשומה שמורה שלמה ובלתי-משתנה אלא דרך שינוי עקיב ומיוחס (→G9, שלמות-רשומה) +**כלל:** החלטה/רשומה שמורה היא **שלמה ובלתי-משתנה** — כל שינוי בה נעשה רק דרך **מנגנון עקיב +ומיוחס** (commit git עם הודעה + actor, או Track Changes מיוחסות), ולא דרך דריסה שקטה. ניתן +לשחזר את מצב-הרשומה בכל נקודת-זמן ולזהות מי שינה מה ומתי. +**מקורות:** ISO 15489-1:2016 (§5.2.2 — integrity: records protected against unauthorized +alteration; אמינות/שלמות-רשומה) · Council of Europe / CEPEJ (2018, traceability) · DAMA-UK — +*Six Primary Dimensions for Data Quality* (2013, consistency/integrity over time) | סטטוס: verified +**אכיפה:** קיבוע git לכל פלט (`git_sync.commit_and_push` — `drafting.py:408,536,582,695`; +`cases.py:387`; `documents.py:86`) עם הודעה תיאורית; Track Changes ב-revisions עוקבות +([06-export §3](06-export.md#3-רישום-הגרסה--active_draft_path--git)); `decision_blocks` עם מפתח +קנוני `UNIQUE(decision_id, block_id)` (`db.py:333`) ו-`updated_at`. אוכף את +[G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai). +**הפרה ידועה:** עריכת-DOCX (`revise_draft`/`apply_user_edit`) הופכת את `active_draft_path` למקור- +בפועל **בלי לעדכן את בלוקי-ה-DB חזרה** — הנתון-הנגזר זוחל למקור-אמת ושלמות ה-DB מול המסמך-החי +נחלשת ([06-export INV-EX1](06-export.md#inv-ex1-ייצוא-דטרמיניסטי-ומשוחזר-מהבלוקים--docx-הוא-נתון-נגזר-g2)) → ממצא ל-[audit](../audit-report.md). + +### INV-AUD3: כל סמכות מצוטטת פתירה חזרה לקורפוס (→G5) +**כלל:** כל סמכות-משפטית המצוטטת בהחלטה (פסק-דין, הלכה, מסמך-תיק) **פתירה לרשומת-מקור בקורפוס** +דרך locator יציב — `case_law_id`/`document_id` + מזהה-עמוד/chunk/quote. ציטוט שאינו פתיר אינו +תקין; הוא נחסם או מסומן לאימות-יו"ר. זהו צד-ה-attribution של [INV-RET5](03-retrieval.md#inv-ret5-כל-span-מוחזר-עקיב-למקורו). +**מקורות:** Pinecone — *Implement multitenancy* (metadata-locator לכל פריט מואנדקס) · RAG +attribution (Lewis et al., 2020, NeurIPS — pinned/non-leaking provenance) · ISO 8000 (Data +quality — completeness/identifiability) | סטטוס: verified +**אכיפה:** `decision_paragraphs.citations` `[{case_law_id,text,type}]` (`db.py:343`); גרף +`precedent_internal_citations` (`db.py:937-947`) פותר ציטוט ל-`case_law` קיים או שומר stub; +פורמטרי-האחזור מצרפים מזהה+locator (`search.py:77-86,322-343`). אוכף את +[G5](00-constitution.md#inv-g5-metadata-מלא--הפרדת-קורפוס-נאכפת-בכל-query). +**הפרה ידועה (GAP):** הקישור קיים ברמת-הסעיף (`decision_paragraphs.citations`), אך **אין אכיפה** +שכל ציטוט בטקסט-הבלוק אכן מקושר לרשומת-קורפוס; ציטוט שהמודל ייצר בלי locator יכול לעבור בלי +חסימה אוטומטית — אימות נשען על שער-היו"ר ([05-qa-review](05-qa-review.md)) → יעד. + +--- + +## 4. רשומת-ה-provenance המאוחדת (TARGET) + +היעד שמאחד את שלוש השכבות: לכל **בלוק-החלטה** נשמר, מעבר ל-`model_used` הקיים, **קישור לקטעי- +המקור** שהוזנו לגנרציה (chunk-ids/`case_law_id`s שהוחזרו מהאחזור והוצגו ל-writer) — כך שניתן לענות +"מאיזו פסיקה/מסמך נולד המשפט הזה?". המנגנון הקנוני המוצע: כתיבת `audit_log.log_action` +(`audit.py:18`) בכל גנרציה (`action="write_block"`, `details={model, generation_type, source_chunk_ids, +retrieved_case_law_ids}`) — הטבלה כבר תומכת ב-`details JSONB` + `actor` + `case_id`/`document_id` +(`db.py:159-167`). זה ממיר את ה-audit_log מ"כמעט-ריק" ל-audit-trail מקצה-לקצה, בלי טבלה חדשה +(תואם כלל-ההנדסה "סימטריה" — הרחבת מסלול קיים, [חוקה §6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)). + +--- + +## 5. Current vs Target — ממצאי-audit + +ההבדלים בין הקוד בפועל ל-TARGET. **אלו תסמינים, לא התנהגויות תקינות.** כל פריט אומת מול הקוד. + +- **`audit_log` קיימת אך כמעט-ולא נכתבת (INV-AUD1).** `log_action` (`audit.py:18-44`) ו-טבלת + `audit_log` (`db.py:159-167`) מוכנות, אך הקריאה היחידה בפועל היא `case_subtype_override` + (`cases.py:203`) — אין רישום ל-`upload`/`extract_claims`/`write_block`/`export` (למרות ש-docstring + של `log_action` מונה אותם, `audit.py:28`). **תסמין:** אין audit-trail אחיד "מי עשה מה מתי" לרוב + פעולות-ה-AI. **יעד:** קריאת `log_action` בכל פעולה משנה-מצב, כולל גנרציות. +- **אין קישור בלוק→קטעי-מקור (INV-AUD1).** `decision_blocks` מתעד `model_used`/`generation_type` + (`db.py:326-327`) אך **לא** את ה-chunks/precedents שהוזנו לגנרציה. **תסמין:** אי-אפשר לשחזר מאיזו + פסיקה/מסמך נגזר בלוק ספציפי. **יעד:** רשומת-provenance מאוחדת ([§4](#4-רשומת-ה-provenance-המאוחדת-target)). +- **ציטוט→קורפוס לא נאכף אוטומטית (INV-AUD3).** `decision_paragraphs.citations` (`db.py:343`) + תומך בקישור, אך אין בדיקה שכל ציטוט בטקסט אכן פתיר ל-`case_law`. **תסמין:** ציטוט שהמודל ייצר בלי + locator יכול לעבור. **יעד:** ולידציה שכל citation בעלת `case_law_id` פתיר, אחרת flag לאימות-יו"ר. +- **שלמות ה-DB מול ה-DOCX-החי נחלשת אחרי עריכה (INV-AUD2).** אחרי `revise_draft`/`apply_user_edit`, + `active_draft_path` הופך מקור-בפועל בלי re-sync לבלוקים (`db.py:185-188`; + [06-export INV-EX1](06-export.md#inv-ex1-ייצוא-דטרמיניסטי-ומשוחזר-מהבלוקים--docx-הוא-נתון-נגזר-g2)). + **יעד:** re-sync מהבלוקים או חוזה מפורש + health-check לגילוי drift. +- **telemetry בולעת שגיאות בשתיקה (תיעוד, לא הערכה).** `log_search` swallow מכוון + (`telemetry.py:100-101`) כדי שלא להפיל חיפוש — תקין כ-fire-and-forget, אך אינו audit-trail + מהימן (רשומה עלולה ללכת לאיבוד בשקט). תואם את העיקרון "אין בליעה שקטה" רק כי זו telemetry-תצפית, + לא רשומת-שלמות; ה-audit-trail המהימן הוא git ([§2.1](#2-רכיבי-העקיבוּת-הקיימים-מאומת-fileline)). + +--- + +## 6. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — [INV-G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai) + (עקיבוּת + audit-trail) · [INV-G5](00-constitution.md#inv-g5-metadata-מלא--הפרדת-קורפוס-נאכפת-בכל-query) (attribution). +- [03-retrieval.md](03-retrieval.md#inv-ret5-כל-span-מוחזר-עקיב-למקורו) — INV-RET5 (locator פתיר בכל span — בסיס ל-INV-AUD3). +- [06-export.md](06-export.md#inv-ex2-עקיבוּת-מקור-נשמרת-בהחלטה-המיוצאת-g9) — INV-EX2 (עקיבוּת בפלט) + commit git (INV-AUD2). +- [05-qa-review.md](05-qa-review.md) — שער-היו"ר שמאמת ציטוטים (משלים את INV-AUD3). +- [02-data-model.md](02-data-model.md) — `decision_blocks`/`decision_paragraphs`/`case_law` (הישויות שעליהן נשמרת ה-provenance). +- [07-learning.md](07-learning.md) — `decision_lessons.source` + `chair_feedback` (מקור הלקחים). +- [01-ingest.md](01-ingest.md) — קליטה שמייצרת את הקטעים שאליהם פותרים ציטוטים.