Files
legal-ai/docs/spec/06-export.md
2026-05-30 15:16:00 +00:00

14 KiB
Raw Blame History

06 — ייצוא DOCX (Export Contract)

קובץ-תחום זה כפוף ל-חוקת המערכת ומגדיר את חוזה-הייצוא של עוזר משפטי: הרינדור של החלטה ל-DOCX מעוצב (גופן David, RTL, סגנונות-טמפלט). העיקרון המכונן — ה-DB הוא מקור-האמת היחיד, וה-DOCX הוא נתון נגזר (derived) הניתן לשחזור. הקובץ אוכף את INV-G2 (מקור-אמת יחיד / נתון-נגזר משוחזר) ואת INV-G9 (עקיבוּת-מקור), והוא השלב שאחרי שער-הייצוא הקריטי של 05-qa-review.md / INV-QA3.

כללי-סגנון — סמכות אחת. מכניקת העיצוב (line classification, dash policy, placeholder, מיפוי-סגנונות, RTL-runs) מתועדת במלואה בסקיל dafna-decision-template/SKILL.mdהוא המקור הסמכותי. הקובץ הזה מסכם ומפנה, לא משכפל. כללי-הסגנון עצמם הם תוכן-משפטי-דומייני (סמכות היו"ר + הסקיל), בעוד שחוזה-ה-derived-data (INV-EX1) ועקיבוּת-המקור (INV-EX2) הם invariants הנדסיים הנושאים מקורות + סטטוס.


1. חוזה-הייצוא — DB הוא המקור, DOCX הוא הנגזר

החלטה מאוחסנת כ-בלוקים מובְנים ב-DBdecision_blocks (12 בלוקים, מפתח קנוני UNIQUE(decision_id, block_id)) תחת decisions (UNIQUE(case_id, version)); ראה 02-data-model.md §1. ה-DOCX נגזר מהבלוקים האלה ואינו מקור-אמת עצמאי: מחיקתו אינה מאבדת תוכן, וייצוא חוזר מאותם בלוקים מפיק מסמך שקול.

מסלול-הייצוא הקנוני (הסופי):

  1. export_docx(case_number) (tools/drafting.py:384, נחשף server.py:557) שולף את התיק, ואז קורא ל-docx_exporter.export_decision(case_id, …, mode="final") (services/docx_exporter.py:306).
  2. export_decision שולף את הבלוקים ישירות מ-decision_blocks (SELECT block_id, block_index, title, content, word_count … ORDER BY block_index, docx_exporter.py:336-342) — אין מקור-תוכן אחר.
  3. טוען את טמפלט-דפנה (skills/docx/decision_template.docx, docx_exporter.py:27-29,364), מנקה את גוף-המסמך (_clear_body), וכותב כל בלוק עם bookmark עוטף (אנקור ל-revisions עתידיים, _wrap_block_with_bookmarks, docx_exporter.py:367-382).
  4. שומר לקובץ מגורסן data/cases/{case_number}/exports/טיוטה-v{N}.docx (גרסה אוטומטית עולה, docx_exporter.py:384-400).

שני מסלולי-ייצוא לפי מקור-התוכן (לא מסלולים-מקבילים מתפצלים):

  • docx_exporter.pyההחלטה הסופית מ-12 הבלוקים ב-decision_blocks (mode="final"), וגם טיוטת-ביניים (mode="interim" — תת-קבוצת בלוקים בסדר חדש: רקע→תכניות→טענות→הליכים, export_interim_draft, drafting.py:511). שני המצבים שולפים מאותה טבלה — וריאציית-תצוגה של אותו מקור-אמת, לא מסלול שני.
  • analysis_docx_exporter.py (build_analysis_docx, :401) — מייצא את מסמך הניתוח המשפטי (analysis-and-research.md) שכתב legal-analyst, לא את בלוקי-ההחלטה. זהו תוצר-עזר שונה (שלב ניתוח, לא החלטה) — והוא המסלול שהסקיל מתעד בעיקר. שניהם חולקים את אותו טמפלט ואותם כללי-סגנון, כנדרש מ-INV-G2 (סימטריה — לא שתי שכבות-סגנון מתפצלות).

2. כללי-הסגנון — סיכום (הסמכות: הסקיל)

ה-service מחיל את סגנונות-הטמפלט בלבד (paragraph.style = "Heading 2") — בלי font/size/indent ידני; העיצוב (David, RTL, גדלים) מגיע מ-styles.xml. הפירוט המלא + ה-XML של כל סגנון: SKILL.md + references/.

  • סיווג-שורות (_classify_line): כל שורה מסווגת לאחת מ-6 קטגוריות — label_heading, inline_label, numbered, bullet, heb_letter, plain — שקובעות את הסגנון המוחל (Heading 2 / Normal / List Paragraph). ראה references/line-classification.md.
  • מדיניות-מקפים (_no_dash): דפנה ביקשה "בלי מקפים בכלל" — (U+2014) ו- (U+2013) מוסרים מכל טקסט נכתב; מקף רגיל (-) נשמר.
  • שדות-placeholder: chair_position עם סימן-ריק ([ימולא ע"י יו"ר הוועדה] וכד') מוחלף ב-[טרם מולאה עמדת ועדת הוועדה] ב-italic — סימן ויזואלי שנותר להשלים (תואם INV-G10 — היו"ר משלימה, לא המערכת).
  • RTL-runs: כל run מסומן <w:rtl/> (_mark_run_rtl) — אחרת Word נופל ל-Times New Roman במקום David. ראה references/rtl-runs.md.
  • מספור: מספור אוטומטי רק ב-List Paragraph (decimal); שורות (א)(ב) מקבלות List Paragraph עם _strip_numpr() (המספור העברי בטקסט).

3. רישום הגרסה — active_draft_path + git

לאחר כתיבת ה-DOCX, export_docx (drafting.py:404-408):

  1. set_active_draft_path(case_id, path) (db.py:1177) — רושם את ה-DOCX שיוצא כ- active-draft הנוכחי (cases.active_draft_path, db.py:189). שדה זה הוא האנקור לעריכות עוקבות (revise_draft/apply_user_edit/list_bookmarks), לא מקור-אמת-תוכן מתחרה ל-DB.
  2. git_sync.commit_and_push(case_dir, "ייצוא DOCX: …") (drafting.py:408) — מקבע את הקובץ ב-git של תיקיית-התיק (audit-trail של פלט, INV-G9; ראה X5-audit-provenance.md).

אותו דפוס (set_active_draft_path + commit) חוזר ב-export_interim_draft (drafting.py:533,536), revise_draft (drafting.py:692,695) ו-apply_user_edit (drafting.py:579,582).


4. Invariants של התחום

INV-EX1: ייצוא דטרמיניסטי ומשוחזר מהבלוקים — DOCX הוא נתון-נגזר (→G2)

כלל: הייצוא דטרמיניסטי וניתן-לשחזור מבלוקי-ההחלטה המאוחסנים ב-decision_blocks: אותם בלוקים + אותו טמפלט מפיקים מסמך שקול. ה-DOCX הוא נתון-נגזר (derived)לעולם לא מקור-אמת עצמאי. אסור מסלול-תוכן שני שכותב DOCX ממקור שאינו ה-DB; וריאציות (final/interim) הן תצוגות של אותו מקור. מקורות: Martin Kleppmann — Designing Data-Intensive Applications (O'Reilly, 2017, system-of-record מול derived data, ושחזור derived מהמקור) · Martin Fowler (Canonical Data Model / Single Source of Truth) · SSOT (Single Source of Truth principle) | סטטוס: verified אכיפה: export_decision שולף אך-ורק מ-decision_blocks (docx_exporter.py:336-342); פלט מגורסן + idempotent מבחינת-תוכן; אוכף את INV-G2 וכלל-ההנדסה "סימטריה" (חוקה §6). הפרה ידועה: אחרי revise_draft/apply_user_edit, ה-DOCX המסומן active_draft_path הופך ל"מקור-האמת" לעריכות-Track-Changes העוקבות (db.py:185-188), ובלוקי-ה-DB אינם מתעדכנים חזרה — הנתון-הנגזר זוחל למקור-אמת בפועל בלי סנכרון לאחור. יעד: או re-sync מהבלוקים, או חוזה מפורש ש-active_draft_path הוא רק אנקור-revision ולא מקור-תוכן → ראה §5.

INV-EX2: עקיבוּת-מקור נשמרת בהחלטה המיוצאת (→G9)

כלל: ההחלטה המיוצאת שומרת על עקיבוּת-מקור היכן שנדרש — סמכויות-משפטיות מצוטטות ניתנות-לאיתור (citation resolvable), והפלט מקובע ב-audit-trail (commit git). הפניות-פסיקה בבלוקים אינן מאבדות את מקורן בעת הרינדור. מקורות: Council of Europe / CEPEJ — European Ethical Charter on AI in judicial systems (2018, traceability/transparency) · ISO 15489-1:2016 (records authenticity/integrity) · Lewis et al. (2020, NeurIPS — RAG attribution) | סטטוס: verified אכיפה: export_docx מקבע כל פלט ב-git (git_sync.commit_and_push, drafting.py:408) + רושם active_draft_path (db.py:1177); עקיבוּת-המקור של הציטוטים עצמם נאכפת במעלה-הזרם (חילוץ-טענות/הלכות + provenance, 04-analysis-writing.md, X5-audit-provenance.md). אוכף את INV-G9. הפרה ידועה:

INV-EX3: אין ייצוא בכשל-QA קריטי (restate של INV-QA3 →G10)

כלל: הייצוא חסום כל עוד שער-QA קריטי נכשל (claims_coverage / structural_integrity); export_blocked חייב להיבדק לפני ייצוא. זהו אותו invariant של INV-QA3, בצד-הייצוא. מקורות: NCSC/JTC — Principles & Practices for AI Use in Courts (controlled, auditable 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); נאכף ברמת- הזרימה/agent בלבד — הסוכן legal-exporter מחויב להריץ validate_decision ולבדוק כשלים-קריטיים לפני ייצוא (.claude/agents/legal-exporter.md:71,149). הפרה ידועה: export_docx (drafting.py:384) אינו קורא ל-validate_decision בעצמו — הוא ניגש ישירות ל-docx_exporter.export_decision בלי לבדוק export_blocked. החסימה תלויה במשמעת-הסוכן ואינה hard-block בקוד-הייצוא → ראה §5 (תואם 05-qa-review §4).


5. Current vs Target

  • שער-ייצוא אכוף-זרימה ולא אכוף-קוד (INV-EX3 / INV-QA3). אומת בקוד: export_docx (drafting.py:384-419) קורא ישירות ל-docx_exporter.export_decision (:403) ללא קריאה ל-qa_validator.validate_decision ובלי בדיקת export_blocked. החסימה מתקיימת רק כי הסוכן legal-exporter מחויב להריץ QA קודם (legal-exporter.md:71,149) — אדם/סוכן שיקרא ל-export_docx ישירות יעקוף את השער. יעד: hard-block בתוך export_docx — שליפת qa_results/export_blocked ודחייה לפני כתיבת ה-DOCX, כך שאי-אפשר לעקוף.
  • active_draft_path כ-derived-שזוחל-למקור (INV-EX1). ה-DOCX נגזר מהבלוקים בייצוא הראשון, אך אחרי עריכה (revise_draft/apply_user_edit) ה-DOCX הופך ל"מקור-האמת" לעריכות הבאות (db.py:185-188) בלי לעדכן את decision_blocks חזרה — סטייה אפשרית בין הבלוקים למסמך-החי. יעד: חוזה מפורש — או re-sync מהבלוקים, או הגדרת active_draft_path כאנקור-revision בלבד (לא מקור-תוכן), עם בדיקת-בריאות לגילוי drift בין הבלוקים ל-DOCX הפעיל.

6. הפניות-אחיות