Files
legal-ai/docs/superpowers/specs/2026-05-30-fu7-audit-provenance-design.md
Chaim 99cd6bc4dd docs(spec): FU-7 audit-trail + provenance design (GAP-17/18/19/20)
Reuse audit_log.log_action with details JSONB (X5 §4, no new table) for
end-to-end audit + block→source provenance. GAP-17 drift = blocks_stale flag
+ health-check (not fragile DOCX→blocks reparse). GAP-20 = structural
case_law_id resolution (not Hebrew citation NLP). Verified vs 3+ sources
(append-only lineage event; GitOps drift detect-don't-auto-remediate).
Pure-code, no migration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 21:15:50 +00:00

9.9 KiB
Raw Blame History

FU-7 — Audit-Trail + Provenance — עיצוב

סטטוס: מאושר-לעיצוב · תאריך: 2026-05-30 · ענף: TBD מכסה: GAP-17, GAP-18, GAP-19, GAP-20 · מספק: INV-AUD1, INV-AUD2, INV-AUD3, INV-EX1, INV-G9 מקורות: X5-audit-provenance.md, 06-export.md, gap-audit.md משימה: TaskMaster #65 · תלוי ב: FU-1 (#59) · סוג: pure-code (schema-additive קל) מיגרציה: אין. כל השינויים forward-only; backfill קל אופציונלי (provenance של בלוקים קיימים לא נאכף רטרואקטיבית).


1. מטרה והיקף

X5 §4 קובע את המנגנון הקנוני: שימוש חוזר ב-audit_log.log_action עם details JSONB — לא טבלה חדשה (כלל-הנדסה "סימטריה"). FU-7 ממיר את audit_log מ"כמעט-ריק" ל-audit-trail מקצה-לקצה, מוסיף provenance בלוק→מקורות, אוכף ציטוט→קורפוס, ומגלה drift בין DOCX-החי לבלוקים.

GAP בעיה (מאומת בקוד) יעד FU-7
GAP-18 log_action נכתב רק ב-case_subtype_override (cases.py:203) קריאות log_action ב-4 פעולות משנות-מצב: upload, extract_claims, write_block, export
GAP-19 decision_blocks נושא model_used בלבד — אין קישור לקטעי-מקור רשומת provenance ב-audit_log.details עם source ids שהזינו את הגנרציה
GAP-20 אין אכיפה שציטוט פתיר לקורפוס ולידציה דטרמיניסטית של case_law_id בציטוטים → flag לבלתי-פתירים
GAP-17 active_draft_path הופך SoT אחרי revise/apply בלי re-sync לבלוקים דגל blocks_stale דטרמיניסטי + חשיפת drift ב-health-check (לא re-sync שביר)

2. הכרעות אדריכליות (מאומתות ≥3 מקורות)

החלטה נימוק מקורות
provenance כ-event ב-audit_log append-only (details payload), לא עמודה/טבלה חדשה דפוס lineage בוגר: entity-key + event-type + actor + source-ids; X5 §4 (סימטריה) Snowflake data-lineage; OvalEdge provenance; DesignGurus append-only audit
GAP-17 = detect + flag, מקור-אמת=בלוקים, לא auto-resync auto-remediation דורש rollback אמין; reparse DOCX→blocks שביר (edits שוברים מבנה) Flux GitOps drift; Terraform drift (env0); Spacelift
GAP-20 = ולידציה מבנית של case_law_id פתיר, לא NLP של ציטוט חופשי NLP-ציטוט עברי חופף ל-extract_internal_citations הקיים; INV-AUD3 מנוסח סביב פתירוּת case_law_id X5 INV-AUD3; RAG attribution (Lewis 2020); ISO 8000
audit כ-non-fatal (כשל-audit מתעד warning, לא מפיל פעולה) git הוא שכבת-השלמות (X5 §2.1); audit_log הוא observability "מי/מה/מתי" X5 §2.1; דפוס audit fire-safe

3. הקבצים

  • Modify tools/audit.py — אין שינוי לחתימת log_action; להוסיף helper log_action_safe(...) שעוטף ב-try/except (warning, non-fatal) כדי שכשל-audit לא יפיל את הפעולה.
  • Modify tools/documents.pydocument_upload (:14) + extract_claims (:300): קריאת log_action_safe.
  • Modify services/block_writer.pywrite_block/store_block (~:1010): לאסוף source ids מ-context builders + לכתוב audit write_block עם provenance.
  • Modify tools/drafting.pyexport_docx (:384): audit export_docx; revise_draft (:647) + apply_user_edit (~:569): סימון blocks_stale=true.
  • Modify services/db.py — מיגרציה V22: עמודת cases.blocks_stale boolean DEFAULT false; helper mark_blocks_stale(case_id, val); helper resolve_citation_case_law_ids(ids) (בדיקת קיום); helper audit_provenance_query (קריאה — לא חובה).
  • Modify services/qa_validator.py (או היכן שרץ QA) — בדיקת ציטוט→קורפוס: לכל case_law_id בציטוטי-הבלוק, אם לא פתיר → ממצא-QA (warning) + audit citation_unresolved.
  • Modify health-check (metrics.py / processing_status) — חשיפת cases_with_stale_blocks count.
  • Test tests/test_audit_provenance.py (חדש) — offline, monkeypatched.

גבול: אין שינוי לחתימות ציבוריות; אין מיגרציית-נתונים. provenance של בלוקים קיימים לא נאכף רטרואקטיבית (forward-only) — תואם FU-1/FU-2a.

4. GAP-18 — audit על כל פעולה משנה-מצב

log_action_safe(action, case_id=, document_id=, details=, user=) — עטיפת log_action ב-try/except (כשל → logger.warning, ה-action ממשיך). נקודות-הקריאה:

פעולה action details
document_upload "document_upload" {title, doc_type, classification}
extract_claims "extract_claims" {docs_processed, claims_count}
write_block (GAP-19) "write_block" {decision_id, block_id, model_used, generation_type, source_document_ids, retrieved_case_law_ids, claim_ids}
export_docx "export_docx" {path, file_size, block_count}

5. GAP-19 — provenance בלוק→מקורות

write_block כבר אוסף הקשר מ-_build_source_context (document chunks), _build_precedents_context (para_results/caselaw_rowscase_law_ids), _build_claims_context (claim ids). היעד: לאסוף את המזהים הללו ל-dict sources = {document_ids, case_law_ids, claim_ids} ולכלול אותו ברשומת ה-audit write_block (§4). כך audit_log עונה "מאיזו פסיקה/מסמך נולד הבלוק" — בלי עמודה/טבלה חדשה. מפתח-הקישור: details.decision_id+details.block_id (audit_log עצמו keyed ב-case_id/document_id).

6. GAP-20 — ציטוט→קורפוס נאכף

resolve_citation_case_law_ids(ids) -> {resolved: [...], unresolved: [...]} — בדיקת EXISTS מול case_law. בנקודת ה-QA (לפני export, משתלב עם שערי FU-6): לאסוף את כל ה-case_law_id מציטוטי-הבלוקים (decision_paragraphs.citations אם מאוכלס, אחרת מ-provenance של §5), ולהריץ resolve. בלתי-פתירים → ממצא-QA (warning, לא חוסם-קריטי) + audit citation_unresolved. אכיפה מבנית בלבד (case_law_id), לא חילוץ-NLP של ציטוט חופשי.

הערה: decision_paragraphs אינו מאוכלס כיום ע"י אף כלי (ממצא Explore). לכן ולידציית-הציטוט פועלת על ה-case_law_ids שנרשמו ב-provenance (§5); אם/כאשר decision_paragraphs יאוכלס — אותה ולידציה חלה עליו. זה שומר את ה-GAP סגור בלי לבנות צינור-ציטוטים חדש (מחוץ-להיקף).

7. GAP-17 — drift בין DOCX-חי לבלוקים

מקור-אמת = decision_blocks (INV-EX1). אחרי revise_draft/apply_user_edit שהופכים את active_draft_path ל-SoT-בפועל בלי re-sync, מסמנים cases.blocks_stale=true (חוזה מפורש: "הבלוקים ידועים כלא-מסונכרנים מול ה-DOCX-החי"). export_docx מ-blocks מאפס blocks_stale=false (הבלוקים שוב SoT). health-check חושף cases_with_stale_blocks. לא מבצעים reparse DOCX→blocks (שביר).

נקודה פעולה על blocks_stale
revise_draft / apply_user_edit = true (DOCX-חי חרג מהבלוקים)
export_docx (מ-blocks) = false (בלוקים = SoT שוב)
write_block / save_block_content = false (בלוק עודכן ב-DB)

8. שינויי-התנהגות וסיכון

שינוי השפעה סיכון
audit על 4 פעולות audit_log מתמלא; observability נמוך — non-fatal, לא משנה תוצאת-פעולה
provenance ב-write_block audit רשומת מקור לכל גנרציה חדשה נמוך — forward-only; בלוקים קיימים לא מושפעים
ציטוט-QA warning ציטוט בלתי-פתיר מסומן לאימות-יו נמוך — warning, לא חוסם export (לא קריטי)
blocks_stale flag חשיפת drift; אינו חוסם נמוך — דגל אינפורמטיבי; V22 additive

9. אסטרטגיית בדיקה

tests/test_audit_provenance.py — offline, monkeypatch DB pool. מקרים:

  1. log_action_safe בולע כשל-DB (warning) ולא מרים.
  2. כל אחת מ-4 הפעולות קוראת ל-audit עם ה-action הנכון (monkeypatch log_action, assert call).
  3. write_block audit כולל source_document_ids/retrieved_case_law_ids מה-context.
  4. resolve_citation_case_law_ids: מפריד resolved/unresolved נכון (monkeypatch EXISTS).
  5. ציטוט בלתי-פתיר → ממצא-QA warning (לא חוסם-קריטי).
  6. blocks_stale: revise/apply → true; export-from-blocks → false.
  7. health-check חושף cases_with_stale_blocks.

בדיקות-DB אמיתיות (audit_log INSERT, V22, EXISTS) — smoke מול DB מקומי (5433) בסיום, כמו FU-2a.

10. סדר-ביצוע

  1. בדיקות אדומות.
  2. log_action_safe + מיגרציה V22 (blocks_stale) + helpers (mark_blocks_stale, resolve_citation_case_law_ids).
  3. GAP-18: 4 קריאות audit (upload, extract_claims, export_docx + write_block בסיס).
  4. GAP-19: איסוף source ids ב-write_block → provenance ב-audit.
  5. GAP-20: ולידציית-ציטוט ב-QA + audit citation_unresolved.
  6. GAP-17: blocks_stale ב-revise/apply/export/write_block + health-check.
  7. בדיקות ירוקות + smoke מול DB + lint + TaskMaster.