From a5b22dadf31348429af6a2f243b3b6775f1f7f9f Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:05:06 +0000 Subject: [PATCH 01/30] docs(spec): master design for system spec + integrity layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Establishes the foundation to fix a recurring root-cause failure class (non-canonical identifiers, asymmetric ingest paths, silent manual gates): - Confirmed system mission (quasi-judicial decision assistant; human decides) - Decomposition into 5 sub-projects (spec → audit → integrity layer → re-check → process agents) - spec-set structure under docs/spec/ (lifecycle-organized + cross-cutting files) - 11 global invariants + engineering rules, each backed by ≥3 authoritative sources (NCSC/JTC, FJC, CEPEJ, South Bucks; RAG/Lewis, Manning IR, Elastic/Pinecone/Weaviate; DAMA-DMBOK, ISO 8000, ISO 15489, Kleppmann, Codd, Fowler) - 3-source verification protocol; UNVERIFIED items escalated, not decided solo Co-Authored-By: Claude Opus 4.8 --- .../specs/2026-05-30-system-spec-design.md | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-30-system-spec-design.md diff --git a/docs/superpowers/specs/2026-05-30-system-spec-design.md b/docs/superpowers/specs/2026-05-30-system-spec-design.md new file mode 100644 index 0000000..901efa2 --- /dev/null +++ b/docs/superpowers/specs/2026-05-30-system-spec-design.md @@ -0,0 +1,168 @@ +# מסמך-עיצוב אב — ספ המערכת והשכבה החסרה (System Spec & Integrity Layer) + +**תאריך:** 2026-05-30 +**סטטוס:** עיצוב מאושר (Design approved) — ממתין לכתיבת קבצי הספ +**בעלים:** חיים מרכוס +**הקשר:** מהלך-יסוד להגדרת "מהו תקין" במערכת *עוזר משפטי*, ולסגירת כשל-שורש חוזר. + +--- + +## 1. הבעיה — כשל-השורש החוזר + +מה שנחווה כ"כל פעם משהו אחר לא מדויק" אינו אוסף תקלות אקראיות אלא **כשל אחד שחוזר בתחפושות**. ראיות שצפו (30.5.2026): + +| תסמין | שורש | +|--------|------| +| `8126-25` לא נמצא (האמיתי `8126-03-25`); קומיט "tolerant case_number lookup" | אין מפתח קנוני — מתקנים תסמין בקריאה | +| 3 החלטות "סופר" ב-3 פורמטים שונים (`8126/24`, ציטוט-מלא-כ-case_number) | אין חוזה-נתונים אחיד | +| ערן סופר 8046/24 עלתה בלי metadata (headnote/summary/tags ריקים) | מסלול ה-ingest הפנימי לא מתזמן חילוץ metadata — אסימטרי למסלול החיצוני | +| 10/19 הלכות מאושרות, התגלה במקרה | שער ידני שקוף בלי נראות backlog | +| משימות #56, #57 | אי-עקביות בין רכיבים (דליפה חוצת-קורפוסים, chunker) | + +**אבחנה:** המערכת גדלה בקצב *הוספת יכולות* מהר יותר מקצב *שמירת עקביות* — מסלולים/כלים/קורפוסים מקבילים שנוספים בבידוד ומתפצלים (drift), בלי שכבה שמגדירה ואוכפת "תקין". כל פגם מתגלה בדיעבד, אחד-אחד. + +**התרופה:** לא לתקן 10 דברים — להוסיף **שכבה אחת חסרה**: חוקה + חוזה-שלמות + בדיקת-בריאות אחת + איחוד מסלולי ה-ingest. זה הופך כשל מ"מתפרץ במקום אקראי" ל"נחסם בכניסה, גלוי בדשבורד". + +--- + +## 2. ייעוד המערכת (מאושר ע"י חיים) + +> מערכת AI שמסייעת ליו"ר ועדת הערר לתכנון ובנייה (מחוז ירושלים, עו"ד דפנה תמיר) לנסח **החלטות מעין-שיפוטיות כתובות ומנומקות** — מסמכים משפטיים פורמליים שעומדים לביקורת שיפוטית — תוך שמירה על **הקול, השיקול והאחריות של היו"ר**. + +- **משרת:** יו"ר הוועדה (משתמש-על) והסוכנים הפועלים בשמה. +- **מחזור-חיים:** ניהול תיקים → בסיס ידע (3 קורפוסים) → אחזור סמנטי (RAG) → סיוע-כתיבה (12 בלוקים, סגנון דפנה) → ייצוא DOCX. +- **3 סוגי עררים:** רישוי ובנייה (1xxx, חם), היטל השבחה (8xxx, קר), פיצויים ס'197 (9xxx, קר). +- **ה"למה" העמוק:** המערכת מסייעת — היו"ר מכריעה (שערים קריטיים ידניים בכוונה); מנוע צבירת-ידע (לומד מהחלטות סופיות ומפידבק); רב-חברתי (CMP/CMPA). + +--- + +## 3. עקרונות-עבודה למהלך + +1. **אסור להניח שהקיים תקין.** כל מה שמופה בקוד/בקורפוס = "טענה לבדיקה", לא "אמת". "תקין" נגזר ממקורות חיצוניים, לא מהמערכת שתחת חשד. +2. **פרוטוקול אימות 3-מקורות:** כל invariant/חוק בספ מגובה ב-**≥3 מקורות סמכותיים מוכרים** בעלי ידע מקצועי מוכח. כשאין 3 → מסומן `⚠ UNVERIFIED` ומועלה לחיים, לא מוכרע לבד. +3. **מנגנון:** מחקר עצמאי → טיוטה לביקורת. +4. **מודל-שיתוף:** על החלטות טכניות/אדריכליות אני חוקר ומכריע מקצועית ומציג תוצאה מוגמרת. שואל את חיים רק במקום שבו *הוא* הסמכות — כוונה, עדיפויות עסקיות, עובדות משפטיות-דומייניות. + +--- + +## 4. פירוק ל-5 תת-פרויקטים (לפי תלות) + +| # | תת-פרויקט | תוצר | תלות | +|---|-----------|------|------| +| 1 | **ספ המערכת + חוקה** | spec-set ב-`docs/spec/` המגדיר מודל קנוני + invariants | — | +| 2 | **מפת הפערים (Audit)** | סריקה אמפירית מול הספ → רשימת משימות | תת-פרויקט 1 | +| 3 | **שכבת שלמות-נתונים** | חוזה-שלמות באכיפת-קוד + בדיקת-בריאות אחת + **איחוד מסלולי ingest** | 1, 2 | +| 4 | **בדיקה חוזרת** | הרצת בריאות/audit אחרי התיקון | 3 | +| 5 | **סוכני-תהליך** | add-feature / fix-feature / spec-guardian — מכירים את הספ, "עושים שיעורי בית", לומדים ומתעדכנים | 1 (3) | + +כל תת-פרויקט יקבל מחזור spec→plan→implementation משלו. מסמך זה מפרט את **תת-פרויקט 1** במלואו ומקבע את ההחלטות העקרוניות לכולם. + +--- + +## 5. מבנה הספ-set (תת-פרויקט 1) + +מיקום: **`docs/spec/`** (ספ חי). ארגון קבצי-תחום: **לפי מחזור-חיים** (גישה A) — חושף ישירות אסימטריות-זרימה. + +``` +docs/spec/ +├── 00-constitution.md ← ייעוד · invariants גלובליים · כללי-הנדסה · אינדקס · תבנית-invariant · פרוטוקול-אימות +│ ── מחזור-החיים ── +├── 01-ingest.md ← קליטה מאוחדת: מסמכי-תיק / פסיקה חיצונית / החלטות-ועדה — חוזה מסלול-יחיד +├── 02-data-model.md ← אחסון: ישויות (cases, case_law, documents, chunks, halachot…) + חוזה-שלמות לכל ישות +├── 03-retrieval.md ← 3 קורפוסים + כלי-חיפוש · hybrid/RRF · attribution · eval harness · invariants +├── 04-analysis-writing.md ← חילוץ טענות · 12 בלוקים · סגנון דפנה (מצטט block-schema.md וכו') +├── 05-qa-review.md ← שערי QA + שערים אנושיים (אישור הלכה, בחירת תוצאה, פידבק) כ-invariant +├── 06-export.md ← ייצוא DOCX לפי תבנית דפנה +├── 07-learning.md ← Hermes · לקחים · לולאת פידבק היו"ר · צמיחת קורפוס (quality-at-source) +│ ── חוצי-שלבים ── +├── X1-identifiers.md ← מודל מזהים קנוני: נרמול case_number **בכתיבה** · cases מול case_law · פורמטי ציטוט +├── X2-multi-company.md ← CMP/CMPA · 14 סוכנים · כללי sync +├── X3-integration-deploy.md ← Paperclip (wakeup, ניתוב comments, webhooks) · Coolify/pm2 +├── X4-agents.md ← מפת הסוכנים (דומיין + סוכני-התהליך מתת-פרויקט 5) +└── X5-audit-provenance.md ← audit-trail לשימוש ב-AI · עקיבוּת כל מקור מצוטט · שלמות-רשומה (CEPEJ/NCSC/ISO 15489) +``` + +**עקרונות:** כל קובץ עצמאי, ממוקד, agent-readable, יעד ≤~500 שורות (תפיחה = סימן לפיצול). `00-constitution.md` = שער-כניסה יחיד. מסמכים קיימים (`architecture.md`, `product-specification.md`, `block-schema.md`…) לא נמחקים ולא משוכפלים — מצוטטים כ"מקור" ומאומתים מול הסמכויות; סתירה = ממצא ל-audit. + +### תבנית-invariant (מבנה אחיד לכל חוק בספ) +``` +### INV-<תחום><מספר>: <כותרת קצרה> +**כלל:** <ניסוח נורמטיבי חד — מה חייב להתקיים> +**מקורות:** <≥3 סמכויות> | סטטוס: verified / ⚠ UNVERIFIED +**אכיפה:** <היכן/איך נאכף — schema, ולידציית-כתיבה, בדיקת-בריאות, שער> +**הפרה ידועה:** <דוגמה מהמערכת, אם יש — מקשר ל-audit> +``` + +--- + +## 6. ה-Invariants הגלובליים (לב `00-constitution.md`) + +כל אחד מגובה ב-≥3 סמכויות (פירוט ב-§9). אלה החוקים שמייבשים את כשל-השורש: + +| # | Invariant | סמכויות | +|---|-----------|---------| +| **G1** | מזהה קנוני, **מנורמל בכתיבה** (לא תיקון-סלחני בקריאה בלבד) | SSOT/normalization · Codd 1NF · Kleppmann | +| **G2** | מקור-אמת יחיד; **אין מסלולי-קוד מקבילים שמתפצלים** — אחים חולקים מסלול קנוני אחד; derived data משוחזר | Kleppmann (system of record) · Fowler (canonical model) · SSOT | +| **G3** | ingest **אחיד ו-idempotent** (upsert על מפתח דטרמיניסטי) | Kleppmann · Stripe/CDC idempotency · ISO 8000 | +| **G4** | **חוזה-שלמות:** שדות חובה מולאו לפני שרשומה "שמישה/ניתנת-לחיפוש"; נבדק מול spec מפורש | ISO 8000 · DAMA (completeness) · ISO 15489 (reliability) | +| **G5** | metadata מלא לכל פריט מואנדקס + **הפרדת-קורפוס נאכפת בכל מסלול-query** | Pinecone (multitenancy) · RAG attribution · ISO 8000 | +| **G6** | **re-index בכל שינוי תוכן** (אין embeddings מיושנים) | Pinecone · Weaviate · RAG freshness | +| **G7** | מיזוג **לפי דירוג (RRF)**, לא סכום-ציונים גולמי בין retrievers | Elastic · Weaviate · OpenSearch/Azure (corrob.) | +| **G8** | איכות-אחזור **נמדדת (precision+recall)**, לא מונחת | Manning (IR textbook) · RAG eval literature | +| **G9** | כל פלט **עקיב למקורו** + audit-trail לשימוש ב-AI | CEPEJ (user control) · NCSC · ISO 15489 | +| **G10** | המערכת מסייעת; **שערים אנושיים** (אישור הלכה/תוצאה/פידבק) הם invariant, לא רשות | NCSC · CEPEJ · FJC | +| **G11** | **תוכן החלטה מנומקת:** רקע ניטרלי · ללא כפילות · מענה לטענות המפסיד · מבחן-השופט · טענות מקוריות | FJC (Writing Manual) · South Bucks (adequacy) · חוק 1958 (חובת הנמקה) | + +### כללי-הנדסה (constitution — מונעים הישנות) +- **סימטריה:** אסור להוסיף מסלול מקביל ליכולת קיימת — מרחיבים את המסלול הקנוני. (נגזר מ-G2) +- **נרמול לא תיקון-תסמין:** מתקנים נתון במקור (קנוני), לא מטליאים בקריאה. (נגזר מ-G1) +- **Quality-at-source:** שלמות נאכפת קרוב ככל האפשר לקליטה. (Fowler/Data-Mesh) +- **אין בליעה שקטה:** רשומה חסרה מסומנת ומדווחת, לא מתקבלת בשקט. (תואם feedback קיים) + +--- + +## 7. פרוטוקול-אימות ומודל-שיתוף (ייכנס ל-`00-constitution.md`) + +- כל invariant נושא `מקורות` + `סטטוס: verified / ⚠ UNVERIFIED`. +- `⚠ UNVERIFIED` (פחות מ-3 מקורות) → לא מוכרע לבד; מועלה לחיים. +- החלטות טכניות → מחקר עצמאי + הכרעה מקצועית + הצגת תוצאה. שאלה לחיים רק במקום שהוא הסמכות. + +--- + +## 8. פריטים פתוחים — אימות-מקור-ראשוני נדרש +(החוקר אימת מסגרת; הפריטים הישראליים דורשים אימות לפני ציטוט כ-סמכות, בשלב כתיבת `04`/`05`/`X5`) +1. מספר הסעיף המדויק בחוק לתיקון סדרי המינהל (החלטות והנמקות) תשי"ט-1958 (וכן תיקון תשכ"ט-1969). +2. ציטוט מדויק מ-ברק-ארז, *משפט מינהלי*. +3. אסמכתאות פסיקה: בג"ץ 143/56; עע"ם 2994/21 (מעמד ועדת ערר כגוף תכנוני-מקצועי). + +--- + +## 9. נספח מקורות סמכותיים (מאומתים במחקר 30.5.2026) + +**ממשל-AI שיפוטי + מבנה החלטה מנומקת** +- NCSC / JTC — *Court Technology Standards* + *Principles & Practices for AI Use in Courts*. https://www.ncsc.org/our-centers-projects/joint-technology-committee/court-technology-standards +- Federal Judicial Center — *Judicial Writing Manual* (2d ed.). https://www.fjc.gov/content/judicial-writing-manual-pocket-guide-judges-second-edition +- Council of Europe / CEPEJ — *European Ethical Charter on the use of AI in judicial systems* (2018). +- *South Buckinghamshire DC v Porter (No 2)* [2004] UKHL 33 (adequacy of reasons). https://publications.parliament.uk/pa/ld200304/ldjudgmt/jd040701/south-1.htm +- חוק לתיקון סדרי המינהל (החלטות והנמקות), תשי"ט-1958. https://www.nevo.co.il/law_html/law00/98603.htm +- Kevin D. Ashley — *Artificial Intelligence and Legal Analytics* (CUP). + +**אחזור / RAG / IR** +- Lewis et al. (2020) — *Retrieval-Augmented Generation* (NeurIPS). https://arxiv.org/abs/2005.11401 +- Manning, Raghavan & Schütze — *Introduction to Information Retrieval* (CUP, 2008). https://nlp.stanford.edu/IR-book/ +- Elastic — *Reciprocal Rank Fusion*. https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion +- Pinecone — *Implement multitenancy*. https://docs.pinecone.io/guides/index-data/implement-multitenancy +- Weaviate — *Hybrid Search Explained*. https://weaviate.io/blog/hybrid-search-explained + +**שלמות-נתונים / איכות / רשומות** +- DAMA-DMBOK2 + DAMA-UK — *Six Primary Dimensions for Data Quality* (2013). +- ISO 8000 — Data quality (8000-8/61/110). +- ISO 15489-1:2016 — Records management (authenticity/reliability/integrity/usability). +- Martin Kleppmann — *Designing Data-Intensive Applications* (O'Reilly, 2017). +- E.F. Codd — Relational model & normalization (CACM 13(6), 1970). +- Martin Fowler — Canonical Data Model / Data Mesh (quality-at-source). + +--- + +## 10. השלב הבא +לאחר ביקורת חיים על מסמך זה → invoke `writing-plans` לבניית תוכנית-יישום מפורטת לתת-פרויקט 1 (כתיבת קבצי הספ-set, החל מ-`00-constitution.md`). From 1d01135e3287d62a6e5383abf9f24e2e1ef853d4 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:08:31 +0000 Subject: [PATCH 02/30] docs(plan): implementation plan for system spec-set (sub-project 1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 13 tasks across 3 phases (keystone constitution → lifecycle files → cross-cutting), each verification-gated (≥3 sources or UNVERIFIED+escalate) with review checkpoints. Co-Authored-By: Claude Opus 4.8 --- .../plans/2026-05-30-system-spec-set.md | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-30-system-spec-set.md diff --git a/docs/superpowers/plans/2026-05-30-system-spec-set.md b/docs/superpowers/plans/2026-05-30-system-spec-set.md new file mode 100644 index 0000000..60709ec --- /dev/null +++ b/docs/superpowers/plans/2026-05-30-system-spec-set.md @@ -0,0 +1,254 @@ +# System Spec-Set (Sub-Project 1) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Author the living system spec-set under `docs/spec/` that canonically defines the *עוזר משפטי* system and its invariants ("what is correct"), each invariant backed by ≥3 authoritative sources. + +**Architecture:** A `00-constitution.md` keystone (mission, global invariants, engineering rules, invariant template, verification protocol, index) + lifecycle-organized domain files (`01-ingest` … `07-learning`) + cross-cutting files (`X1`…`X5`). Existing docs are cited as verified sources, never duplicated. This is documentation, not code: the "test" is the **verification gate** — every invariant carries ≥3 verified sources or is marked `⚠ UNVERIFIED` and escalated to the chair (never decided solo). + +**Tech Stack:** Markdown. Sources verified via WebSearch/WebFetch + primary texts (Nevo for Israeli statutes). Design basis: [docs/superpowers/specs/2026-05-30-system-spec-design.md](../specs/2026-05-30-system-spec-design.md). + +**Branch:** `system-spec` (already created; design doc committed at `a5b22da`). + +--- + +## Conventions for every file (apply in each task) + +- **Invariant template** (use verbatim structure): + ``` + ### INV-: + **כלל:** + **מקורות:** <≥3 authorities> | סטטוס: verified / ⚠ UNVERIFIED + **אכיפה:** + **הפרה ידועה:** + ``` +- **Language:** Hebrew prose, English for technical terms and source names (matches project docs + RTL preference). +- **Length target:** ≤ ~500 lines/file. If exceeding, that domain needs splitting — note it, don't cram. +- **Citing existing docs:** reference (e.g., `block-schema.md`) as a *source to verify*; if it contradicts the ≥3 authorities, record a one-line audit-finding rather than silently trusting it. +- **Cross-links:** link sibling spec files by relative path; link global invariants as `00-constitution.md#inv-g`. + +## Per-file verification gate (the "test") + +A file passes only when ALL hold (this checklist is a literal step in each task): +1. Every `INV-*` has either ≥3 named authoritative sources (`verified`) or is marked `⚠ UNVERIFIED` with an escalation note. +2. No placeholder text (`TBD`/`TODO`/"להשלים"). +3. All cross-links resolve to a real file/anchor. +4. Consistent with `00-constitution.md` (no invariant contradicts a global invariant). +5. ≤ ~500 lines. + +--- + +## Phase 0 — Scaffold + +### Task 0: Create the spec directory + +**Files:** +- Create: `docs/spec/README.md` + +- [ ] **Step 1: Create `docs/spec/` with a short README** + +Write `docs/spec/README.md`: +```markdown +# ספ המערכת — עוזר משפטי (Living System Spec) + +זהו מקור-האמת הקנוני ל"מהו תקין" במערכת. שער-הכניסה: [00-constitution.md](00-constitution.md). +כל invariant מגובה ב-≥3 מקורות סמכותיים; פריט לא-מאומת מסומן ⚠ UNVERIFIED ומועלה ליו"ר. + +מבנה: 00 חוקה · 01–07 מחזור-חיים · X1–X5 חוצי-שלבים. ראה אינדקס מלא בחוקה. +בסיס-עיצוב: docs/superpowers/specs/2026-05-30-system-spec-design.md +``` + +- [ ] **Step 2: Commit** + +```bash +git add docs/spec/README.md +git commit -m "docs(spec): scaffold docs/spec/ living spec-set" +``` + +--- + +## Phase 1 — Keystone (REVIEW CHECKPOINT after) + +### Task 1: `00-constitution.md` — the keystone + +**Files:** +- Create: `docs/spec/00-constitution.md` + +- [ ] **Step 1: Write the constitution** with these sections (content is already determined by the approved design): + + 1. **ייעוד** — paste the confirmed mission paragraph from the design doc §2. + 2. **עקרונות-עבודה** — the 4 work principles (design doc §3): don't assume existing is correct; 3-source protocol; research→draft; collaboration model. + 3. **תבנית-invariant** — the template from "Conventions" above. + 4. **פרוטוקול-אימות** — `verified` vs `⚠ UNVERIFIED`; escalation to chair; never decide solo. + 5. **Invariants גלובליים G1–G11** — each written with the full template. Content + sources from design doc §6 / §9: + + - **INV-G1 מזהה קנוני מנורמל בכתיבה** — SSOT/normalization · Codd 1NF (CACM 13(6), 1970) · Kleppmann DDIA. אכיפה: normalization-on-write in the ingest path + `X1-identifiers.md`. הפרה ידועה: tolerant `_normalize_case_number` on read only; `8126-25` vs `8126-03-25`. + - **INV-G2 מקור-אמת יחיד, אין מסלולים מקבילים מתפצלים** — Kleppmann (system of record) · Fowler (Canonical Data Model) · SSOT. אכיפה: one canonical ingest path; siblings share it. הפרה ידועה: `ingest_precedent` vs `ingest_internal_decision` asymmetry. + - **INV-G3 ingest אחיד ו-idempotent (upsert על מפתח דטרמיניסטי)** — Kleppmann · Stripe/CDC idempotency · ISO 8000. אכיפה: `01-ingest.md` unified path. + - **INV-G4 חוזה-שלמות לפני "שמיש/ניתן-לחיפוש"** — ISO 8000 · DAMA-UK (completeness) · ISO 15489 (reliability). אכיפה: write-validation + health-check; `02-data-model.md`. הפרה ידועה: ערן סופר 8046/24 indexed with empty headnote/summary/tags. + - **INV-G5 metadata מלא לכל פריט מואנדקס + הפרדת-קורפוס בכל query** — Pinecone (multitenancy) · RAG attribution (Lewis et al.) · ISO 8000. אכיפה: `03-retrieval.md`. הפרה ידועה: task #56 halacha_filters source_kind leak. + - **INV-G6 re-index בכל שינוי תוכן** — Pinecone · Weaviate · RAG freshness. אכיפה: ingest/update path. + - **INV-G7 מיזוג RRF לא סכום-ציונים** — Elastic (RRF) · Weaviate · OpenSearch/Azure (corrob.). אכיפה: retrieval fusion (already implemented — codified). + - **INV-G8 איכות-אחזור נמדדת (precision+recall)** — Manning IR textbook · RAG eval literature · (Elastic eval guidance). אכיפה: eval harness in `03-retrieval.md`. + - **INV-G9 עקיבוּת-מקור + audit-trail ל-AI** — CEPEJ (user control) · NCSC · ISO 15489. אכיפה: `X5-audit-provenance.md`. + - **INV-G10 המערכת מסייעת; שערים אנושיים = invariant** — NCSC ("never replace human judgment") · CEPEJ · FJC. אכיפה: `05-qa-review.md` human gates. + - **INV-G11 תוכן החלטה מנומקת** (רקע ניטרלי · ללא כפילות · מענה לטענות המפסיד · מבחן-השופט · טענות מקוריות) — FJC Writing Manual · South Bucks [2004] UKHL 33 · חוק לתיקון סדרי המינהל (החלטות והנמקות) תשי"ט-1958. אכיפה: `04-analysis-writing.md` + `05-qa-review.md`. + + 6. **כללי-הנדסה** — סימטריה · נרמול-לא-תיקון-תסמין · quality-at-source (Fowler/Data-Mesh) · אין בליעה שקטה. + 7. **אינדקס** — table linking all spec files (00, 01–07, X1–X5) with one-line purpose each. + 8. **נספח מקורות** — paste the full source appendix from design doc §9. + +- [ ] **Step 2: Run the per-file verification gate** (the 5-point checklist above). Fix inline. + +- [ ] **Step 3: Commit** + +```bash +git add docs/spec/00-constitution.md +git commit -m "docs(spec): 00-constitution — mission, 11 global invariants, engineering rules" +``` + +- [ ] **Step 4: REVIEW CHECKPOINT** — present `00-constitution.md` to חיים. Do not start Phase 2 until approved. If the constitution's framing changes, the domain files adapt to it. + +--- + +## Phase 2 — Lifecycle domain files + +> Each task: (a) targeted research to verify domain-specific invariants to ≥3 sources (global invariants already verified — reuse their sources; only NEW domain claims need fresh sourcing); (b) draft the file; (c) run the verification gate; (d) commit. Group review checkpoint at end of Phase 2. + +### Task 2: `01-ingest.md` — unified intake contract + +**Files:** Create `docs/spec/01-ingest.md` + +- [ ] **Step 1:** Document the **target single ingest path** for all three intake kinds (case documents / external precedent / internal-committee decisions). Describe the canonical pipeline: stage file → extract text → chunk → embed → store → queue metadata extraction → queue halacha extraction → set statuses. State which steps are **uniform across all kinds** (this is the fix for the asymmetry). +- [ ] **Step 2:** Define domain invariants applying INV-G2/G3/G4/G6 to ingest, e.g.: + - **INV-ING1:** every intake kind flows through the same canonical ingest function; a new kind extends it via parameters, never a parallel function. (sources: INV-G2 set) + - **INV-ING2:** ingest is idempotent on the canonical identifier (re-ingest = upsert, no duplicate row/chunks). (sources: INV-G3 set) + - **INV-ING3:** metadata extraction is queued for *every* kind that has extractable metadata — not conditional per path. (sources: INV-G4 set; הפרה ידועה: internal path skipped `request_metadata_extraction`) +- [ ] **Step 3:** Cite current reality as audit-findings (the 8 documented asymmetries from the design research) — as `הפרה ידועה` lines, not as "correct." +- [ ] **Step 4:** Run verification gate. **Step 5:** Commit `docs(spec): 01-ingest unified intake contract`. + +### Task 3: `02-data-model.md` — entities + completeness contract + +**Files:** Create `docs/spec/02-data-model.md` + +- [ ] **Step 1:** Enumerate the canonical entities (cases, case_law, documents, chunks, halachot, chair_feedback, …) — name, purpose, key fields. Mark this as the **target** model (verify field names against current schema during execution; divergences → audit-findings). +- [ ] **Step 2:** Define the **completeness contract per entity** — the mandatory-field set that makes a record "usable/searchable" (INV-G4). For `case_law`: e.g., canonical case_number, case_name, court, practice_area, source_kind, + (for searchable) ≥1 chunk and non-empty metadata. State explicitly that records failing the contract are flagged, not silently searchable. + - **INV-DM1:** a case_law row is "searchable" only when its completeness contract is satisfied. (sources: ISO 8000 · DAMA-UK · ISO 15489) + - **INV-DM2:** each entity has exactly one canonical identifier; no field stores a full citation as the identifier. (sources: INV-G1 set; הפרה ידועה: citation-as-case_number for סופר entries) +- [ ] **Step 3:** Run gate. **Step 4:** Commit `docs(spec): 02-data-model entities + completeness contract`. + +### Task 4: `03-retrieval.md` — corpora + retrieval invariants + +**Files:** Create `docs/spec/03-retrieval.md` + +- [ ] **Step 1:** Document the 3 corpora + their search tools (source_kind mapping) and the hybrid/RRF design. (Reuse research from design §9 RAG sources — already verified.) +- [ ] **Step 2:** Define invariants (apply INV-G5/G6/G7/G8/G9): + - **INV-RET1:** corpus separation enforced on 100% of query paths (chunks AND halachot filters). (Pinecone · ISO · RAG; הפרה ידועה: task #56) + - **INV-RET2:** no item indexed without complete required metadata + resolvable source locator. (INV-G5 set) + - **INV-RET3:** heterogeneous retrievers fused by RRF, never raw-score sum. (Elastic · Weaviate) + - **INV-RET4:** retrieval quality measured by a standing precision+recall eval harness on a fixed labeled query set. (Manning · RAG eval) + - **INV-RET5:** every returned span is attributable to its source. (CEPEJ · RAG) +- [ ] **Step 3:** Run gate. **Step 4:** Commit `docs(spec): 03-retrieval corpora + retrieval invariants`. + +### Task 5: `04-analysis-writing.md` — claims, 12 blocks, Dafna style + +**Files:** Create `docs/spec/04-analysis-writing.md` + +- [ ] **Step 1:** Reference (cite, don't duplicate) `block-schema.md`, `decision-methodology.md`, `skills/decision/SKILL.md` as sources; summarize the 12-block model + claims extraction at spec altitude. +- [ ] **Step 2:** Verify the Israeli reasoned-decision sources (design doc §8 open items #1–#3): confirm exact section of חוק 1958 (תשכ"ט-1969 amendment) on Nevo; confirm/locate ברק-ארז citation; confirm בג"ץ 143/56 / עע"ם 2994/21. Mark each `verified` or `⚠ UNVERIFIED` + escalate. +- [ ] **Step 3:** Define invariants from INV-G11: + - **INV-WR1:** block ו (background) is neutral — no judgment words, no party quotes. (FJC · חובת הנמקה) + - **INV-WR2:** no duplication — block י references prior blocks, does not restate facts. (FJC §non-duplication) + - **INV-WR3:** every losing-side principal argument is addressed. (FJC · South Bucks adequacy) + - **INV-WR4:** block ז = original claims only; supplements go to block ח. (project rule; cite corpus-analysis) + - **INV-WR5:** judge-unfamiliar-with-case test — decision is self-contained and traceable. (FJC · South Bucks) +- [ ] **Step 4:** Run gate. **Step 5:** Commit `docs(spec): 04-analysis-writing — 12 blocks + reasoned-decision invariants`. + +### Task 6: `05-qa-review.md` — QA gates + human gates + +**Files:** Create `docs/spec/05-qa-review.md` + +- [ ] **Step 1:** Document the existing automated QA gates (`validate_decision`: neutral_background, claims_coverage, weight_compliance, structural_integrity, no_duplication, sequential_numbering) — as the QA contract (verify against `qa_validator.py` at execution). +- [ ] **Step 2:** Define human-gate invariants (INV-G10): + - **INV-QA1:** halacha approval is a manual chair decision; auto-extracted halachot are `pending_review` until the chair approves. (NCSC · CEPEJ · project rule) + - **INV-QA2:** outcome selection and chair feedback are human gates, never automated. (NCSC · CEPEJ · FJC) + - **INV-QA3:** a decision cannot be exported while critical QA gates fail. (FJC · validate_decision design) +- [ ] **Step 3:** Run gate. **Step 4:** Commit `docs(spec): 05-qa-review — QA + human gates`. + +### Task 7: `06-export.md` — DOCX export contract + +**Files:** Create `docs/spec/06-export.md` + +- [ ] **Step 1:** Reference `skills/dafna-decision-template/SKILL.md`; document the export contract: line classification, dash policy, placeholder handling, template styles. Define: + - **INV-EX1:** export is deterministic from the stored decision blocks (single source = DB blocks; the DOCX is derived). (INV-G2 derived-data set) + - **INV-EX2:** export preserves source traceability where required. (INV-G9) +- [ ] **Step 2:** Run gate. **Step 3:** Commit `docs(spec): 06-export DOCX contract`. + +### Task 8: `07-learning.md` — Hermes, lessons, feedback loop + +**Files:** Create `docs/spec/07-learning.md` + +- [ ] **Step 1:** Document the learning loop: Hermes curator (post-export analysis), `docs/legal-decision-lessons.md`, chair-feedback weekly analysis. Define: + - **INV-LRN1:** curator proposes; changes to SKILL.md/lessons.md require manual chair approval. (INV-G10; project rule) + - **INV-LRN2:** quality accountability sits at the source (ingest/authoring), not downstream. (Fowler/Data-Mesh) +- [ ] **Step 2:** Run gate. **Step 3:** Commit `docs(spec): 07-learning loop`. + +- [ ] **Phase 2 REVIEW CHECKPOINT** — present `01`–`07` to חיים for review before Phase 3. + +--- + +## Phase 3 — Cross-cutting files (final REVIEW after) + +### Task 9: `X1-identifiers.md` — canonical identifier model + +**Files:** Create `docs/spec/X1-identifiers.md` + +- [ ] **Step 1:** Define the canonical case_number model: the normalized written form, the relationship `cases.case_number` vs `case_law.case_number`, and citation formats. Specify **normalize-on-write** (INV-G1), with tolerant-match-on-read as a *secondary* convenience, not the primary mechanism. + - **INV-ID1:** case_number is normalized to canonical form at write time. (SSOT · Codd · Kleppmann) + - **INV-ID2:** no entity uses a full citation string as its identifier. (INV-G1; הפרה ידועה: סופר entries) +- [ ] **Step 2:** Run gate. **Step 3:** Commit `docs(spec): X1-identifiers canonical model`. + +### Task 10: `X2-multi-company.md` + +**Files:** Create `docs/spec/X2-multi-company.md` + +- [ ] **Step 1:** Document CMP (1xxx) / CMPA (8xxx), 14 agents (7×2), and the sync rules (cite `sync_agents_across_companies.py`, `HEARTBEAT.md`). Define: + - **INV-MC1:** any agent-config change in master must be synced to the mirror company via the API sync script. (project rule) +- [ ] **Step 2:** Run gate. **Step 3:** Commit `docs(spec): X2-multi-company`. + +### Task 11: `X3-integration-deploy.md` + +**Files:** Create `docs/spec/X3-integration-deploy.md` + +- [ ] **Step 1:** Document Paperclip integration (wakeup via API not DB; comment routing via CEO; outbound case-status webhook) and the deploy model (Coolify dockerimage for legal-ai; pm2 for paperclip/chat-service). Define: + - **INV-INT1:** Paperclip wakeup goes through `POST /api/agents/{id}/wakeup` with `payload.issueId`, never a direct DB insert. (project rule; cite memory reference) + - **INV-INT2:** legal-ai code changes require commit→push→Coolify deploy; no local uvicorn. (project rule) +- [ ] **Step 2:** Run gate. **Step 3:** Commit `docs(spec): X3-integration-deploy`. + +### Task 12: `X4-agents.md` + +**Files:** Create `docs/spec/X4-agents.md` + +- [ ] **Step 1:** Map the domain agents (ceo, researcher, analyst, writer, qa, proofreader, exporter, hermes) — role + which spec files each must read. Reserve a section for the **process agents** (sub-project 5: add-feature / fix-feature / spec-guardian) to be defined later. Define: + - **INV-AG1:** every agent reads `00-constitution.md` first and the relevant domain spec before acting. (governance rule) +- [ ] **Step 2:** Run gate. **Step 3:** Commit `docs(spec): X4-agents map`. + +### Task 13: `X5-audit-provenance.md` + +**Files:** Create `docs/spec/X5-audit-provenance.md` + +- [ ] **Step 1:** Define the audit-trail + provenance requirements (INV-G9): logging of AI-assisted generation, traceability of every cited authority/source in a decision back to the corpus, record integrity over time. + - **INV-AUD1:** every AI-assisted artifact records what sources/data produced it. (CEPEJ user-control · NCSC · ISO 15489) + - **INV-AUD2:** record integrity — a stored decision/record is complete and unaltered except via tracked, attributed changes. (ISO 15489 §5.2.2.3) +- [ ] **Step 2:** Run gate. **Step 3:** Commit `docs(spec): X5-audit-provenance`. + +- [ ] **FINAL REVIEW** — present the complete spec-set to חיים. On approval, sub-project 1 is done; proceed to sub-project 2 (Audit) in its own spec→plan cycle. + +--- + +## Self-Review (run after writing this plan) + +- **Spec coverage:** every design-doc section maps to a task — mission/principles → Task 1; G1–G11 → Task 1 + applied in 2–13; spec-set structure → Tasks 0–13; verification protocol → conventions + gate; open legal items → Task 5 Step 2. ✓ +- **Placeholder scan:** domain-file invariants are enumerated with IDs + sources, not "define later"; the only deferred content is the process-agents section (Task 12) which is explicitly sub-project 5, and the legal `⚠ UNVERIFIED` items (Task 5) which are an intentional escalation, not a placeholder. ✓ +- **Type/name consistency:** invariant IDs are unique (G1–G11, ING1–3, DM1–2, RET1–5, WR1–5, QA1–3, EX1–2, LRN1–2, ID1–2, MC1, INT1–2, AG1, AUD1–2); file names consistent with design doc §5. ✓ +``` From c351d6d7147af86c77910abc9d1755e570637b64 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:12:25 +0000 Subject: [PATCH 03/30] docs(spec): scaffold docs/spec/ living spec-set Co-Authored-By: Claude Opus 4.8 --- docs/spec/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/spec/README.md diff --git a/docs/spec/README.md b/docs/spec/README.md new file mode 100644 index 0000000..60226fd --- /dev/null +++ b/docs/spec/README.md @@ -0,0 +1,7 @@ +# ספ המערכת — עוזר משפטי (Living System Spec) + +זהו מקור-האמת הקנוני ל"מהו תקין" במערכת. שער-הכניסה: [00-constitution.md](00-constitution.md). +כל invariant מגובה ב-≥3 מקורות סמכותיים; פריט לא-מאומת מסומן ⚠ UNVERIFIED ומועלה ליו"ר. + +מבנה: 00 חוקה · 01–07 מחזור-חיים · X1–X5 חוצי-שלבים. ראה אינדקס מלא בחוקה. +בסיס-עיצוב: docs/superpowers/specs/2026-05-30-system-spec-design.md From fd682d130fc9e0f995b36897c44ddf957443df1f Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:15:28 +0000 Subject: [PATCH 04/30] =?UTF-8?q?docs(spec):=2000-constitution=20=E2=80=94?= =?UTF-8?q?=20mission,=2011=20global=20invariants,=20engineering=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- docs/spec/00-constitution.md | 269 +++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 docs/spec/00-constitution.md diff --git a/docs/spec/00-constitution.md b/docs/spec/00-constitution.md new file mode 100644 index 0000000..c731a43 --- /dev/null +++ b/docs/spec/00-constitution.md @@ -0,0 +1,269 @@ +# 00 — חוקת המערכת (Constitution) + +זהו שער-הכניסה היחיד לספ המערכת *עוזר משפטי*. הוא מגדיר את הייעוד, עקרונות-העבודה, +תבנית ה-invariant, פרוטוקול-האימות, ה-invariants הגלובליים (G1–G11), כללי-ההנדסה, +אינדקס הספ ונספח המקורות. כל קובץ-תחום (01–07, X1–X5) כפוף לחוקה זו ומפנה אליה. + +--- + +## 1. ייעוד + +> מערכת AI שמסייעת ליו"ר ועדת הערר לתכנון ובנייה (מחוז ירושלים, עו"ד דפנה תמיר) לנסח +> **החלטות מעין-שיפוטיות כתובות ומנומקות** — מסמכים משפטיים פורמליים שעומדים לביקורת +> שיפוטית — תוך שמירה על **הקול, השיקול והאחריות של היו"ר**. + +- **משרת:** יו"ר הוועדה (משתמש-על) והסוכנים הפועלים בשמה. +- **מחזור-חיים:** ניהול תיקים → בסיס ידע (3 קורפוסים) → אחזור סמנטי (RAG) → סיוע-כתיבה + (12 בלוקים, סגנון דפנה) → ייצוא DOCX. +- **3 סוגי עררים:** רישוי ובנייה (1xxx, חם), היטל השבחה (8xxx, קר), פיצויים ס'197 (9xxx, קר). +- **ה"למה" העמוק:** המערכת מסייעת — היו"ר מכריעה (שערים קריטיים ידניים בכוונה); מנוע + צבירת-ידע (לומד מהחלטות סופיות ומפידבק); רב-חברתי (CMP/CMPA). + +--- + +## 2. עקרונות-עבודה + +1. **אסור להניח שהקיים תקין.** כל מה שמופה בקוד/בקורפוס = "טענה לבדיקה", לא "אמת". + "תקין" נגזר ממקורות חיצוניים סמכותיים, לא מהמערכת שתחת חשד. +2. **פרוטוקול אימות 3-מקורות:** כל invariant/חוק בספ מגובה ב-**≥3 מקורות סמכותיים מוכרים** + בעלי ידע מקצועי מוכח. כשאין 3 → מסומן `⚠ UNVERIFIED` ומועלה ליו"ר, לא מוכרע לבד. +3. **מנגנון:** מחקר עצמאי → טיוטה לביקורת. קודם חוקרים את הסמכויות החיצוניות, ורק אז + מנסחים את החוק/ההחלטה. +4. **מודל-שיתוף:** על החלטות טכניות/אדריכליות אני חוקר ומכריע מקצועית ומציג תוצאה + מוגמרת. שואל את היו"ר (חיים) רק במקום שבו *הוא* הסמכות — כוונה, עדיפויות עסקיות, + עובדות משפטיות-דומייניות. + +--- + +## 3. תבנית-invariant + +מבנה אחיד לכל חוק בספ (בכל הקבצים): + +``` +### INV-<תחום><מספר>: <כותרת קצרה> +**כלל:** <ניסוח נורמטיבי חד — מה חייב להתקיים> +**מקורות:** <≥3 סמכויות> | סטטוס: verified / ⚠ UNVERIFIED +**אכיפה:** <היכן/איך נאכף — schema / ולידציית-כתיבה / בדיקת-בריאות / שער אנושי> +**הפרה ידועה:** <דוגמה מהמערכת, אם יש — מקשר ל-audit; אחרת "—"> +``` + +--- + +## 4. פרוטוקול-אימות + +- כל invariant נושא שדה `מקורות` + `סטטוס: verified / ⚠ UNVERIFIED`. +- **verified** = מגובה ב-**≥3 מקורות סמכותיים** מוכרים בעלי ידע מקצועי מוכח. +- **⚠ UNVERIFIED** = פחות מ-3 מקורות מאומתים, או פריט שדורש אימות-מקור-ראשוני + (למשל ציטוט חקיקה ישראלי מדויק). פריט כזה **לא מוכרע לבד** — מועלה ליו"ר עם + הערת-הסלמה המתעדת מה חסר והיכן יאומת. +- החלטות טכניות → מחקר עצמאי + הכרעה מקצועית + הצגת תוצאה. שאלה ליו"ר רק במקום + שבו הוא הסמכות (ראה עיקרון 4 לעיל). + +--- + +## 5. Invariants גלובליים (G1–G11) + +אלה החוקים החוצים את כל המערכת — לב החוקה. כל אחד מגובה ב-≥3 סמכויות (נספח §8). +ביחד הם מייבשים את כשל-השורש החוזר: מסלולים/קורפוסים מקבילים שמתפצלים (drift) בלי +שכבה שמגדירה ואוכפת "תקין". + +### INV-G1: מזהה קנוני מנורמל בכתיבה +**כלל:** לכל ישות יש מזהה קנוני יחיד, **מנורמל בנקודת-הכתיבה** (לא תיקון-סלחני בקריאה +בלבד). `case_number` נשמר בצורה קנונית אחת; קריאה משווה מול הצורה הקנונית, לא מטליאה. +**מקורות:** SSOT (Single Source of Truth — normalization principle) · E.F. Codd, First +Normal Form (CACM 13(6), 1970) · Martin Kleppmann, *Designing Data-Intensive Applications* +(O'Reilly, 2017) | סטטוס: verified +**אכיפה:** schema (אילוץ ייחודיות על המפתח הקנוני) + ולידציית-כתיבה בנקודת-הקליטה; +מפורט ב-[X1-identifiers.md](X1-identifiers.md) ו-[02-data-model.md](02-data-model.md). +**הפרה ידועה:** `_normalize_case_number` סלחני בקריאה בלבד (קומיט "tolerant case_number +lookup"); `8126-25` לא נמצא מול האמיתי `8126-03-25` → ממצא ל-[audit](../audit-report.md). + +### INV-G2: מקור-אמת יחיד — אין מסלולים מקבילים מתפצלים +**כלל:** לכל סוג-נתון יש **מקור-אמת יחיד** ומסלול-קוד קנוני אחד. אסור להוסיף מסלול +מקביל ליכולת קיימת — ישויות-אחיות חולקות מסלול קנוני אחד; נתונים נגזרים (derived) +משוחזרים מהמקור, לא נכתבים במקביל. +**מקורות:** Martin Kleppmann (system of record vs. derived data, *DDIA* 2017) · Martin +Fowler (Canonical Data Model) · SSOT (Single Source of Truth) | סטטוס: verified +**אכיפה:** ביקורת-ארכיטקטורה + כלל-הנדסה "סימטריה" (§6); מפורט ב-[01-ingest.md](01-ingest.md). +**הפרה ידועה:** אסימטריה בין `ingest_precedent` (חיצוני, מתזמן חילוץ metadata) לבין +`ingest_internal_decision` (פנימי, לא מתזמן) → ממצא ל-[audit](../audit-report.md). + +### INV-G3: ingest אחיד ו-idempotent +**כלל:** קליטה היא **אחידה ו-idempotent** — upsert על מפתח דטרמיניסטי. קליטה חוזרת של +אותו פריט אינה יוצרת כפילות ואינה משנה תוצאה. +**מקורות:** Martin Kleppmann (*DDIA*, idempotence & exactly-once) · Stripe / CDC +idempotency-key pattern · ISO 8000 (Data quality) | סטטוס: verified +**אכיפה:** ולידציית-כתיבה + מפתח-upsert דטרמיניסטי בנקודת-הקליטה; מפורט ב- +[01-ingest.md](01-ingest.md). +**הפרה ידועה:** 3 החלטות "סופר" נקלטו ב-3 פורמטים שונים (`8126/24`, ציטוט-מלא +כ-case_number) — היעדר upsert דטרמיניסטי → ממצא ל-[audit](../audit-report.md). + +### INV-G4: חוזה-שלמות לפני "שמיש / ניתן-לחיפוש" +**כלל:** רשומה אינה נחשבת "שמישה" או "ניתנת-לחיפוש" עד ש**שדות-החובה שלה מולאו ואומתו +מול spec מפורש**. שלמות נבדקת לפני חשיפה לאחזור. +**מקורות:** ISO 8000 (completeness) · DAMA-UK *Six Primary Dimensions for Data Quality* +(2013, completeness) · ISO 15489-1:2016 (records reliability) | סטטוס: verified +**אכיפה:** חוזה-שלמות באכיפת-קוד + בדיקת-בריאות; מפורט ב-[02-data-model.md](02-data-model.md) +ו-[03-retrieval.md](03-retrieval.md). +**הפרה ידועה:** ערן סופר 8046/24 אונדקס עם `headnote`/`summary`/`tags` ריקים → ממצא +ל-[audit](../audit-report.md). + +### INV-G5: metadata מלא + הפרדת-קורפוס נאכפת בכל query +**כלל:** לכל פריט מואנדקס יש **metadata מלא** (כולל מזהה-מקור וסוג-קורפוס), ו**הפרדת- +הקורפוס נאכפת בכל מסלול-query** — אין דליפה בין 3 הקורפוסים. +**מקורות:** Pinecone (multitenancy / metadata filtering) · RAG attribution (Lewis et al., +2020, NeurIPS) · ISO 8000 (Data quality) | סטטוס: verified +**אכיפה:** schema (metadata חובה) + פילטר-קורפוס נאכף בשכבת-החיפוש; מפורט ב- +[03-retrieval.md](03-retrieval.md) ו-[X5-audit-provenance.md](X5-audit-provenance.md). +**הפרה ידועה:** משימה #56 — דליפת `source_kind` ב-`halacha_filters` בין קורפוסים → +ממצא ל-[audit](../audit-report.md). + +### INV-G6: re-index בכל שינוי תוכן +**כלל:** כל שינוי-תוכן של פריט מואנדקס מפעיל **re-index** של ה-embedding שלו. אין +embeddings מיושנים מול התוכן הנוכחי. +**מקורות:** Pinecone (index freshness / data sync) · Weaviate (re-vectorization on update) +· RAG freshness (Lewis et al., 2020) | סטטוס: verified +**אכיפה:** טריגר re-index בנקודת-העדכון + בדיקת-בריאות (גילוי drift); מפורט ב- +[02-data-model.md](02-data-model.md) ו-[03-retrieval.md](03-retrieval.md). +**הפרה ידועה:** — + +### INV-G7: מיזוג RRF — לא סכום-ציונים +**כלל:** מיזוג תוצאות בין retrievers נעשה **לפי דירוג (Reciprocal Rank Fusion)**, לא +סכום/ממוצע ציונים גולמיים — שכן ציונים בסקיילים שונים אינם בני-השוואה ישירה. +**מקורות:** Elastic (*Reciprocal Rank Fusion*) · Weaviate (*Hybrid Search Explained*) · +OpenSearch / Azure AI Search (corroborating RRF guidance) | סטטוס: verified +**אכיפה:** קוד-המיזוג בשכבת-האחזור; מפורט ב-[03-retrieval.md](03-retrieval.md). +**הפרה ידועה:** — + +### INV-G8: איכות-אחזור נמדדת — precision + recall +**כלל:** איכות-האחזור **נמדדת אמפירית (precision + recall)** באמצעות eval harness, לא +מונחת. שינוי בשכבת-האחזור מלווה במדידה. +**מקורות:** Manning, Raghavan & Schütze, *Introduction to Information Retrieval* (CUP, +2008) · RAG evaluation literature (Lewis et al., 2020 ואחריו) · Elastic (relevance +evaluation guidance) | סטטוס: verified +**אכיפה:** eval harness + בדיקת-בריאות תקופתית; מפורט ב-[03-retrieval.md](03-retrieval.md). +**הפרה ידועה:** — + +### INV-G9: עקיבוּת-מקור + audit-trail ל-AI +**כלל:** כל פלט של המערכת **עקיב למקורו** (citation/provenance), וכל שימוש ב-AI מתועד +ב-**audit-trail** הניתן לביקורת. +**מקורות:** Council of Europe / CEPEJ — *European Ethical Charter on AI in judicial systems* +(2018, user-control principle) · NCSC/JTC — *Principles & Practices for AI Use in Courts* · +ISO 15489-1:2016 (records authenticity/integrity) | סטטוס: verified +**אכיפה:** audit-trail באכיפת-קוד + עקיבוּת-מקור בכל פלט; מפורט ב- +[X5-audit-provenance.md](X5-audit-provenance.md). +**הפרה ידועה:** — + +### INV-G10: המערכת מסייעת — שערים אנושיים הם invariant +**כלל:** המערכת **מסייעת ואינה מחליפה את שיקול-הדעת האנושי**. השערים האנושיים (אישור +הלכה, בחירת תוצאה, פידבק היו"ר) הם **invariant — חובה, לא רשות**. +**מקורות:** NCSC/JTC — *Principles & Practices for AI Use in Courts* ("never replace human +judgment") · CEPEJ (2018, under user control) · Federal Judicial Center — *Judicial Writing +Manual* (2d ed.) | סטטוס: verified +**אכיפה:** שערים אנושיים בקוד-הזרימה (gate לא ניתן לעקיפה); מפורט ב-[05-qa-review.md](05-qa-review.md). +**הפרה ידועה:** 10/19 הלכות מאושרות, התגלה במקרה — שער ידני שקוף בלי נראות backlog → +ממצא ל-[audit](../audit-report.md). + +### INV-G11: תוכן החלטה מנומקת +**כלל:** החלטה מנומקת מקיימת: **רקע ניטרלי** (עובדות בלבד, ללא שיפוט) · **ללא כפילות** +(בלוק דיון מפנה, לא חוזר) · **מענה לטענות הצד המפסיד** · **"מבחן-השופט"** (קריא לשופט שלא +מכיר את התיק) · **טענות מקוריות בלבד** (מכתבי הטענות). +**מקורות:** +- Federal Judicial Center — *Judicial Writing Manual* (2d ed.) | סטטוס: verified +- *South Buckinghamshire DC v Porter (No 2)* [2004] UKHL 33 (adequacy of reasons) | + סטטוס: verified +- חוק לתיקון סדרי המינהל (החלטות והנמקות), תשי"ט-1958 (חובת הנמקה) | סטטוס: ⚠ UNVERIFIED +**אכיפה:** שערי QA + checklist-תוכן לפי סוג-ערר; מפורט ב-[04-analysis-writing.md](04-analysis-writing.md) +ו-[05-qa-review.md](05-qa-review.md). +**הפרה ידועה:** — +**הערת-הסלמה (⚠ UNVERIFIED):** מספר-הסעיף המדויק בחוק תשי"ט-1958 וכן תיקון תשכ"ט-1969 +דורשים אימות-מקור-ראשוני מול Nevo. G11 נשמר תקף עם **2 מקורות verified** (FJC + +South Bucks) + מקור-חקיקה אחד שספציפיות-הסעיף שלו לא-מאומתת. האימות המדויק +(§/תיקון) יבוצע בקובץ [04-analysis-writing.md](04-analysis-writing.md) ויועלה ליו"ר. + +--- + +## 6. כללי-הנדסה (מונעים הישנות) + +- **סימטריה:** אסור להוסיף מסלול מקביל ליכולת קיימת — מרחיבים את המסלול הקנוני + (נגזר מ-[G2](#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים)). +- **נרמול לא תיקון-תסמין:** מתקנים נתון במקור (קנוני), לא מטליאים בקריאה + (נגזר מ-[G1](#inv-g1-מזהה-קנוני-מנורמל-בכתיבה)). +- **Quality-at-source:** שלמות נאכפת קרוב ככל האפשר לקליטה (Martin Fowler — Data Mesh / + quality-at-source; נגזר מ-[G4](#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש)). +- **אין בליעה שקטה:** רשומה חסרה/פגומה מסומנת ומדווחת, לא מתקבלת בשקט (תואם feedback + קיים — אסור bare `except: pass`; נגזר מ-[G4](#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש)). + +--- + +## 7. אינדקס הספ + +| קובץ | תפקיד | אוכף invariants | +|------|--------|-----------------| +| [00-constitution.md](00-constitution.md) | חוקה — ייעוד, invariants גלובליים, כללי-הנדסה, אינדקס | G1–G11 | +| [01-ingest.md](01-ingest.md) | קליטה מאוחדת: מסמכי-תיק / פסיקה חיצונית / החלטות-ועדה — חוזה מסלול-יחיד | G2, G3 | +| [02-data-model.md](02-data-model.md) | אחסון: ישויות (cases, case_law, documents, chunks, halachot…) + חוזה-שלמות לכל ישות | G1, G4, G6 | +| [03-retrieval.md](03-retrieval.md) | 3 קורפוסים + כלי-חיפוש · hybrid/RRF · attribution · eval harness | G4, G5, G6, G7, G8 | +| [04-analysis-writing.md](04-analysis-writing.md) | חילוץ טענות · 12 בלוקים · סגנון דפנה (מצטט block-schema.md) | G11 | +| [05-qa-review.md](05-qa-review.md) | שערי QA + שערים אנושיים (אישור הלכה, בחירת תוצאה, פידבק) כ-invariant | G10, G11 | +| [06-export.md](06-export.md) | ייצוא DOCX לפי תבנית דפנה | — | +| [07-learning.md](07-learning.md) | Hermes · לקחים · לולאת פידבק היו"ר · צמיחת קורפוס (quality-at-source) | G4, G10 | +| [X1-identifiers.md](X1-identifiers.md) | מודל מזהים קנוני: נרמול case_number בכתיבה · cases מול case_law · פורמטי ציטוט | G1 | +| [X2-multi-company.md](X2-multi-company.md) | CMP/CMPA · 14 סוכנים · כללי sync | G2 | +| [X3-integration-deploy.md](X3-integration-deploy.md) | Paperclip (wakeup, ניתוב comments, webhooks) · Coolify/pm2 | — | +| [X4-agents.md](X4-agents.md) | מפת הסוכנים (דומיין + סוכני-התהליך) | G10 | +| [X5-audit-provenance.md](X5-audit-provenance.md) | audit-trail לשימוש ב-AI · עקיבוּת כל מקור מצוטט · שלמות-רשומה | G5, G9 | + +**עקרונות:** כל קובץ עצמאי, ממוקד, agent-readable, יעד ≤~500 שורות (תפיחה = סימן +לפיצול). מסמכים קיימים (`architecture.md`, `product-specification.md`, `block-schema.md`…) +לא נמחקים ולא משוכפלים — מצוטטים כ"מקור" ומאומתים מול הסמכויות; סתירה = ממצא ל-audit. + +--- + +## 8. נספח מקורות סמכותיים + +(מאומתים במחקר 30.5.2026) + +**ממשל-AI שיפוטי + מבנה החלטה מנומקת** +- NCSC / JTC — *Court Technology Standards* + *Principles & Practices for AI Use in Courts*. + https://www.ncsc.org/our-centers-projects/joint-technology-committee/court-technology-standards +- Federal Judicial Center — *Judicial Writing Manual* (2d ed.). + https://www.fjc.gov/content/judicial-writing-manual-pocket-guide-judges-second-edition +- Council of Europe / CEPEJ — *European Ethical Charter on the use of AI in judicial + systems* (2018). +- *South Buckinghamshire DC v Porter (No 2)* [2004] UKHL 33 (adequacy of reasons). + https://publications.parliament.uk/pa/ld200304/ldjudgmt/jd040701/south-1.htm +- חוק לתיקון סדרי המינהל (החלטות והנמקות), תשי"ט-1958. + https://www.nevo.co.il/law_html/law00/98603.htm +- Kevin D. Ashley — *Artificial Intelligence and Legal Analytics* (CUP). + +**אחזור / RAG / IR** +- Lewis et al. (2020) — *Retrieval-Augmented Generation* (NeurIPS). + https://arxiv.org/abs/2005.11401 +- Manning, Raghavan & Schütze — *Introduction to Information Retrieval* (CUP, 2008). + https://nlp.stanford.edu/IR-book/ +- Elastic — *Reciprocal Rank Fusion*. + https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion +- Pinecone — *Implement multitenancy*. + https://docs.pinecone.io/guides/index-data/implement-multitenancy +- Weaviate — *Hybrid Search Explained*. https://weaviate.io/blog/hybrid-search-explained + +**שלמות-נתונים / איכות / רשומות** +- DAMA-DMBOK2 + DAMA-UK — *Six Primary Dimensions for Data Quality* (2013). +- ISO 8000 — Data quality (8000-8/61/110). +- ISO 15489-1:2016 — Records management (authenticity/reliability/integrity/usability). +- Martin Kleppmann — *Designing Data-Intensive Applications* (O'Reilly, 2017). +- E.F. Codd — Relational model & normalization (CACM 13(6), 1970). +- Martin Fowler — Canonical Data Model / Data Mesh (quality-at-source). + +--- + +## פריטים פתוחים — אימות-מקור-ראשוני נדרש + +(החוקר אימת מסגרת; הפריטים הישראליים דורשים אימות לפני ציטוט כ-סמכות, בשלב כתיבת +`04`/`05`/`X5`) + +1. מספר הסעיף המדויק בחוק לתיקון סדרי המינהל (החלטות והנמקות) תשי"ט-1958 (וכן תיקון + תשכ"ט-1969) — ⚠ UNVERIFIED, יאומת מול Nevo בקובץ [04-analysis-writing.md](04-analysis-writing.md). +2. ציטוט מדויק מ-ברק-ארז, *משפט מינהלי*. +3. אסמכתאות פסיקה: בג"ץ 143/56; עע"ם 2994/21 (מעמד ועדת ערר כגוף תכנוני-מקצועי). From 1daaa4861bbc7f15fb92924092d1f72d85c34546 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:21:00 +0000 Subject: [PATCH 05/30] docs(spec): reframe G2 example as structural asymmetry + note forthcoming files Co-Authored-By: Claude Opus 4.8 --- docs/spec/00-constitution.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/spec/00-constitution.md b/docs/spec/00-constitution.md index c731a43..7c8769d 100644 --- a/docs/spec/00-constitution.md +++ b/docs/spec/00-constitution.md @@ -85,8 +85,10 @@ lookup"); `8126-25` לא נמצא מול האמיתי `8126-03-25` → ממצא **מקורות:** Martin Kleppmann (system of record vs. derived data, *DDIA* 2017) · Martin Fowler (Canonical Data Model) · SSOT (Single Source of Truth) | סטטוס: verified **אכיפה:** ביקורת-ארכיטקטורה + כלל-הנדסה "סימטריה" (§6); מפורט ב-[01-ingest.md](01-ingest.md). -**הפרה ידועה:** אסימטריה בין `ingest_precedent` (חיצוני, מתזמן חילוץ metadata) לבין -`ingest_internal_decision` (פנימי, לא מתזמן) → ממצא ל-[audit](../audit-report.md). +**הפרה ידועה:** שני מסלולי ingest מקבילים לישויות-אחיות (`ingest_precedent` מול +`ingest_internal_decision`) שמתפצלים — לדוגמה: המסלול החיצוני מתזמן חילוץ metadata +(`request_metadata_extraction`), והמסלול הפנימי לא — ולכן ערן סופר 8046/24 נקלטה בלי +metadata → ממצא ל-[audit](../audit-report.md). ### INV-G3: ingest אחיד ו-idempotent **כלל:** קליטה היא **אחידה ו-idempotent** — upsert על מפתח דטרמיניסטי. קליטה חוזרת של @@ -198,6 +200,9 @@ South Bucks) + מקור-חקיקה אחד שספציפיות-הסעיף שלו ## 7. אינדקס הספ +> הערה: נכון לעכשיו קיימת רק החוקה (קובץ זה). הקבצים 01–07 ו-X1–X5 ייכתבו בהמשך — +> הקישורים אליהם הם הפניות-קדימה, לא קישורים שבורים. + | קובץ | תפקיד | אוכף invariants | |------|--------|-----------------| | [00-constitution.md](00-constitution.md) | חוקה — ייעוד, invariants גלובליים, כללי-הנדסה, אינדקס | G1–G11 | From 6afd155dc152a77cd80ce7713b9d6f548bbec542 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:37:54 +0000 Subject: [PATCH 06/30] =?UTF-8?q?docs(spec):=20scope=20=E2=89=A53-source?= =?UTF-8?q?=20rule=20to=20engineering=20decisions;=20reframe=20legal-conte?= =?UTF-8?q?nt=20(G11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per chair clarification: the ≥3-authoritative-source verification protocol governs ENGINEERING/architecture decisions only (G1–G10). Legal-domain content (G11) is the authority of the chair + project docs (block-schema, decision-methodology, lessons, skills/decision) — NOT externally triple-sourced. - §2/§4/§5 scoped to engineering invariants; added the two-authority distinction - G11 reframed: source-of-authority = chair + project docs; removed FJC/South Bucks/ 1958-statute as "sources to verify" and the UNVERIFIED flag - Removed the "open items — primary-source verification" section (the over-application) - Pruned now-orphaned legal sources from the appendix (kept NCSC/CEPEJ/FJC for G9/G10) Co-Authored-By: Claude Opus 4.8 --- docs/spec/00-constitution.md | 87 ++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/docs/spec/00-constitution.md b/docs/spec/00-constitution.md index 7c8769d..d7802f7 100644 --- a/docs/spec/00-constitution.md +++ b/docs/spec/00-constitution.md @@ -23,15 +23,19 @@ ## 2. עקרונות-עבודה -1. **אסור להניח שהקיים תקין.** כל מה שמופה בקוד/בקורפוס = "טענה לבדיקה", לא "אמת". - "תקין" נגזר ממקורות חיצוניים סמכותיים, לא מהמערכת שתחת חשד. -2. **פרוטוקול אימות 3-מקורות:** כל invariant/חוק בספ מגובה ב-**≥3 מקורות סמכותיים מוכרים** - בעלי ידע מקצועי מוכח. כשאין 3 → מסומן `⚠ UNVERIFIED` ומועלה ליו"ר, לא מוכרע לבד. -3. **מנגנון:** מחקר עצמאי → טיוטה לביקורת. קודם חוקרים את הסמכויות החיצוניות, ורק אז - מנסחים את החוק/ההחלטה. +1. **אסור להניח שהקיים תקין (בהנדסה).** כל מה שמופה בקוד = "טענה לבדיקה", לא "אמת". + "תקין" מבחינה הנדסית נגזר ממקורות חיצוניים סמכותיים, לא מהמערכת שתחת חשד. +2. **פרוטוקול אימות 3-מקורות — חל על החלטות הנדסה/פיתוח בלבד:** כל invariant הנדסי/ + ארכיטקטוני (תכנון ובניית האפליקציה — נתונים, מזהים, ingest, אחזור) מגובה ב-**≥3 מקורות + סמכותיים מוכרים** בעלי ידע מקצועי מוכח. כשאין 3 → מסומן `⚠ UNVERIFIED` ומועלה ליו"ר. + **התוכן המשפטי אינו כפוף לכלל זה** — הסמכות עליו היא היו"ר (דפנה) ומסמכי-הפרויקט + (block-schema, decision-methodology, legal-decision-lessons, skills/decision), לא + מקורות חיצוניים. +3. **מנגנון:** מחקר עצמאי → טיוטה לביקורת. קודם חוקרים את הסמכויות החיצוניות (להחלטות + הנדסה), ורק אז מנסחים את ה-invariant. 4. **מודל-שיתוף:** על החלטות טכניות/אדריכליות אני חוקר ומכריע מקצועית ומציג תוצאה מוגמרת. שואל את היו"ר (חיים) רק במקום שבו *הוא* הסמכות — כוונה, עדיפויות עסקיות, - עובדות משפטיות-דומייניות. + ותוכן משפטי-דומייני. --- @@ -47,25 +51,36 @@ **הפרה ידועה:** <דוגמה מהמערכת, אם יש — מקשר ל-audit; אחרת "—"> ``` +> **שדה המקורות לפי סוג:** invariant הנדסי → `מקורות` = ≥3 סמכויות חיצוניות + `סטטוס`. +> invariant תוכן-משפטי → `מקור-סמכות` = היו"ר + מסמכי-הפרויקט (ללא סטטוס-אימות חיצוני). + --- ## 4. פרוטוקול-אימות -- כל invariant נושא שדה `מקורות` + `סטטוס: verified / ⚠ UNVERIFIED`. +> חל על **invariants הנדסיים (G1–G10)** — החלטות תכנון/בניית האפליקציה. ה-invariant של +> תוכן-משפטי (G11) **אינו** כפוף לפרוטוקול זה; הסמכות עליו היא היו"ר + מסמכי-הפרויקט. + +- כל invariant הנדסי נושא שדה `מקורות` + `סטטוס: verified / ⚠ UNVERIFIED`. - **verified** = מגובה ב-**≥3 מקורות סמכותיים** מוכרים בעלי ידע מקצועי מוכח. -- **⚠ UNVERIFIED** = פחות מ-3 מקורות מאומתים, או פריט שדורש אימות-מקור-ראשוני - (למשל ציטוט חקיקה ישראלי מדויק). פריט כזה **לא מוכרע לבד** — מועלה ליו"ר עם - הערת-הסלמה המתעדת מה חסר והיכן יאומת. +- **⚠ UNVERIFIED** = החלטה הנדסית שיש לה פחות מ-3 מקורות סמכותיים מאומתים. פריט כזה + **לא מוכרע לבד** — מועלה ליו"ר עם הערת-הסלמה המתעדת מה חסר והיכן יאומת. - החלטות טכניות → מחקר עצמאי + הכרעה מקצועית + הצגת תוצאה. שאלה ליו"ר רק במקום שבו הוא הסמכות (ראה עיקרון 4 לעיל). --- -## 5. Invariants גלובליים (G1–G11) +## 5. Invariants גלובליים -אלה החוקים החוצים את כל המערכת — לב החוקה. כל אחד מגובה ב-≥3 סמכויות (נספח §8). -ביחד הם מייבשים את כשל-השורש החוזר: מסלולים/קורפוסים מקבילים שמתפצלים (drift) בלי -שכבה שמגדירה ואוכפת "תקין". +אלה החוקים החוצים את כל המערכת — לב החוקה. הם נחלקים לשני סוגים לפי **מקור-הסמכות**: + +- **G1–G10 — invariants הנדסיים** (תכנון/בניית האפליקציה): כל אחד מגובה ב-**≥3 סמכויות + טכניות מוכרות** (נספח §8). ביחד הם מייבשים את כשל-השורש החוזר: מסלולים/קורפוסים + מקבילים שמתפצלים (drift) בלי שכבה שמגדירה ואוכפת "תקין". +- **G11 — invariant תוכן-משפטי:** הסמכות עליו היא **היו"ר (דפנה) + מסמכי-הפרויקט**, לא + מקורות חיצוניים, ואינו כפוף לפרוטוקול ≥3-המקורות. + +### 5א. Invariants הנדסיים (G1–G10) ### INV-G1: מזהה קנוני מנורמל בכתיבה **כלל:** לכל ישות יש מזהה קנוני יחיד, **מנורמל בנקודת-הכתיבה** (לא תיקון-סלחני בקריאה @@ -166,22 +181,19 @@ Manual* (2d ed.) | סטטוס: verified **הפרה ידועה:** 10/19 הלכות מאושרות, התגלה במקרה — שער ידני שקוף בלי נראות backlog → ממצא ל-[audit](../audit-report.md). +### 5ב. Invariant תוכן-משפטי (G11) + ### INV-G11: תוכן החלטה מנומקת **כלל:** החלטה מנומקת מקיימת: **רקע ניטרלי** (עובדות בלבד, ללא שיפוט) · **ללא כפילות** (בלוק דיון מפנה, לא חוזר) · **מענה לטענות הצד המפסיד** · **"מבחן-השופט"** (קריא לשופט שלא מכיר את התיק) · **טענות מקוריות בלבד** (מכתבי הטענות). -**מקורות:** -- Federal Judicial Center — *Judicial Writing Manual* (2d ed.) | סטטוס: verified -- *South Buckinghamshire DC v Porter (No 2)* [2004] UKHL 33 (adequacy of reasons) | - סטטוס: verified -- חוק לתיקון סדרי המינהל (החלטות והנמקות), תשי"ט-1958 (חובת הנמקה) | סטטוס: ⚠ UNVERIFIED +**מקור-סמכות:** היו"ר (עו"ד דפנה תמיר) + מסמכי-הפרויקט — [block-schema.md](../block-schema.md), +[decision-methodology.md](../decision-methodology.md), [legal-decision-lessons.md](../legal-decision-lessons.md), +[skills/decision/SKILL.md](../../skills/decision/SKILL.md). **אינו כפוף לפרוטוקול ≥3-המקורות החיצוני** — +זהו תוכן משפטי-דומייני, באחריות היו"ר. **אכיפה:** שערי QA + checklist-תוכן לפי סוג-ערר; מפורט ב-[04-analysis-writing.md](04-analysis-writing.md) ו-[05-qa-review.md](05-qa-review.md). **הפרה ידועה:** — -**הערת-הסלמה (⚠ UNVERIFIED):** מספר-הסעיף המדויק בחוק תשי"ט-1958 וכן תיקון תשכ"ט-1969 -דורשים אימות-מקור-ראשוני מול Nevo. G11 נשמר תקף עם **2 מקורות verified** (FJC + -South Bucks) + מקור-חקיקה אחד שספציפיות-הסעיף שלו לא-מאומתת. האימות המדויק -(§/תיקון) יבוצע בקובץ [04-analysis-writing.md](04-analysis-writing.md) ויועלה ליו"ר. --- @@ -229,18 +241,14 @@ South Bucks) + מקור-חקיקה אחד שספציפיות-הסעיף שלו (מאומתים במחקר 30.5.2026) -**ממשל-AI שיפוטי + מבנה החלטה מנומקת** +**ממשל-AI שיפוטי + שערים אנושיים (G9, G10)** - NCSC / JTC — *Court Technology Standards* + *Principles & Practices for AI Use in Courts*. https://www.ncsc.org/our-centers-projects/joint-technology-committee/court-technology-standards -- Federal Judicial Center — *Judicial Writing Manual* (2d ed.). - https://www.fjc.gov/content/judicial-writing-manual-pocket-guide-judges-second-edition - Council of Europe / CEPEJ — *European Ethical Charter on the use of AI in judicial - systems* (2018). -- *South Buckinghamshire DC v Porter (No 2)* [2004] UKHL 33 (adequacy of reasons). - https://publications.parliament.uk/pa/ld200304/ldjudgmt/jd040701/south-1.htm -- חוק לתיקון סדרי המינהל (החלטות והנמקות), תשי"ט-1958. - https://www.nevo.co.il/law_html/law00/98603.htm -- Kevin D. Ashley — *Artificial Intelligence and Legal Analytics* (CUP). + systems* (2018, user-control principle). +- Federal Judicial Center — *Judicial Writing Manual* (2d ed.) — לעניין שיקול-הדעת + האנושי בכתיבה השיפוטית. + https://www.fjc.gov/content/judicial-writing-manual-pocket-guide-judges-second-edition **אחזור / RAG / IR** - Lewis et al. (2020) — *Retrieval-Augmented Generation* (NeurIPS). @@ -261,14 +269,5 @@ South Bucks) + מקור-חקיקה אחד שספציפיות-הסעיף שלו - E.F. Codd — Relational model & normalization (CACM 13(6), 1970). - Martin Fowler — Canonical Data Model / Data Mesh (quality-at-source). ---- - -## פריטים פתוחים — אימות-מקור-ראשוני נדרש - -(החוקר אימת מסגרת; הפריטים הישראליים דורשים אימות לפני ציטוט כ-סמכות, בשלב כתיבת -`04`/`05`/`X5`) - -1. מספר הסעיף המדויק בחוק לתיקון סדרי המינהל (החלטות והנמקות) תשי"ט-1958 (וכן תיקון - תשכ"ט-1969) — ⚠ UNVERIFIED, יאומת מול Nevo בקובץ [04-analysis-writing.md](04-analysis-writing.md). -2. ציטוט מדויק מ-ברק-ארז, *משפט מינהלי*. -3. אסמכתאות פסיקה: בג"ץ 143/56; עע"ם 2994/21 (מעמד ועדת ערר כגוף תכנוני-מקצועי). +(נספח המקורות מתייחס ל-invariants ההנדסיים G1–G10 בלבד. התוכן המשפטי — G11 — נשען על +מסמכי-הפרויקט וסמכות היו"ר, כמפורט ב-G11.) From da80bcf0fe24f6486b5e95a31c25974e3827fa12 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:42:26 +0000 Subject: [PATCH 07/30] docs(spec): 01-ingest unified intake contract Co-Authored-By: Claude Opus 4.8 --- docs/spec/01-ingest.md | 154 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 docs/spec/01-ingest.md diff --git a/docs/spec/01-ingest.md b/docs/spec/01-ingest.md new file mode 100644 index 0000000..11358ea --- /dev/null +++ b/docs/spec/01-ingest.md @@ -0,0 +1,154 @@ +# 01 — קליטה מאוחדת (Unified Ingest Contract) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומפרט את **חוזה הקליטה** של כל סוגי +ה-intake. הוא אוכף את [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) +(מקור-אמת יחיד, אין מסלולים מקבילים) ואת [G3](00-constitution.md#inv-g3-ingest-אחיד-ו-idempotent) +(ingest אחיד ו-idempotent), ונשען על [G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) +ו-[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן). + +כשל-השורש שהקובץ מייבש: **שני מסלולי ingest לישויות-אחיות שמתפצלים** — `ingest_precedent` +(פסיקה חיצונית) מול `ingest_internal_decision` (החלטות-ועדה). מסלולים מקבילים גוררים drift: +פריט שנקלט במסלול אחד מקבל טיפול שונה מפריט במסלול האחר, והפער מתגלה רק כשרשומה חסרה +metadata או לא נמצאת בחיפוש. החוזה כאן מגדיר **מסלול קנוני אחד** ש-3 סוגי ה-intake עוברים בו. + +--- + +## 1. שלושת סוגי ה-intake + +| סוג-intake | מזהה-קנוני | קורפוס-יעד | מאפיין ייחודי | +|------------|------------|------------|----------------| +| מסמכי-תיק (case documents) | `case_number` + מזהה-מסמך | תיק ערר פעיל | משויך לתיק, מסווג לפי סוג-מסמך | +| פסיקה חיצונית (external precedent) | `citation` (קנוני) | `case_law` (external) | staging לפי `source_type`, ולידציית-enums, citation guard, multimodal | +| החלטות-ועדה (internal-committee) | `case_number` (קנוני) | `case_law` (internal_committee) | staging לפי district, `chair_name` חובה, גזירת district/proceeding_type | + +שלושתם הם **ישויות-אחיות**: אותו טיפוס-עיבוד (קובץ → טקסט → chunks → embeddings → metadata +→ הלכות), נבדלים בפרמטרים בלבד — לא במסלול-קוד. זוהי משמעות "סימטריה" (חוקה §6). + +--- + +## 2. המסלול הקנוני (Canonical Pipeline) + +צעדי-העיבוד, **בסדר מחייב**. כל סוג-intake עובר את אותם צעדים; ההבדל הוא אילו פרמטרים +מוזרקים בקלט, לא אילו צעדים מורצים. + +1. **Stage file** — העתקה דטרמיניסטית לאחסון המתמיד. נתיב-ה-staging הוא פרמטר + (`source_type` לפסיקה חיצונית, district להחלטות-ועדה), לא ענף-קוד נפרד. +2. **Extract text** — `extractor.extract_text` → `(text, page_count, page_offsets)`. + טקסט ריק = כשל מדווח (לא בליעה שקטה; חוקה §6). +3. **Strip Nevo preamble** — `extractor.strip_nevo_preamble` להסרת עטיפת-Nevo. **אחיד לכל סוג.** +4. **Chunk** — היררכי (`chunk_document_hierarchical`) אם `PARENT_DOC_RETRIEVAL_ENABLED`, + אחרת שטוח (`chunk_document`). **אותו ענף-flag בדיוק לכל סוג** — בורר הצ'אנקינג נגזר + מ-config, לא מסוג-ה-intake. +5. **Embed** — `embeddings.embed_texts(..., input_type="document")` ל-children (היררכי) + או לכל ה-chunks (שטוח). +6. **Store chunks** — `store_precedent_chunks_hierarchical` או `store_precedent_chunks`. +7. **Page-image embed (multimodal)** — אם `MULTIMODAL_ENABLED` **וגם** הקובץ PDF + **וגם** `page_count>0`: הטמעת עמודי-תמונה (`_embed_precedent_pages`). non-fatal: + מסלול-הטקסט כבר הצליח. **התנאי אחיד** — הפעלה תלויה ב-flag+סוג-קובץ, לא בסוג-ה-intake. +8. **Queue metadata extraction** — `request_metadata_extraction(case_law_id)`. נדרש לכל + סוג שתומך במטא-דאטה (ראה [INV-ING3](#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג)). +9. **Queue halacha extraction** — `request_halacha_extraction(case_law_id)`. +10. **Set statuses** — `extraction_status=completed`, `halacha_status=pending`. + החילוץ ה-LLM-י (metadata + הלכות) רץ בנפרד מ-Claude Code המקומי + (`precedent_process_pending`), כי `claude` CLI אינו זמין בקונטיינר. + +> **צעדים שחייבים להיות אחידים בכל סוג (תיקון האסימטריה):** 2 (extract), 3 (strip-Nevo), +> 4 (בורר-chunk לפי flag), 5–6 (embed+store), **7 (multimodal — לפי flag+PDF, לא לפי +> סוג)**, **8–9 (תיזמון שני החילוצים)**, 10 (statuses). מה ש**רשאי** להשתנות לפי סוג: +> נתיב-ה-staging (צעד 1), ולידציות-קלט ספציפיות, וגזירת-שדות (district/proceeding_type) +> — אלו פרמטרים של אותו מסלול, לא מסלול נפרד. + +--- + +## 3. Invariants של התחום + +### INV-ING1: מסלול-קליטה קנוני יחיד +**כלל:** כל סוגי ה-intake (מסמכי-תיק / פסיקה חיצונית / החלטות-ועדה) זורמים דרך **פונקציית- +קליטה קנונית אחת**. סוג-intake חדש מורחב דרך **פרמטרים** של אותה פונקציה — לעולם לא דרך +פונקציה מקבילה. נתון-נגזר (district, proceeding_type) מחושב בתוך המסלול, לא בענף נפרד. +**מקורות:** Martin Kleppmann, *DDIA* (O'Reilly, 2017 — system of record יחיד) · Martin +Fowler (*Canonical Data Model*) · SSOT (Single Source of Truth) | סטטוס: verified +**אכיפה:** ביקורת-ארכיטקטורה + כלל-הנדסה "סימטריה" (חוקה §6); הקליטה מתנקזת לפונקציה אחת +שמקבלת פרמטרי-סוג. אוכף את [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים). +**הפרה ידועה:** היום קיימים **שני** מסלולים — `ingest_precedent` +(`precedent_library.py:88`) ו-`ingest_internal_decision` (`internal_decisions.py:73`) — +שמשכפלים את צעדי 2–10 ומתפצלים בפרטים → ממצא ל-[audit](../audit-report.md). + +### INV-ING2: קליטה idempotent על המזהה הקנוני +**כלל:** הקליטה היא **idempotent על המזהה הקנוני** (`citation` לפסיקה חיצונית, +`case_number` להחלטות-ועדה ולמסמכי-תיק). קליטה חוזרת של אותו פריט = **upsert** — +אין רשומה כפולה ואין chunks כפולים; התוצאה זהה. +**מקורות:** Martin Kleppmann, *DDIA* (idempotence & exactly-once) · Stripe / CDC +idempotency-key pattern · ISO 8000 (Data quality) | סטטוס: verified +**אכיפה:** מפתח-upsert דטרמיניסטי על המזהה הקנוני בנקודת-הקליטה (`create_external_case_law` +/ `create_internal_committee_decision`) + ולידציית-כתיבה; קשור ל- +[X1-identifiers.md](X1-identifiers.md) (נרמול בכתיבה). אוכף את +[G3](00-constitution.md#inv-g3-ingest-אחיד-ו-idempotent). +**הפרה ידועה:** 3 החלטות "סופר" נקלטו ב-3 פורמטים (`8126/24`, ציטוט-מלא כ-`case_number`) +— היעדר מפתח-upsert דטרמיניסטי גרר רשומות-כפל במקום עדכון → ממצא ל-[audit](../audit-report.md). + +### INV-ING3: תור חילוץ מטא-דאטה + הלכות לכל סוג +**כלל:** חילוץ-מטא-דאטה **וגם** חילוץ-הלכות מתוזמנים (queue) עבור **כל** סוג-intake שתומך +בהם — תיזמון אחיד, **לא** מותנה במסלול. שני התורים נפתחים יחד בסיום העיבוד הלא-LLM-י. +**מקורות:** ISO 8000 (completeness) · DAMA-UK *Six Primary Dimensions for Data Quality* +(2013, completeness) · Martin Fowler (quality-at-source) | סטטוס: verified +**אכיפה:** קריאה ל-`request_metadata_extraction` **ו**-`request_halacha_extraction` +בנקודת-סיום-הקליטה, לכל סוג; חוזה-שלמות יסמן רשומה ללא מטא-דאטה כלא-שמישה +([G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש), מפורט ב- +[02-data-model.md](02-data-model.md)). +**הפרה ידועה:** המסלול הפנימי (`internal_decisions.py:208`) מתזמן **רק** +`request_halacha_extraction` ואינו קורא ל-`request_metadata_extraction` (בניגוד +ל-`precedent_library.py:292-293` שקורא לשניהם) → ערן סופר 8046/24 נקלטה **בלי +metadata** (headnote/summary/tags ריקים) → ממצא ל-[audit](../audit-report.md). + +### INV-ING4: re-index בקליטה-חוזרת (upsert ⇒ re-embed) +**כלל:** קליטה-חוזרת ששינתה את תוכן-הפריט מפעילה **re-index** — chunks ו-embeddings +ישנים נמחקים ונבנים מחדש מהתוכן החדש. אין embeddings מיושנים אחרי upsert. +**מקורות:** Pinecone (index freshness / data sync) · Weaviate (re-vectorization on update) +· RAG freshness (Lewis et al., 2020, NeurIPS) | סטטוס: verified +**אכיפה:** טריגר re-embed בנתיב ה-upsert של הקליטה + בדיקת-בריאות לגילוי drift; מפורט +ב-[02-data-model.md](02-data-model.md) ו-[03-retrieval.md](03-retrieval.md). אוכף את +[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן). +**הפרה ידועה:** — + +--- + +## 4. מצב קיים מול יעד — audit-findings + +הסעיף מתעד את ההבדלים בין שני המסלולים הקיימים. **אלו תסמינים לאיחוד תחת המסלול הקנוני, +לא התנהגויות תקינות.** כל פריט אומת מול הקוד בפועל. + +- **חילוץ מטא-דאטה חסר במסלול הפנימי.** `ingest_precedent` קורא ל- + `request_metadata_extraction` **ו**-`request_halacha_extraction` + (`precedent_library.py:292-293`); `ingest_internal_decision` קורא **רק** ל- + `request_halacha_extraction` (`internal_decisions.py:208`) — אין `request_metadata_extraction`. + זו ההפרה הקונקרטית של [INV-ING3](#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג) (ערן סופר 8046/24). + **יעד:** צעד 8 אחיד לשני הסוגים. +- **ולידציית-enums א-סימטרית.** המסלול החיצוני מוודא `practice_area`/`source_type` מול + רשימות חוקיות (`precedent_library.py:131-134`); המסלול הפנימי **אינו** מוודא enums. + **יעד:** ולידציה אחידה בנקודת-הקליטה (חוזה-שלמות, [G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש)). +- **staging מפוצל.** החיצוני עושה stage לפי `source_type` (`precedent_library.py:138`); + הפנימי עושה stage לפי district (`internal_decisions.py:113-115`). **יעד:** נתיב-staging + כפרמטר של המסלול הקנוני (צעד 1), לא ענף-קוד. +- **גזירת-שדות רק במסלול הפנימי.** הפנימי גוזר district מ-court (`:104`) ו-proceeding_type + מ-appeal_subtype/case_name (`:105`), ודורש `chair_name` (`:134`). החיצוני אינו גוזר אלו. + **יעד:** גזירה כפרמטר אופציונלי של המסלול הקנוני (שדות-סוג, לא מסלול-סוג). +- **citation guard רק במסלול החיצוני.** החיצוני חוסם ציטוט שמתחיל ב-`ערר`/`בל"מ` + ומפנה למסלול הפנימי (`precedent_library.py:124-130`). היעד שומר על השער הזה כניתוב-סוג + בתוך המסלול הקנוני, לא כהפרדת-פונקציות. +- **multimodal page-image embed רק במסלול החיצוני.** החיצוני מטמיע עמודי-תמונה כש- + `MULTIMODAL_ENABLED` + PDF (`precedent_library.py:272-278`); הפנימי **אינו** מטמיע + עמודי-תמונה. **יעד:** צעד 7 אחיד — מותנה ב-flag+סוג-קובץ בלבד. +- **fallback `case_name→citation` רק במסלול החיצוני.** החיצוני נופל ל-`citation` כשם + כשחסר `case_name` (`precedent_library.py:158`); הפנימי נופל ל-`case_number` + (`internal_decisions.py:130`). **יעד:** מדיניות-fallback אחת לשם-תצוגה במסלול הקנוני. + +--- + +## 5. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — invariants גלובליים + כללי-הנדסה. +- [02-data-model.md](02-data-model.md) — סכמת-האחסון + חוזה-שלמות שאוכף את תוצרי הקליטה. +- [03-retrieval.md](03-retrieval.md) — אחזור, re-index, eval — היעד של ה-chunks הנקלטים. +- [X1-identifiers.md](X1-identifiers.md) — נרמול המזהה הקנוני בכתיבה (בסיס ל-INV-ING2). +- [X5-audit-provenance.md](X5-audit-provenance.md) — שלמות-רשומה + עקיבוּת-מקור של פריט נקלט. From 9199214b7cd77e84f182e3bd7a5327cb7db1f5e7 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:46:23 +0000 Subject: [PATCH 08/30] =?UTF-8?q?docs(spec):=2001-ingest=20=E2=80=94=20tri?= =?UTF-8?q?m=20=C2=A74=20redundancy=20(reference=20INV-ING3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- docs/spec/01-ingest.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/spec/01-ingest.md b/docs/spec/01-ingest.md index 11358ea..898cdcc 100644 --- a/docs/spec/01-ingest.md +++ b/docs/spec/01-ingest.md @@ -118,12 +118,8 @@ metadata** (headnote/summary/tags ריקים) → ממצא ל-[audit](../audit-r הסעיף מתעד את ההבדלים בין שני המסלולים הקיימים. **אלו תסמינים לאיחוד תחת המסלול הקנוני, לא התנהגויות תקינות.** כל פריט אומת מול הקוד בפועל. -- **חילוץ מטא-דאטה חסר במסלול הפנימי.** `ingest_precedent` קורא ל- - `request_metadata_extraction` **ו**-`request_halacha_extraction` - (`precedent_library.py:292-293`); `ingest_internal_decision` קורא **רק** ל- - `request_halacha_extraction` (`internal_decisions.py:208`) — אין `request_metadata_extraction`. - זו ההפרה הקונקרטית של [INV-ING3](#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג) (ערן סופר 8046/24). - **יעד:** צעד 8 אחיד לשני הסוגים. +- **חילוץ מטא-דאטה חסר במסלול הפנימי.** ראה [INV-ING3](#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג) + (ההפרה המתועדת שם — ערן סופר 8046/24). **יעד:** צעד 8 (תור חילוץ) אחיד לשני הסוגים. - **ולידציית-enums א-סימטרית.** המסלול החיצוני מוודא `practice_area`/`source_type` מול רשימות חוקיות (`precedent_library.py:131-134`); המסלול הפנימי **אינו** מוודא enums. **יעד:** ולידציה אחידה בנקודת-הקליטה (חוזה-שלמות, [G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש)). From 998194462f7c2484ccc7c144f11240f482ea2349 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:50:06 +0000 Subject: [PATCH 09/30] docs(spec): 02-data-model entities + completeness contract Co-Authored-By: Claude Opus 4.8 --- docs/spec/02-data-model.md | 155 +++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 docs/spec/02-data-model.md diff --git a/docs/spec/02-data-model.md b/docs/spec/02-data-model.md new file mode 100644 index 0000000..c8c3e9e --- /dev/null +++ b/docs/spec/02-data-model.md @@ -0,0 +1,155 @@ +# 02 — מודל-הנתונים (Data Model & Completeness Contract) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומגדיר את **מודל-הנתונים הקנוני (TARGET)** +של עוזר משפטי — הישויות, שדות-המפתח, והיכן יושב כל פריט מואנדקס. הוא אוכף את +[G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה) (מזהה קנוני יחיד), +[G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) (חוזה-שלמות) ו- +[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן) (re-index בשינוי-תוכן). + +> **TARGET, לא תיאור-מצב.** המודל כאן הוא היעד הקנוני. כל מקום שבו ה-schema בפועל +> (`mcp-server/src/legal_mcp/services/db.py`) סוטה ממנו — מתועד כ-**audit-finding** (§4), +> תסמין לאיחוד, לא התנהגות תקינה. כל טענה על ה-schema הקיים מצוטטת `file:line`. + +--- + +## 1. הישויות הקנוניות + +הטבלה מונה את ישויות-הליבה. "מזהה-קנוני" = השדה היחיד המזהה רשומה ([G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה)). + +| ישות | תפקיד | מזהה-קנוני | שדות-מפתח (מאומתים `db.py`) | +|------|--------|-------------|------------------------------| +| `cases` | תיק ערר חי (1xxx/8xxx/9xxx) | `case_number` + `proceeding_type` | `title`, `status`, `practice_area`, `appeal_subtype`, `proceeding_type`, `chair_name` (`db.py:74-91,182-189,747,912`) | +| `documents` | מסמך-מקור משויך לתיק | `id` (UUID); FK→`cases` | `doc_type`, `title`, `file_path`, `extracted_text`, `extraction_status`, `page_count` (`db.py:93-104`) | +| `document_chunks` | chunk של מסמך-תיק + embedding | `id`; FK→`documents`/`cases` | `chunk_index`, `content`, `section_type`, `embedding vector(1024)`, `page_number` (`db.py:106-116`) | +| `case_law` | קורפוס פסיקה — חיצוני **וגם** החלטות-ועדה | ראה [§2 + INV-DM2](#inv-dm2-מזהה-קנוני-יחיד-לכל-ישות) | `case_name`, `court`, `practice_area`, `source_kind`, `proceeding_type`, `source_type`, `headnote`, `summary`, `subject_tags`, `extraction_status`, `halacha_extraction_status` (`db.py:366-378,522-526,599-611,883,907`) | +| `precedent_chunks` | chunk של פסק-דין מואנדקס (`source_kind='external_upload'`/`internal_committee`) | `id`; FK→`case_law` | `chunk_index`, `content`, `section_type`, `page_number`, `embedding vector(1024)`, `content_tsv` (`db.py:624-634,776`) | +| `halachot` | הלכה מחולצת — כלל + ציטוט מילולי | `id`; FK→`case_law` | `rule_statement`, `supporting_quote`, `rule_type`, `practice_areas`, `subject_tags`, `confidence`, `quote_verified`, `review_status`, `embedding`, `rule_tsv` (`db.py:644-666,780`) | +| `decisions` | החלטת-תיק מנוסחת (גרסה) | `id`; `UNIQUE(case_id, version)` | `version`, `status`, `outcome`, `outcome_summary` (`db.py:299-314`) | +| `decision_blocks` | בלוק (12) של החלטה | `id`; `UNIQUE(decision_id, block_id)` | `block_id`, `block_index`, `content`, `status` (`db.py:317-334`) | +| `claims` | טענת-צד (בלוק ז) | `id`; FK→`cases` | `party_role`, `claim_text`, `source_document`, `claim_type`, `claim_handling` (`db.py:349-359,506-512`) | +| `chair_feedback` | הערת-יו"ר על טיוטה | `id`; FK→`cases` | `block_id`, `feedback_text`, `category`, `lesson_extracted`, `resolved` (`db.py:452-462`) | +| `missing_precedents` | תקדים חסר שהתבקש ולא נמצא | `id` | (`db.py:806`) — backlog ל-quality-at-source | +| `style_corpus` | קורפוס-סגנון של דפנה (אימון) | `id`; FK→`documents` | `decision_number`, `full_text`, `practice_area`, `appeal_subtype` (`db.py:118-131`) | + +> שכבות-עזר נוספות (`document_image_embeddings`, `precedent_image_embeddings` — multimodal, +> `db.py:707,726`; `case_law_relations` — שרשרת-תיק, `db.py:754`; `precedent_internal_citations` +> — גרף-ציטוטים, `db.py:937`) הן נגזרות ([G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים)): +> משוחזרות מהמקור, לא מקור-אמת עצמאי. + +--- + +## 2. חוזה-שלמות לכל ישות (Completeness Contract) + +[G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) דורש: **רשומה אינה "שמישה / +ניתנת-לחיפוש" עד ששדות-החובה שלה מולאו ואומתו מול spec מפורש.** כל ישות מגדירה שתי רמות — +**usable** (קיימת ומזוהה) ו-**searchable** (חשופה לאחזור). רשומה שנכשלת בחוזה **מסומנת +ומדווחת — לא מתקבלת בשקט** (חוקה §6, "אין בליעה שקטה"). + +### 2א. `case_law` — החוזה הקונקרטי + +המזהה הקנוני אינו `case_number` לבדו: `case_law` נושאת **שני** unique partial indexes לפי +`source_kind` (`db.py:904-909`) — חיצוני: `UNIQUE(case_number)`; פנימי: `UNIQUE(case_number, +proceeding_type)`. לכן המזהה הקנוני הוא **(`case_number` מנורמל, `source_kind`, +`proceeding_type`)**. + +**רמת usable** (רשומה לגיטימית): +- `case_number` קנוני מנורמל-בכתיבה ([INV-DM2](#inv-dm2-מזהה-קנוני-יחיד-לכל-ישות) — **לא** ציטוט-מלא) +- `case_name` לא-ריק (לא fallback לציטוט/למספר) +- `court` לא-ריק +- `practice_area ∈ {rishuy_uvniya, betterment_levy, compensation_197}` (אכוף ב-CHECK, `db.py:614-617`) +- `source_kind` מהמילון (`external_upload` / `cited_only` / `internal_committee` / `nevo_seed`) (`db.py:599-601`, `internal_decisions.py:4`) +- `proceeding_type ∈ {ערר, בל"מ}` כשפנימי (אכוף ב-CHECK, `db.py:897-899`) + +**רמת searchable** (חשוף לאחזור — מעבר ל-usable): +- **≥1 `precedent_chunk`** עם `embedding` לא-NULL (אחרת אין מה לאחזר סמנטית) +- **metadata לא-ריק:** לפחות אחד מ-`headnote` / `summary` / `subject_tags` מלא — אלו השדות + ש-search מציג ומסנן לפיהם +- `extraction_status = completed` (מטא-דאטה הושלם, `db.py:603`) + +**אכיפה מפורשת:** רשומה שעוברת usable אך נכשלת ב-searchable — **מסומנת `searchable=false` +ולא מוחזרת מ-search**, ומופיעה ב-health-check כ-backlog. היא **אינה מתקבלת בשקט** כ"זמינה". + +### 2ב. חוזה תמציתי לישויות נוספות + +- `documents` → usable: `file_path`+`doc_type`; searchable: `extraction_status=completed` ו-`extracted_text` לא-ריק ו-≥1 `document_chunk` עם embedding. +- `halachot` → usable: `rule_statement`+`supporting_quote`; **searchable: `review_status ∈ {approved, published}` בלבד** — `pending_review`/`rejected` מוסתרות מ-`search_precedent_library` (שער-הלכה ידני, `db.py:644-660`, [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant)). +- `decision_blocks` → usable: `block_id`∈12-הבלוקים; "מוכן": `status=final` ו-`content` לא-ריק. +- `chair_feedback` → usable: `feedback_text`+`category` מהמילון; "פתוח" עד `resolved=true`. + +--- + +## 3. Invariants של התחום + +### INV-DM1: searchable רק כשחוזה-השלמות מתקיים +**כלל:** רשומת `case_law` נחשבת **searchable** אך ורק כשחוזה-השלמות של [§2א](#2א-case_law--החוזה-הקונקרטי) +מתקיים במלואו (מזהה קנוני · `case_name`/`court`/`practice_area`/`source_kind` · ≥1 chunk עם +embedding · metadata לא-ריק). רשומה שנכשלת **מסומנת `searchable=false` ומדווחת ל-health-check — +לא מוחזרת מ-search ולא מתקבלת בשקט**. +**מקורות:** ISO 8000 (completeness) · DAMA-UK *Six Primary Dimensions for Data Quality* (2013, +completeness) · ISO 15489-1:2016 (records reliability/usability) | סטטוס: verified +**אכיפה:** ולידציית-כתיבה בנקודת-הקליטה ([01-ingest.md](01-ingest.md) צעד 8) + בדיקת-בריאות +תקופתית שמסמנת backlog; הסינון נאכף בשכבת-החיפוש ([03-retrieval.md](03-retrieval.md)). אוכף את +[G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש). +**הפרה ידועה:** ערן סופר 8046/24 אונדקס כ-searchable עם `headnote`/`summary`/`subject_tags` +ריקים — המסלול הפנימי לא תיזמן חילוץ-מטא-דאטה ([01-ingest INV-ING3](01-ingest.md#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג), +`internal_decisions.py:208`) → ממצא ל-[audit](../audit-report.md). + +### INV-DM2: מזהה קנוני יחיד לכל ישות +**כלל:** לכל ישות **מזהה קנוני אחד**, מנורמל בכתיבה. **אסור** ששדה-המזהה יאחסן ציטוט-מלא — +`case_number` הוא מספר-תיק מנורמל (`8126-03-25`), **לא** מחרוזת-ציטוט (`ערר 8126/24 פלוני נ' הוועדה +(נבו...)`). הציטוט המלא חי בשדה ייעודי נפרד (`citation_formatted`, `db.py:1070`), לא במזהה. +**מקורות:** SSOT (Single Source of Truth — normalization) · E.F. Codd, First Normal Form (CACM +13(6), 1970) · Martin Kleppmann, *Designing Data-Intensive Applications* (O'Reilly, 2017) | סטטוס: verified +**אכיפה:** unique partial indexes על המזהה הקנוני (`db.py:904-909`) + נרמול-בכתיבה +([X1-identifiers.md](X1-identifiers.md)); ציטוט-מלא ב-`citation_formatted` בלבד. אוכף את +[G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה). +**הפרה ידועה:** החלטות "סופר" נקלטו עם **ציטוט-מלא כ-`case_number`** (שדה-המזהה של רשומה מכיל את +מחרוזת-הציטוט במקום מספר-תיק מנורמל) — חיפוש מול `8126-03-25` נכשל, ו-`_normalize_case_number` +(`db.py:1196-1211`) רק **מטליא בקריאה** (סלחני, לא קנוני), בניגוד ל-[G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה) +→ ממצא ל-[audit](../audit-report.md). + +### INV-DM3: שינוי-תוכן ⇒ re-index +**כלל:** כל שינוי בתוכן-המקור של ישות מואנדקסת (`content` של chunk, `rule_statement`/`supporting_quote` +של הלכה, `full_text`/`extracted_text` של מסמך) מפעיל **re-index** של ה-embedding **ושל +ה-tsvector** הנגזרים. אין embedding או `content_tsv`/`rule_tsv`/`meta_tsv` מיושנים מול התוכן. +**מקורות:** Pinecone (index freshness / data sync) · Weaviate (re-vectorization on update) · +RAG freshness (Lewis et al., 2020, NeurIPS) | סטטוס: verified +**אכיפה:** טריגר re-embed בנקודת-העדכון + בדיקת-בריאות לגילוי drift; ה-tsvectors `GENERATED ALWAYS +… STORED` (`db.py:776-788,1083-1090`) מתעדכנים אוטומטית, אך ה-`embedding` **אינו** generated — +הוא תלוי-טריגר. מפורט ב-[03-retrieval.md](03-retrieval.md). אוכף את +[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן). +**הפרה ידועה:** — + +--- + +## 4. מצב קיים מול יעד — audit-findings + +ההבדלים בין ה-schema בפועל ל-TARGET. **אלו תסמינים, לא התנהגויות תקינות.** כל פריט אומת מול `db.py`. + +- **`case_law` כפולת-תפקיד ללא מזהה מודע-סוג בכתיבה.** טבלה אחת משרתת פסיקה חיצונית **וגם** + החלטות-ועדה, מובדלות ב-`source_kind` (`db.py:599`). המזהה הקנוני האמיתי הוא טריפלט + (`case_number, source_kind, proceeding_type`, `db.py:904-909`), אך השדה `case_number TEXT + UNIQUE NOT NULL` המקורי (`db.py:368`) הוסר רק ב-V15 (`db.py:902-903`) — מורשת שאפשרה את + הפרת [INV-DM2](#inv-dm2-מזהה-קנוני-יחיד-לכל-ישות). **יעד:** נרמול-בכתיבה אכוף + ציטוט-מלא רק ב-`citation_formatted`. +- **`summary` קיים על `case_law` אך לא בחוזה-הקליטה הפנימי.** העמודה קיימת (`db.py:373`) אך + המסלול הפנימי אינו ממלא אותה (כפועל-יוצא מהיעדר חילוץ-מטא-דאטה, [INV-ING3](01-ingest.md#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג)). + **יעד:** searchable מותנה ב-metadata לא-ריק ([INV-DM1](#inv-dm1-searchable-רק-כשחוזה-השלמות-מתקיים)). +- **שני שדות-סטטוס-חילוץ נפרדים, ללא דגל-`searchable` מפורש.** `extraction_status` + + `halacha_extraction_status` (`db.py:603-605`) מתארים תהליך, אך אין שדה יחיד שמסמן "עבר + חוזה-שלמות → searchable". **יעד:** דגל/view נגזר ש-search מסנן לפיו, מגובה health-check. +- **`embedding` אינו `GENERATED` (בניגוד ל-tsvector).** ה-tsvectors מסונכרנים אוטומטית + (`db.py:776,780,1083`), אך ה-`embedding vector(1024)` תלוי-טריגר חיצוני — נקודת-drift אפשרית + ל-[INV-DM3](#inv-dm3-שינוי-תוכן--re-index). **יעד:** טריגר re-embed מובטח + health-check ל-drift. +- **`halachot.review_status` כשער-searchable ללא נראות-backlog.** הסינון תקין (`pending_review` + מוסתר, `db.py:659`), אך אין נראות כמה ממתינות — תואם את ההפרה הידועה ב-[G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) + (10/19 מאושרות, התגלה במקרה). **יעד:** health-check חושף backlog-הלכות. + +--- + +## 5. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — invariants גלובליים (G1, G4, G6) + כללי-הנדסה. +- [01-ingest.md](01-ingest.md) — חוזה-הקליטה שמייצר את הרשומות; חוזה-השלמות כאן אוכף את תוצריו. +- [03-retrieval.md](03-retrieval.md) — שכבת-האחזור שאוכפת את הסינון searchable + re-index. +- [X1-identifiers.md](X1-identifiers.md) — נרמול המזהה הקנוני בכתיבה (בסיס ל-INV-DM2). +- [X5-audit-provenance.md](X5-audit-provenance.md) — שלמות-רשומה + עקיבוּת-מקור. From 72737df154b0af3670c824b96154c540df6b939f Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 14:57:11 +0000 Subject: [PATCH 10/30] docs(spec): 03-retrieval corpora + retrieval invariants Co-Authored-By: Claude Opus 4.8 --- docs/spec/03-retrieval.md | 178 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 docs/spec/03-retrieval.md diff --git a/docs/spec/03-retrieval.md b/docs/spec/03-retrieval.md new file mode 100644 index 0000000..b054d67 --- /dev/null +++ b/docs/spec/03-retrieval.md @@ -0,0 +1,178 @@ +# 03 — אחזור (Retrieval: Corpora · Hybrid/RRF · Attribution · Eval) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומגדיר את **שכבת-האחזור הקנונית (TARGET)** — +שלושת הקורפוסים, כלי-החיפוש המכוונים לכל אחד, מנגנון ה-hybrid (dense + lexical) ומיזוג ה-RRF, +עקיבוּת-המקור והרמוניית-המדידה. הוא אוכף את +[G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) (חוזה-שלמות לפני "ניתן-לחיפוש"), +[G5](00-constitution.md#inv-g5-metadata-מלא--הפרדת-קורפוס-נאכפת-בכל-query) (הפרדת-קורפוס בכל query), +[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן) (re-index), +[G7](00-constitution.md#inv-g7-מיזוג-rrf--לא-סכום-ציונים) (מיזוג RRF), +[G8](00-constitution.md#inv-g8-איכות-אחזור-נמדדת--precision--recall) (eval) ו- +[G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai) (עקיבוּת-מקור). + +> **TARGET, לא תיאור-מצב.** כל מקום שבו הקוד בפועל סוטה מהיעד מתועד כ-**audit-finding** (§5), +> תסמין לתיקון — לא התנהגות תקינה. כל טענה על הקוד מצוטטת `file:line`. + +כשל-השורש שהקובץ מייבש: **3 קורפוסים שחולקים תשתית-אחזור אחת, אך הפרדת-הקורפוס נאכפת רק על +חלק ממסלולי-ה-query** — כך שפריט מקורפוס אחד דולף לתוצאה של חיפוש בקורפוס אחר (cross-corpus leak). + +--- + +## 1. שלושת הקורפוסים וכלי-החיפוש + +| קורפוס | טבלת-אחסון | `source_kind` | כלי-MCP מכוון | אימות `file:line` | +|--------|------------|----------------|----------------|--------------------| +| מסמכי-תיק + קורפוס-סגנון דפנה | `document_chunks` | — (מובחן ב-`case_id`/`practice_area`) | `search_decisions` · `search_case_documents` · `find_similar_cases` | `search.py:15,91,145` → `hybrid_search.py:41` (`search_documents_hybrid`) → `db.search_similar` (`hybrid_search.py:56`) | +| פסיקה חיצונית סמכותית | `case_law` + `precedent_chunks`/`halachot` | `external_upload` | `search_precedent_library` | `search.py`→`precedent_library.py:235` → `search_library` → `hybrid_search.py:89,101` (`source_kind="external_upload"`) | +| החלטות ועדות-ערר (פנימי) | `case_law` + `precedent_chunks`/`halachot` | `internal_committee` | `search_internal_decisions` | `search.py:228` → `internal_decisions.py:395,411-418` (`source_kind="internal_committee"`) → `hybrid_search.py:89` | + +**הבחנת-שם קריטית (לא קורפוס רביעי):** `precedent_search_library` (`server.py:160`) הוא כלי **שונה** — +מחפש בציטוטים שהיו"ר צירפה ידנית לתיקים (`case_precedents`), לא בקורפוס הפסיקה הסמכותית. +`search_precedent_library` (`server.py:280`) הוא הכלי לקורפוס החיצוני. אל תבלבל ביניהם. + +הקורפוס החיצוני והפנימי **חולקים טבלה אחת** (`case_law`), מובחנים ב-`source_kind` בלבד +([02-data-model §2א](02-data-model.md#2א-case_law--החוזה-הקונקרטי)). שניהם רצים דרך **אותן** פונקציות-DB +(`search_precedent_library_semantic`/`_lexical`) — לכן הפרדת-הקורפוס היא **תנאי-סינון בתוך אותה שאילתה**, +ושם נולדת ההפרה ב-§5. + +--- + +## 2. עיצוב ה-hybrid retrieval + +לכל קורפוס שני retrievers הטרוגניים המאוחים ב-RRF, ולא בסכום-ציונים — ראה [INV-RET3](#inv-ret3-מיזוג-retrievers-הטרוגניים-ב-rrf-בלבד): + +1. **Dense (semantic)** — דמיון-קוסינוס מול `embedding vector(1024)` (voyage). פסיקה: + `search_precedent_library_semantic` (`db.py:3143`); מסמכי-תיק: `db.search_similar`. +2. **Lexical (BM25-style)** — `ts_rank_cd` מול `content_tsv`/`rule_tsv`/`meta_tsv` (Postgres FTS). + פסיקה: `search_precedent_library_lexical` (`db.py:3366`). מופעל כש-`BM25_HYBRID_ENABLED` + (`hybrid_search.py:139`). +3. **מיזוג sem+lex** — `_merge_sem_lex` (`hybrid_search.py:240-308`), נוסחת + `rrf_score = 1/(k+sem_rank) + 1/(k+lex_rank)` (`hybrid_search.py:256`). +4. **שכבת-multimodal (אופציונלית)** — כש-`MULTIMODAL_ENABLED`, עמודי-תמונה (voyage-multimodal-3) + מאוחים לטקסט ב-RRF נפרד: `_merge` (`hybrid_search.py:311-389`), `text_weight/(k+rank) + + img_weight/(k+rank)` (`hybrid_search.py:356-357`). +5. **Diversity cap (MMR-style)** — `_diversify_by_case_law` (`hybrid_search.py:196-225`): לכל היותר + `max_per_case_law` hits לכל `case_law_id`, כדי שפסק-דין יחיד לא ישתלט על הרשימה. + +> **למה RRF ולא סכום משוקלל:** קוסינוס (~0.4–0.7) ו-`ts_rank_cd` (~0.001–0.5, תלוי-אורך-שאילתה) +> חיים בסקיילים שונים — סכום משוקלל היה נותן לצד אחד להשתלט במקרה. RRF מאחד **לפי דירוג**, ולכן +> עמיד להבדלי-סקייל (`hybrid_search.py:248-252,319-323`). תואם feedback קיים (RRF, לא weighted-sum). + +--- + +## 3. Invariants של התחום + +### INV-RET1: הפרדת-קורפוס נאכפת ב-100% ממסלולי-ה-query +**כלל:** הפרדת 3 הקורפוסים נאכפת בכל מסלול-אחזור — **גם בסינון ה-chunks וגם בסינון ההלכות**. +אין פריט מקורפוס אחד שמופיע בתוצאת חיפוש שכוון לקורפוס אחר. כל ענף-SQL (semantic/lexical, +chunks/halachot) נושא את אותו תנאי-`source_kind`. +**מקורות:** Pinecone — *Implement multitenancy* (metadata-filter isolation per tenant) · RAG +attribution (Lewis et al., 2020, NeurIPS — pinned non-leaking provenance) · ISO 8000 (Data +quality / conformance) | סטטוס: verified +**אכיפה:** תנאי-`source_kind` בכל ענף-SQL בשכבת-החיפוש; בדיקת-בריאות שמריצה שאילתת-ביקורת +(חיפוש מכוון-קורפוס שמחזיר פריט בעל `source_kind` זר = כשל). אוכף את +[G5](00-constitution.md#inv-g5-metadata-מלא--הפרדת-קורפוס-נאכפת-בכל-query). +**הפרה ידועה:** משימה #56 — `halacha_filters` **אינם** כוללים `cl.source_kind` ב- +`search_precedent_library_semantic` (`db.py:3168`, ענף ה-halacha; לעומת `chunk_filters` שכן — +`db.py:3169`) **וב**-`search_precedent_library_lexical` (`db.py:3401` מול `db.py:3402`). שני +ה-`halacha_sql` עושים `JOIN case_law cl` בלי לסנן `source_kind` (`db.py:3236-3238`, `db.py:3475-3477`) +→ הלכות מהקורפוס הפנימי דולפות לתוצאות החיפוש בקורפוס החיצוני ולהפך → ממצא ל-[audit](../audit-report.md). + +### INV-RET2: אין החזרה/אינדוקס בלי metadata מלא + locator פתיר +**כלל:** פריט אינו מוחזר מ-search (ואינו נחשף לאחזור) אלא אם **שדות-החובה שלו מולאו** +([G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש)) **ובידו locator פתיר למקור** +(`case_law_id`/`document_id` + מזהה-עמוד/chunk). רשומה ללא metadata לא-ריק או ללא chunk עם +embedding מסומנת `searchable=false` ולא מוחזרת ([02-data-model INV-DM1](02-data-model.md#inv-dm1-searchable-רק-כשחוזה-השלמות-מתקיים)). +**מקורות:** Pinecone (metadata filtering — completeness לפני שליפה) · RAG attribution (Lewis et +al., 2020) · ISO 8000 (completeness) | סטטוס: verified +**אכיפה:** חוזה-שלמות בנקודת-הקליטה ([02-data-model §2](02-data-model.md#2-חוזה-שלמות-לכל-ישות-completeness-contract)) ++ סינון בשכבת-החיפוש (`embedding IS NOT NULL`, `db.py:3239,3271`; `length(trim(content))>=50`, +`db.py:3274`) + בדיקת-בריאות שחושפת backlog. אוכף את +[G5](00-constitution.md#inv-g5-metadata-מלא--הפרדת-קורפוס-נאכפת-בכל-query). +**הפרה ידועה:** ערן סופר 8046/24 — נקלטה בלי metadata (headnote/summary/tags ריקים), היעדר +תיזמון חילוץ-מטא-דאטה במסלול הפנימי ([01-ingest INV-ING3](01-ingest.md#inv-ing3-תור-חילוץ-מטא-דאטה--הלכות-לכל-סוג)), +אך ללא דגל-`searchable` מפורש שימנע את חשיפתה לאחזור → ממצא ל-[audit](../audit-report.md). + +### INV-RET3: מיזוג retrievers הטרוגניים ב-RRF בלבד +**כלל:** מיזוג תוצאות בין retrievers שונים (semantic↔lexical, text↔image) נעשה **אך ורק +לפי דירוג (Reciprocal Rank Fusion)** — לעולם לא סכום/ממוצע ציונים גולמיים, שכן ציונים בסקיילים +שונים אינם בני-השוואה ישירה. +**מקורות:** Elastic — *Reciprocal Rank Fusion* · Weaviate — *Hybrid Search Explained* · Manning, +Raghavan & Schütze, *Introduction to Information Retrieval* (CUP, 2008) | סטטוס: verified +**אכיפה:** מיזוג sem+lex ב-`_merge_sem_lex` (`hybrid_search.py:240-308`, נוסחה ב-`:256`) ומיזוג +text+image ב-`_merge` (`hybrid_search.py:311-389`, נוסחה ב-`:356-357`), שניהם עם +`k = MULTIMODAL_RRF_K`. אוכף את [G7](00-constitution.md#inv-g7-מיזוג-rrf--לא-סכום-ציונים). +**מצב:** **כבר ממומש** (codify, לא gap) — הקוד הקיים מיישם RRF נכון בשני המיזוגים. ה-invariant +מקבע את ההתנהגות הקיימת כחוזה. **הפרה ידועה:** — + +### INV-RET4: איכות-אחזור נמדדת ב-eval harness עומד (precision + recall) +**כלל:** איכות-האחזור **נמדדת אמפירית** — precision **ו**-recall — מול **סט-שאילתות מתויג קבוע** +(labeled query set) ב-eval harness עומד. כל שינוי בשכבת-האחזור (משקלי-RRF, `k`, סף-chunk, embedder) +מלווה במדידה לפני/אחרי; אין כוונון "לפי תחושה". +**מקורות:** Manning, Raghavan & Schütze, *Introduction to Information Retrieval* (CUP, 2008 — fixed +relevance judgments, precision/recall) · RAG evaluation literature (Lewis et al., 2020 ואחריו) · +Elastic — *relevance evaluation guidance* | סטטוס: verified +**אכיפה:** eval harness עם gold-set מתויג + בדיקת-בריאות תקופתית; שער-CI על שינוי שכבת-האחזור. +אוכף את [G8](00-constitution.md#inv-g8-איכות-אחזור-נמדדת--precision--recall). +**הפרה ידועה (GAP):** אין כיום eval harness ולא gold-set — קיים רק `telemetry.log_search_bg` +(`search.py:62,118,190,271`; `precedent_library.py:280`) שמתעד שאילתות בפועל, אך **אינו מודד +precision/recall מול תיוג** (תצפית, לא הערכה). היעד: harness שמריץ סט קבוע ומחזיר metrics → +ממצא ל-[audit](../audit-report.md). + +### INV-RET5: כל span מוחזר עקיב למקורו +**כלל:** כל קטע מוחזר נושא **עקיבוּת-מקור מלאה** — מזהה-מסמך/פסק-דין (`case_law_id`/`document_id`/ +`case_number`) **ו**-locator בתוכו (`page_number` / `chunk_id` / `supporting_quote` להלכה). פלט +ללא ייחוס פתיר אינו תקין; היו"ר חייבת לאמת כל ציטוט מול מקורו. +**מקורות:** Council of Europe / CEPEJ — *European Ethical Charter on AI in judicial systems* +(2018, traceability) · RAG attribution (Lewis et al., 2020) · ISO 15489-1:2016 (records +authenticity/integrity) | סטטוס: verified +**אכיפה:** כל פורמטר-תוצאה כולל מזהה + locator: `search.py:77-86` (case_number/page/section), +`_format_internal_row` (`search.py:322-343`: case_number/case_name/court + content/page או +rule/quote להלכה). עקיבוּת מלאה מפורטת ב-[X5-audit-provenance.md](X5-audit-provenance.md). אוכף את +[G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai). +**הפרה ידועה:** — + +--- + +## 4. re-index ושינוי-תוכן (G6) + +האחזור מסתמך על embeddings מסונכרנים מול התוכן. ה-tsvectors (`content_tsv`/`rule_tsv`/`meta_tsv`) +הם `GENERATED ALWAYS … STORED` (`db.py:778,782,1086`) ולכן מתעדכנים אוטומטית; אך ה-`embedding +vector(1024)` **אינו** generated — הוא תלוי-טריגר-חיצוני, נקודת-drift אפשרית +([02-data-model INV-DM3](02-data-model.md#inv-dm3-שינוי-תוכן--re-index)). שינוי-תוכן חייב להפעיל +re-embed; בדיקת-בריאות מגלה embeddings מיושנים. אוכף את +[G6](00-constitution.md#inv-g6-re-index-בכל-שינוי-תוכן). + +--- + +## 5. מצב קיים מול יעד — audit-findings + +ההבדלים בין הקוד בפועל ל-TARGET. **אלו תסמינים, לא התנהגויות תקינות.** כל פריט אומת מול הקוד. + +- **דליפת-הלכות חוצת-קורפוס (משימה #56).** `halacha_filters` נפתחים רק עם `review_status` + (`db.py:3168`, `db.py:3401`) ואינם מוסיפים `cl.source_kind`, בעוד `chunk_filters` כן + (`db.py:3169`, `db.py:3402`). שני ה-`halacha_sql` עושים `JOIN case_law` בלי סינון + (`db.py:3236-3242`, `db.py:3463-3482`). **תסמין:** חיפוש בקורפוס החיצוני + (`search_precedent_library`, `source_kind="external_upload"`) יכול להחזיר הלכה שמקורה + בהחלטת-ועדה פנימית — ולהפך עבור `search_internal_decisions` (`source_kind="internal_committee"`, + `internal_decisions.py:418`). **יעד:** `halacha_filters` יתחילו ב-`cl.source_kind = '{source_kind}'` + בדיוק כמו `chunk_filters` ([INV-RET1](#inv-ret1-הפרדת-קורפוס-נאכפת-ב-100-ממסלולי-ה-query)). +- **אין eval harness — מדידת-איכות לא קיימת.** רק `telemetry.log_search_bg` מתעד שאילתות + (`search.py:62,118,190,271`); אין gold-set מתויג ואין precision/recall. **יעד:** harness עומד + ([INV-RET4](#inv-ret4-איכות-אחזור-נמדדת-ב-eval-harness-עומד-precision--recall)). +- **`search_decisions` מתעד אזהרה כשאין `practice_area` אך לא חוסם.** ללא פילטר-תחום החיפוש + עלול לערבב תחומים משפטיים (`search.py:45-49,172-176` — `logger.warning`, ממשיך). **יעד:** הפרדה + לפי תחום נאכפת, לא מומלצת בלבד — תואם את עקרון ההפרדה ב-[G5](00-constitution.md#inv-g5-metadata-מלא--הפרדת-קורפוס-נאכפת-בכל-query). +- **`embedding` אינו `GENERATED` (בניגוד ל-tsvector).** נקודת-drift אפשרית בין תוכן ל-embedding + אחרי עדכון ([§4](#4-re-index-ושינוי-תוכן-g6); תואם [02-data-model](02-data-model.md#inv-dm3-שינוי-תוכן--re-index)). + **יעד:** טריגר re-embed מובטח + health-check. + +--- + +## 6. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — invariants גלובליים (G4–G9) + כללי-הנדסה. +- [01-ingest.md](01-ingest.md) — חוזה-הקליטה שמייצר את ה-chunks/embeddings שהאחזור שולף. +- [02-data-model.md](02-data-model.md) — חוזה-השלמות (searchable) + re-index שהאחזור מסנן לפיהם. +- [05-qa-review.md](05-qa-review.md) — שער-הלכה הידני (`review_status`) שמגדיר אילו הלכות searchable. +- [X5-audit-provenance.md](X5-audit-provenance.md) — עקיבוּת-מקור מלאה של כל span מוחזר (בסיס ל-INV-RET5). From 5e80bf560d615a94d680cd3d214389b2c9ce6dce Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 15:00:30 +0000 Subject: [PATCH 11/30] =?UTF-8?q?docs(spec):=20constitution=20index=20?= =?UTF-8?q?=E2=80=94=20add=20G9=20to=2003-retrieval=20row=20(consistency)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- docs/spec/00-constitution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/00-constitution.md b/docs/spec/00-constitution.md index d7802f7..934f068 100644 --- a/docs/spec/00-constitution.md +++ b/docs/spec/00-constitution.md @@ -220,7 +220,7 @@ Manual* (2d ed.) | סטטוס: verified | [00-constitution.md](00-constitution.md) | חוקה — ייעוד, invariants גלובליים, כללי-הנדסה, אינדקס | G1–G11 | | [01-ingest.md](01-ingest.md) | קליטה מאוחדת: מסמכי-תיק / פסיקה חיצונית / החלטות-ועדה — חוזה מסלול-יחיד | G2, G3 | | [02-data-model.md](02-data-model.md) | אחסון: ישויות (cases, case_law, documents, chunks, halachot…) + חוזה-שלמות לכל ישות | G1, G4, G6 | -| [03-retrieval.md](03-retrieval.md) | 3 קורפוסים + כלי-חיפוש · hybrid/RRF · attribution · eval harness | G4, G5, G6, G7, G8 | +| [03-retrieval.md](03-retrieval.md) | 3 קורפוסים + כלי-חיפוש · hybrid/RRF · attribution · eval harness | G4, G5, G6, G7, G8, G9 | | [04-analysis-writing.md](04-analysis-writing.md) | חילוץ טענות · 12 בלוקים · סגנון דפנה (מצטט block-schema.md) | G11 | | [05-qa-review.md](05-qa-review.md) | שערי QA + שערים אנושיים (אישור הלכה, בחירת תוצאה, פידבק) כ-invariant | G10, G11 | | [06-export.md](06-export.md) | ייצוא DOCX לפי תבנית דפנה | — | From 1582556b0b899c67abe2f028f22a2964368ad623 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 15:03:56 +0000 Subject: [PATCH 12/30] =?UTF-8?q?docs(spec):=2004-analysis-writing=20?= =?UTF-8?q?=E2=80=94=2012=20blocks=20+=20reasoned-decision=20invariants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- docs/spec/04-analysis-writing.md | 186 +++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 docs/spec/04-analysis-writing.md diff --git a/docs/spec/04-analysis-writing.md b/docs/spec/04-analysis-writing.md new file mode 100644 index 0000000..0983933 --- /dev/null +++ b/docs/spec/04-analysis-writing.md @@ -0,0 +1,186 @@ +# 04 — ניתוח וכתיבה (Analysis & Writing) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומפרט את שלב **הסיוע-בכתיבה** — +חילוץ הטענות, ארכיטקטורת 12 הבלוקים, וסגנון דפנה. הוא אוכף את +[INV-G11](00-constitution.md#inv-g11-תוכן-החלטה-מנומקת) (תוכן החלטה מנומקת). + +> **⚠ מודל-סמכות שונה מ-01–03.** זהו קובץ **תוכן-משפטי**, לא קובץ-הנדסה. לפי החוקה +> (§2 עיקרון 2, §5ב) הסמכות עליו היא **היו"ר (עו"ד דפנה תמיר) + מסמכי-הפרויקט** — +> [block-schema.md](../block-schema.md), [decision-methodology.md](../decision-methodology.md), +> [legal-decision-lessons.md](../legal-decision-lessons.md), +> [corpus-analysis.md](../corpus-analysis.md), [skills/decision/SKILL.md](../../skills/decision/SKILL.md). +> ה-invariants כאן **אינם** כפופים לפרוטוקול ≥3-המקורות החיצוני, ו**אינם** נושאים +> `סטטוס: verified / ⚠ UNVERIFIED`. במקום `מקורות: … | סטטוס` הם נושאים `מקור-סמכות:`. +> מסמכי-הפרויקט הם המקור המוסמך; קובץ זה מצטט אותם בגובה-ספ, לא משכפל את ההגדרות. + +--- + +## 1. חילוץ טענות → טיעונים מאוגדים + +לפני הכתיבה, חומרי-המקור הופכים למבנה-נתונים שמזין את הבלוקים. שני שלבים: + +### 1.1 חילוץ טענות גולמיות (claims) + +`extract_claims(case_number, doc_title="", party_hint="")` קורא לכתבי-הטענות בתיק, +ושומר טענות גולמיות ב-DB. הוא מסנן למסמכים מסוג `appeal` / `response` / `objection` +(אלא אם צוין `doc_title` מפורש), ולכל מסמך קורא ל-`claims_extractor.extract_and_store_claims` +— ראה `mcp-server/src/legal_mcp/tools/documents.py:300-347`. + +כל טענה נשמרת עם `party_role` מתוך התפקידים המוכרים: **`appellant` (עוררים)** · +**`respondent` (משיבים)** · **`committee` (ועדה מקומית)** · **`permit_applicant` +(מבקשי היתר)** · **`appraiser` (שמאי)**. `get_claims(case_number, party_role="")` +שולף ומציג אותן בעברית, עם סינון אופציונלי לפי תפקיד +(`documents.py:350-385`; מיפוי-העברית ב-`:370-376`). + +### 1.2 כינוס לטיעונים משפטיים מובחנים (legal arguments) + +`aggregate_claims_to_arguments(case_number, force=False)` מכנס את הפרופוזיציות +הגולמיות לטיעונים משפטיים מובחנים (de-duplication) דרך +`argument_aggregator.aggregate_claims_to_arguments`; `force=True` מוחק טיעונים קיימים +ומחשב מחדש — ראה `mcp-server/src/legal_mcp/tools/legal_arguments.py:11-33`. +`get_legal_arguments(case_number, party="")` שולף את הטיעונים המאוגדים, מקובצים לפי +צד (`appellant`/`respondent`/`committee`/`permit_applicant`/`unknown`); אם אין — +הוא מחזיר הנחיה להריץ קודם את הכינוס (`legal_arguments.py:36-83`). + +> **מדוע זה חשוב לתוכן:** הטיעונים המאוגדים הם הקלט ל-[INV-WR3](#inv-wr3-מענה-לכל-טענה-של-הצד-המפסיד) +> (מענה לכל טענה עיקרית) ול-[INV-WR4](#inv-wr4-בלוק-ז--טענות-מקוריות-בלבד) (הפרדת טענות +> מקוריות מהשלמות). הסינון לפי `party_role` מאפשר לזהות את הצד המפסיד ולוודא שכל טיעון +> שלו מקבל מענה בבלוק י. + +--- + +## 2. ארכיטקטורת 12 הבלוקים (סיכום) + +המבנה הפורמלי המלא — content model, constraints, משקלות, ופרמטרי-עיבוד לכל בלוק — +מוגדר ב-[block-schema.md](../block-schema.md) (המקור המוסמך). כאן רק מפת-גובה: + +| בלוק | תפקיד | CREAC | תוכן מהותי? | +|------|--------|-------|-------------| +| א–ד | כותרת מוסדית · הרכב · צדדים · "החלטה" | — | לא (template-fill) | +| ה | פתיחה ("לפנינו…") | C ראשוני | קל | +| **ו** | רקע עובדתי ("פתח דבר") | — | **כן — עובדות בלבד** | +| **ז** | טענות הצדדים | — | **כן — טענות מקוריות בלבד** | +| ח | הליכים בפני הוועדה | — | כן (תיעוד, ללא הערכה) | +| ט | תכניות חלות (אופציונלי) | R | כן (כשיש מורכבות תכנונית) | +| **י** | דיון והכרעה | full-CREAC | **כן — ה-ratio decidendi** | +| יא | סיכום / סוף דבר | C אחרון | קל | +| יב | חתימות | — | לא | + +יסודות תיאורטיים (CREAC · FJC Judicial Writing Manual · DITA · Akoma Ntoso), +תלויות-בין-בלוקים, וכללי-ולידציה — ב-[block-schema.md](../block-schema.md) §§1, 5, 6. +מתודולוגיית-המשקלות (Communicative / Reader-attention / Judicial-review / Empirical) +— שם §4. **טיוטת-ביניים** (Pre-Ruling Draft) בוחרת תת-קבוצת בלוקים (ו, ט, ז, ח) — +block-schema.md §7; שלב-החילוץ השמאי שלה (`extract_appraiser_facts`) מזין את בלוק ט. + +> **התמקדות לפי feedback היו"ר:** הסיוע מתמקד בבלוקים המהותיים (ו–יב); בלוקים א–ד +> ממולאים מ-template ואינם דורשים ניתוח. ראה `MEMORY.md` → "התעלם מכותרות". + +--- + +## 3. סגנון דפנה (סיכום) + +מדריך-הסגנון המלא הוא [skills/decision/SKILL.md](../../skills/decision/SKILL.md); +המתודולוגיה האנליטית ("איך לחשוב לפני איך לכתוב") היא +[decision-methodology.md](../decision-methodology.md). נקודות-מפתח: + +- **טון לפי סוג-ערר** — רישוי (1xxx) חם יחסית; היטל-השבחה (8xxx) ופיצויים ס'197 (9xxx) + קרים ויבשים (SKILL.md §1; methodology §א.2). +- **מבנה הדיון (בלוק י)** — נפתח במסקנה (CREAC: C→R→E→A→C), סילוגיזם לכל סוגיה, + steel-manning של הצד המפסיד, ציטוט-פסיקה ב"סנדוויץ'" (methodology §§ד, ו, ז). +- **מסלול-דיון לפי תוצאה** — דחייה (עיגולים קונצנטריים) · קבלה (נימוק-נימוק) · קבלה + חלקית (מיפוי-מתחים) · היטל-השבחה (פתיחה ישירה) — SKILL.md §7.3; block-schema.md בלוק י. +- **3 מקורות-פסיקה נפרדים** — אסור לבלבל ביניהם (SKILL.md §7.5; ראה גם + [03-retrieval.md](03-retrieval.md) לשכבת-האחזור שמזינה אותם). +- **לקחים מצטברים** — [legal-decision-lessons.md](../legal-decision-lessons.md) + + ביטויי-מעבר; מתעדכנים מפידבק-היו"ר ומ-Hermes (ראה forward-ref [07-learning.md](07-learning.md)). + +--- + +## 4. Invariants של התחום — תוכן החלטה מנומקת + +חמשת ה-invariants הבאים הם **פאֶטים של [INV-G11](00-constitution.md#inv-g11-תוכן-החלטה-מנומקת)**. +כולם נושאים `מקור-סמכות` (היו"ר + מסמכי-הפרויקט), **ללא** שדה-מקורות-חיצוני ו**ללא** +סטטוס-אימות — כמתחייב מהבחנת שתי-הסמכויות בחוקה (§5). + +### INV-WR1: רקע ניטרלי (בלוק ו) — עובדות בלבד +**כלל:** בלוק ו מציג **עובדות בלבד** ואינו טוען. אסורות מילות-ערך/שיפוט ("חריג", +"בעייתי", "למרבה הפליאה") ואסורים ציטוטים ישירים מצדדים (אלה שייכים לבלוק ז). החלטות +קודמות מובאות כעובדה יבשה ("ביום X נדחתה תכנית Y"), ללא נימוקים. ניטרליות אינה הסתרה: +עובדה מהותית התומכת בצד המפסיד **חייבת** להופיע. +**מקור-סמכות:** היו"ר (עו"ד דפנה תמיר) + [block-schema.md](../block-schema.md) (בלוק ו, +§5.2 "רקע ניטרלי") + [decision-methodology.md](../decision-methodology.md) §ח.2. +**אכיפה:** ולידציית-תוכן בבלוק ו (סעיף עם ציטוט-צד או מילת-שיפוט → לא שייך כאן) + שערי +QA; מפורט ב-[05-qa-review.md](05-qa-review.md). +**הפרה ידועה:** — + +### INV-WR2: ללא כפילות (בלוק י מפנה, לא חוזר) +**כלל:** בלוק י (דיון) **מפנה** לעובדות ולטענות שכבר הוצגו בבלוקים הקודמים ("כאמור +בסעיף X לעיל", "כפי שפורט") — ואינו חוזר עליהן. חריג יחיד: חזרה מכוונת עם שכבת-ניתוח +חדשה ("נשוב על כך כי…"). אין עובדות חדשות בדיון שלא הופיעו ברקע. +**מקור-סמכות:** היו"ר + [block-schema.md](../block-schema.md) (בלוק י, §5.2 "ללא +כפילות") + [skills/decision/SKILL.md](../../skills/decision/SKILL.md) §9.1. +**אכיפה:** ולידציית-מבנה (עובדה בדיון ללא עוגן ברקע = flag) + שערי QA; +מפורט ב-[05-qa-review.md](05-qa-review.md). +**הפרה ידועה:** — + +### INV-WR3: מענה לכל טענה של הצד המפסיד +**כלל:** כל **טענה עיקרית** שהוצגה בבלוק ז — ובמיוחד של הצד המפסיד — מקבלת **מענה +מנומק** בבלוק י (ישיר, "למעלה מן הצורך", או מקובץ עם דומותיה). מותר לא להכריע בטענה +נחוצה-פחות ("נוכח מסקנתנו לעיל, אין צורך…"), אך אסור להתעלם מטענה מרכזית — הצד המפסיד +חייב לראות שהוועדה שקלה את יסודות עמדתו (steel-manning). +**מקור-סמכות:** היו"ר + [decision-methodology.md](../decision-methodology.md) §§ג.2, ו.2 + +[block-schema.md](../block-schema.md) (בלוק י MUST: "מענה לכל טענה" §5.4) + +[skills/decision/SKILL.md](../../skills/decision/SKILL.md) §6.2. +**אכיפה:** מיפוי טענות-בלוק-ז → מענה-בלוק-י (נשען על §1.2, הטיעונים המאוגדים) + שערי QA; +מפורט ב-[05-qa-review.md](05-qa-review.md). +**הפרה ידועה:** — + +### INV-WR4: בלוק ז — טענות מקוריות בלבד +**כלל:** בלוק ז מכיל **אך ורק** טענות מכתבי-הטענות המקוריים (כתב-ערר, כתב-תשובה). +תוכן מהשלמות-טיעון, החלטות-ביניים, ותגובות-מאוחרות → **בלוק ח** (הליכים), לא בלוק ז. +הצגת-הטענות היא בנאמנות וללא הערכה ("טענה זו חלשה") — ההערכה שייכת לבלוק י. +**מקור-סמכות:** היו"ר + [block-schema.md](../block-schema.md) (בלוק ז Sources + +§5.2 "טענות מקוריות בלבד") + [skills/decision/SKILL.md](../../skills/decision/SKILL.md) §4. +**אכיפה:** סיווג-מקור של טענה בעת החילוץ (`extract_claims` מסנן `appeal`/`response`/ +`objection`; מסמכי פוסט-דיון מתויגים `is_post_hearing` ומופנים לבלוק ח — block-schema.md §7) ++ שערי QA; מפורט ב-[05-qa-review.md](05-qa-review.md). +**הפרה ידועה:** — + +### INV-WR5: "מבחן-השופט" — החלטה עצמאית וקריאה +**כלל:** ההחלטה חייבת להיות **עצמאית וקריאה לשופט שלא מכיר את התיק** — תשתית עובדתית +מלאה (בלוק ו), תיעוד procedural-fairness (בלוק ח), והנמקה שעומדת בבדיקת סבירות +ומידתיות (בלוק י). הקורא לא נדרש לחומרי-המקור כדי להבין את ההחלטה ואת הצדקתה. +**מקור-סמכות:** היו"ר + [block-schema.md](../block-schema.md) §4.3 ("מבחן השופט" / +Judicial-Review weight) + [decision-methodology.md](../decision-methodology.md) §יב +(רשימת-ביקורת) + [corpus-analysis.md](../corpus-analysis.md). +**אכיפה:** שער QA סופי ("מבחן-השופט") על ההחלטה כיחידה שלמה; +מפורט ב-[05-qa-review.md](05-qa-review.md). +**הפרה ידועה:** — + +--- + +## 5. צ'קליסט-תוכן לפי סוג-ערר + +בלוק י מקבל **צ'קליסט-תוכן** המוזרק אוטומטית ל-prompt לפי סוג-הערר, מתוך +`CONTENT_CHECKLISTS` ב-`mcp-server/src/legal_mcp/services/lessons.py:355`. הבורר +(`lessons.py:532-555`) ממפה לסוג: `tama38` (תמ"א 38) · `betterment_levy` (היטל-השבחה) · +`licensing_property` · `licensing_threshold` (שאלת-סף) · `licensing_substantive` +(ברירת-מחדל לרישוי). הצ'קליסט מבטיח שהדיון מכסה את הנושאים התכנוניים/המשפטיים שדפנה +מכסה בפועל בקורפוס — ראה [corpus-analysis.md](../corpus-analysis.md) §§3, 6 לדפוסי-התוכן +ולפער שנסגר (§5.3). זהו מנגנון-תוכן באחריות היו"ר, לא חוק-הנדסה. + +--- + +## 6. הפניות-אחיות + +- [00-constitution.md](00-constitution.md#inv-g11-תוכן-החלטה-מנומקת) — INV-G11 + הבחנת + שתי-הסמכויות (תוכן-משפטי מול הנדסה). +- [03-retrieval.md](03-retrieval.md) — שכבת-האחזור (3 קורפוסי-פסיקה) שמזינה ציטוטים לבלוק י. +- [05-qa-review.md](05-qa-review.md) — שערי-QA שאוכפים את INV-WR1–WR5 + שערים אנושיים. +- [06-export.md](06-export.md) — ייצוא DOCX לפי תבנית-דפנה (אחרי הכתיבה). +- [07-learning.md](07-learning.md) — לולאת פידבק-היו"ר + Hermes שמעדכנת lessons/SKILL. +- מסמכי-המקור המוסמכים: [block-schema.md](../block-schema.md) · + [decision-methodology.md](../decision-methodology.md) · + [legal-decision-lessons.md](../legal-decision-lessons.md) · + [corpus-analysis.md](../corpus-analysis.md) · + [skills/decision/SKILL.md](../../skills/decision/SKILL.md). From c1872aa2144ebfb3224645e6da6c35c771c8b899 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 15:09:42 +0000 Subject: [PATCH 13/30] =?UTF-8?q?docs(spec):=2005-qa-review=20=E2=80=94=20?= =?UTF-8?q?QA=20gates=20+=20human=20gates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec/05-qa-review.md | 195 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 docs/spec/05-qa-review.md 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 ועקיבוּת-מקור. From c0ef90d72209ac4e9d7efcb2e6ebc8ae1124d3a9 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 15:12:30 +0000 Subject: [PATCH 14/30] =?UTF-8?q?docs(spec):=2005-qa-review=20=E2=80=94=20?= =?UTF-8?q?clarify=20neutral=5Fbackground=20dual=20return=20path=20(critic?= =?UTF-8?q?al=20fallback=20w/=20passed=3DTrue);=20fix=20line=20ref?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- docs/spec/05-qa-review.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/spec/05-qa-review.md b/docs/spec/05-qa-review.md index f53b6a8..054bf1d 100644 --- a/docs/spec/05-qa-review.md +++ b/docs/spec/05-qa-review.md @@ -42,7 +42,10 @@ ### 1.2 דקויות חשובות (אל תניח — מהקוד) - **רק 2 שערים קריטיים** חוסמים ייצוא: `claims_coverage` ו-`structural_integrity`. שאר - הארבעה הם `warning` (כולל `neutral_background`!) — `qa_validator.py:86, 202, 240, 286`. + הארבעה הם `warning` בנתיב הרגיל — `qa_validator.py:86, 202, 257, 286`. +- **דקות `neutral_background` — שני נתיבי-החזרה:** הנתיב הרגיל מסומן `warning` (`:86`); נתיב + ה-fallback של בלוק-ו ריק/חסר מסומן `critical` (`:70`) **אך מחזיר `passed=True`**, ולכן + אינו נספר ב-`critical_failures` ואינו חוסם ייצוא. תפקודית — השער אינו חוסם. - **`claims_coverage` סובלני ל-20%:** עובר אם `len(missing) ≤ total*0.2` (`qa_validator.py:170`). מסנן לטענות `appellant`/`respondent` שאינן מבלוק-ז (`qa_validator.py:120-129`), כי טענות `committee`/`permit_applicant` הן עמדות-הגנה ולא From 0fd06659da44b1acf1f95244ecfaeda7cda729bd Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 15:16:00 +0000 Subject: [PATCH 15/30] docs(spec): 06-export DOCX contract Co-Authored-By: Claude Opus 4.8 --- docs/spec/06-export.md | 168 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 docs/spec/06-export.md diff --git a/docs/spec/06-export.md b/docs/spec/06-export.md new file mode 100644 index 0000000..ca3fc8d --- /dev/null +++ b/docs/spec/06-export.md @@ -0,0 +1,168 @@ +# 06 — ייצוא DOCX (Export Contract) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומגדיר את **חוזה-הייצוא** של עוזר +משפטי: הרינדור של החלטה ל-DOCX מעוצב (גופן David, RTL, סגנונות-טמפלט). העיקרון המכונן — +**ה-DB הוא מקור-האמת היחיד, וה-DOCX הוא נתון נגזר (derived) הניתן לשחזור**. הקובץ אוכף את +[INV-G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) (מקור-אמת +יחיד / נתון-נגזר משוחזר) ואת [INV-G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai) +(עקיבוּת-מקור), והוא השלב שאחרי שער-הייצוא הקריטי של +[05-qa-review.md / INV-QA3](05-qa-review.md#inv-qa3-החלטה-לא-מיוצאת-עם-כשל-קריטי-governance--g10). + +> **כללי-סגנון — סמכות אחת.** מכניקת העיצוב (line classification, dash policy, placeholder, +> מיפוי-סגנונות, RTL-runs) מתועדת במלואה בסקיל +> [`dafna-decision-template/SKILL.md`](../../skills/dafna-decision-template/SKILL.md) — **הוא +> המקור הסמכותי**. הקובץ הזה **מסכם ומפנה**, לא משכפל. כללי-הסגנון עצמם הם תוכן-משפטי-דומייני +> (סמכות היו"ר + הסקיל), בעוד שחוזה-ה-derived-data (INV-EX1) ועקיבוּת-המקור (INV-EX2) הם +> invariants הנדסיים הנושאים `מקורות` + `סטטוס`. + +--- + +## 1. חוזה-הייצוא — DB הוא המקור, DOCX הוא הנגזר + +החלטה מאוחסנת כ-**בלוקים מובְנים ב-DB** — `decision_blocks` (12 בלוקים, מפתח קנוני +`UNIQUE(decision_id, block_id)`) תחת `decisions` (`UNIQUE(case_id, version)`); ראה +[02-data-model.md §1](02-data-model.md). ה-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](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) +> (סימטריה — לא שתי שכבות-סגנון מתפצלות). + +## 2. כללי-הסגנון — סיכום (הסמכות: הסקיל) + +ה-service מחיל את סגנונות-הטמפלט בלבד (`paragraph.style = "Heading 2"`) — בלי font/size/indent +ידני; העיצוב (David, RTL, גדלים) מגיע מ-`styles.xml`. הפירוט המלא + ה-XML של כל סגנון: +[`SKILL.md`](../../skills/dafna-decision-template/SKILL.md) + `references/`. + +- **סיווג-שורות (`_classify_line`):** כל שורה מסווגת לאחת מ-6 קטגוריות — `label_heading`, + `inline_label`, `numbered`, `bullet`, `heb_letter`, `plain` — שקובעות את הסגנון המוחל + (Heading 2 / Normal / List Paragraph). ראה + [`references/line-classification.md`](../../skills/dafna-decision-template/references/line-classification.md). +- **מדיניות-מקפים (`_no_dash`):** דפנה ביקשה "בלי מקפים בכלל" — `—` (U+2014) ו-`–` (U+2013) + מוסרים מכל טקסט נכתב; מקף רגיל (`-`) נשמר. +- **שדות-placeholder:** `chair_position` עם סימן-ריק (`[ימולא ע"י יו"ר הוועדה]` וכד') מוחלף + ב-`[טרם מולאה עמדת ועדת הוועדה]` ב-italic — סימן ויזואלי שנותר להשלים (תואם + [INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) — היו"ר + משלימה, לא המערכת). +- **RTL-runs:** כל run מסומן `` (`_mark_run_rtl`) — אחרת Word נופל ל-Times New Roman + במקום David. ראה [`references/rtl-runs.md`](../../skills/dafna-decision-template/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](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai); ראה + [X5-audit-provenance.md](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](00-constitution.md#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](#5-current-vs-target). + +### 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](04-analysis-writing.md), +[X5-audit-provenance.md](X5-audit-provenance.md)). אוכף את +[INV-G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai). +**הפרה ידועה:** — + +### INV-EX3: אין ייצוא בכשל-QA קריטי (restate של INV-QA3 →G10) +**כלל:** הייצוא **חסום** כל עוד שער-QA קריטי נכשל (`claims_coverage` / `structural_integrity`); +`export_blocked` חייב להיבדק לפני ייצוא. זהו אותו invariant של +[INV-QA3](05-qa-review.md#inv-qa3-החלטה-לא-מיוצאת-עם-כשל-קריטי-governance--g10), בצד-הייצוא. +**מקורות:** 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](#5-current-vs-target) (תואם +[05-qa-review §4](05-qa-review.md#4-current-vs-target--ממצאי-audit)). + +--- + +## 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. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — [INV-G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) + (derived-data / מקור-יחיד) · [INV-G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai) + (עקיבוּת) · [INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) (שערים). +- [02-data-model.md](02-data-model.md) — `decisions`/`decision_blocks` (המקור שממנו מייצאים). +- [04-analysis-writing.md](04-analysis-writing.md) — כתיבת הבלוקים שמהם נגזר ה-DOCX. +- [05-qa-review.md](05-qa-review.md#inv-qa3-החלטה-לא-מיוצאת-עם-כשל-קריטי-governance--g10) — + INV-QA3 (שער-הייצוא הקריטי שקודם לשלב זה). +- [07-learning.md](07-learning.md) — `ingest_final_version` + Hermes על ההחלטה הסופית. +- [X5-audit-provenance.md](X5-audit-provenance.md) — audit-trail (commit git) ועקיבוּת-מקור. +- [`skills/dafna-decision-template/SKILL.md`](../../skills/dafna-decision-template/SKILL.md) — + **המקור הסמכותי** לכללי-הסגנון (line classification · dash policy · placeholder · RTL-runs). From b46d25f6052191586596de93e6fd0884ab6ab28d Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 15:21:34 +0000 Subject: [PATCH 16/30] docs(spec): 07-learning loop Co-Authored-By: Claude Opus 4.8 --- docs/spec/07-learning.md | 189 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/spec/07-learning.md diff --git a/docs/spec/07-learning.md b/docs/spec/07-learning.md new file mode 100644 index 0000000..d557c6c --- /dev/null +++ b/docs/spec/07-learning.md @@ -0,0 +1,189 @@ +# 07 — לולאת הלמידה (Learning Loop) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) ומפרט כיצד המערכת **לומדת לאורך +זמן** — מהחלטות סופיות (Hermes), מפידבק-היו"ר, ומצמיחת-הקורפוס — באופן שמזין חזרה את +הכתיבה ([04-analysis-writing.md](04-analysis-writing.md)) ואת שערי-האיכות +([05-qa-review.md](05-qa-review.md)). הוא אוכף את +[INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) +(שערים אנושיים — אישור היו"ר על כל עדכון-ידע) ואת +[INV-G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) / +כלל-ההנדסה **quality-at-source** (האחריות לאיכות יושבת במקור, לא בטלאי במורד הזרם). + +> **⚠ קובץ מעורב — שני מודלי-סמכות** (לפי החוקה §3, §5): +> - **שער-הממשל** (Hermes מציע — היו"ר מאשרת ידנית; אין auto-commit ל-SKILL/lessons) +> הוא **invariant הנדסי** במודל הממשל-שיפוטי → נושא `מקורות:` (NCSC/JTC · CEPEJ 2018 · +> FJC) + `סטטוס: verified`. +> - **כלל-ההנדסה quality-at-source** (היכן יושבת האחריות לאיכות-הידע) → invariant הנדסי +> במודל הנדסת-הנתונים → נושא `מקורות:` (Fowler — Data Mesh / quality-at-source · +> DAMA-UK · ISO 8000) + `סטטוס: verified`. + +--- + +## 1. שלוש לולאות-המשנה + +הלמידה אינה אירוע יחיד אלא **שלוש לולאות** המתנקזות לאותם מסמכי-ידע מוסמכים +([legal-decision-lessons.md](../legal-decision-lessons.md), +[skills/decision/SKILL.md](../../skills/decision/SKILL.md)) ולקורפוסים: + +### 1.1 לולאת-Hermes (post-export → הצעה → אישור) + +הסוכן [hermes-curator](../../.claude/agents/hermes-curator.md) (adapter `deepseek_local`, +פרופילים `curator-cmp` / `curator-cmpa`) נקרא **אחרי שדפנה מסמנת קובץ כסופי** ב-UI +(`POST /api/cases/{case_number}/exports/{filename}/mark-final` → `pc_wake_curator_for_final()` +ב-`web/paperclip_client.py` → sub-issue + wakeup; **חיבור ישיר מה-UI, לא דרך CEO** — +`hermes-curator.md:27-35`). הוא: + +- **קורא בלבד** את הטקסט הסופי (`case_get_final_text`), `get_style_guide`, ואת + `SKILL.md` / `legal-decision-lessons.md` / `corpus-analysis.md` המקומיים + (`hermes-curator.md:60-70`). +- מזהה **3–5 דפוסים/פערים** חדשים, כל ממצא מתויג `[סגנון]` / `[מבנה]` / + `[לקסיקון משפטי]` / `[טבלאי]` (`hermes-curator.md:99-108`). +- **מציע** — comment ב-Paperclip + רישום כל ממצא כ-`decision_lesson` דרך + `POST /api/training/corpus/{corpus_id}/lessons` (`source:"curator"`) שמופיע ב-UI + תחת הטאב "מה למדנו" (`hermes-curator.md:73-96`). +- **אינו מעדכן** קבצים בעצמו (skills/, lessons.py, DB) — רק מציע (`hermes-curator.md:125-130`). + +### 1.2 לולאת-פידבק-היו"ר (capture → ניתוח שבועי → לקחים) + +- **לכידה מובנית:** `record_chair_feedback` שומר הערת-דפנה בטבלת `chair_feedback` + (`category ∈ {missing_content, wrong_tone, wrong_structure, factual_error, style, + other}`) — `tools/workflow.py:348`, ראה [05-qa-review.md](05-qa-review.md) §2.3. +- **ניתוח שבועי:** ה-scheduled job `weekly-feedback-analysis` (ראשון 19:00, + `plugin-legal-ai/src/manifest.ts:175-179`) מושך `GET /api/chair-feedback/weekly-summary`, + ואם יש פריטים — **מעיר את ה-CEO** לעדכן את `legal-decision-lessons.md` עם הלקחים + החדשים (`worker.ts:784-837`; הוראת ה-prompt: "הוסף רק לקחים חדשים… קבץ לפי נושא" + — `worker.ts:830`). +- אין פריטים → הג'וב מדלג בשקט (`worker.ts:805`). ל-CEO שמתעורר מ-`weekly-feedback-job` + **אין `issueId`** — הוא כותב לקובץ בלבד, לא מפרסם comment ולא סוגר issue + (כלל מ-[CLAUDE.md](../../CLAUDE.md) "Scheduled Jobs"). + +### 1.3 לולאת-צמיחת-הקורפוס (החלטה סופית → קורפוס → אחזור) + +החלטה סופית נקלטת לקורפוס-הסגנון (`ingest_final_version` — ראה [06-export.md](06-export.md) +§ Hermes), ופסיקה/החלטות-ועדה חדשות נקלטות דרך המסלול הקנוני של +[01-ingest.md](01-ingest.md). כך הקורפוס שמזין את האחזור ([03-retrieval.md](03-retrieval.md)) +**גדל מהפלט עצמו** — והדיון הבא נשען על תקדים עשיר יותר. צמיחה זו כפופה לאותו חוזה-שלמות +([G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש)) כמו כל קליטה. + +--- + +## 2. הלולאה במלואה (הציור) + +``` + ┌──────────────────────────────────────────────────────┐ + │ │ + ┌─────────────▼─────────────┐ ┌────────────────────────┐ │ + │ כתיבה (04) │ ───▶ │ QA + שערים אנושיים (05)│ │ + │ 12 בלוקים · סגנון דפנה │ │ validate_decision + │ │ + │ ← lessons.py CONTENT_ │ │ פידבק-היו"ר │ │ + │ CHECKLISTS · SKILL.md │ └───────────┬────────────┘ │ + └───────────────────────────┘ │ ייצוא (06) │ + ▲ ▼ │ + │ ┌──────────────────────┐ │ + ┌────────┴──────────────┐ │ סימון "סופי" (UI) │ │ + │ legal-decision- │ │ mark-final │ │ + │ lessons.md + SKILL.md │ └───────┬──────────────┘ │ + │ (מסמכי-ידע מוסמכים) │ │ │ + └────────▲──────────────┘ ┌──────────┴───────────┐ │ + │ ▼ ▼ │ + │ ✋ אישור-יו"ר ידני ┌───────────────┐ ┌────────────────┐│ + └──────────────────────│ Hermes curator │ │ ingest_final → ││ + (commit ידני בלבד) │ → הצעות(comment)│ │ קורפוס-סגנון → ┘│ + └───────────────┘ │ אחזור (03) │ + ┌───────────────────────────┐ └────────────────┘ + │ פידבק-היו"ר (05) ──┐ │ + │ chair_feedback │ │ + └────────────────────┼───────┘ + ▼ + weekly-feedback-analysis (job) + │ מעיר CEO + ▼ + עדכון legal-decision-lessons.md ──┐ + └──▶ (חזרה ל-04 / lessons.py) +``` + +הקשר לכתיבה: הלקחים והצ'קליסטים שב-`CONTENT_CHECKLISTS` +(`mcp-server/src/legal_mcp/services/lessons.py:355`, בורר `get_content_checklist` +`:509-555`) ו-`get_lessons_for_outcome` (`lessons.py:309`) מוזרקים ל-prompt-הכתיבה לפי +סוג-ערר ותוצאה — ראה [04-analysis-writing.md](04-analysis-writing.md) §5. כל סגירה של +לולאה (Hermes או פידבק) שמשנה את `legal-decision-lessons.md` / `SKILL.md` משפיעה ישירות +על הכתיבה הבאה. + +--- + +## 3. Invariants של התחום + +### INV-LRN1: עדכון-ידע דורש אישור-יו"ר ידני — אין auto-commit (governance →G10) +**כלל:** מנגנוני-הלמידה (Hermes, ניתוח-פידבק שבועי) **מציעים בלבד**. כל שינוי ב- +[SKILL.md](../../skills/decision/SKILL.md) או ב-[legal-decision-lessons.md](../legal-decision-lessons.md) +מחייב **בחינה ואישור ידניים של היו"ר/חיים** ואז commit ידני — **לעולם לא auto-committed**. +Hermes כותב comment + `decision_lesson`, לא קבצים; ה-CEO השבועי כותב לקובץ אך הצעותיו +מאומתות ידנית לפני קיבוע. זהו פֶּאֶט של [INV-G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) +על שכבת-הידע: גם הלמידה כפופה לשיקול-הדעת האנושי. +**מקורות:** NCSC/JTC — *Principles & Practices for AI Use in Courts* (human-in-the-loop; +never replace human judgment) · Council of Europe / CEPEJ (2018, under user control) · +Federal Judicial Center — *Judicial Writing Manual* (2d ed.) | סטטוס: verified +**אכיפה:** הסוכן read-only על תוכן ו-write רק על comments (`hermes-curator.md:1-3, 125-130`); +תהליך-האישור — הצעת-curator כ-comment ב-Paperclip → חיים בוחן ומאשר ידנית → commit ל- +`SKILL.md` ו-`docs/legal-decision-lessons.md` (מ-[CLAUDE.md](../../CLAUDE.md) "Hermes Curator"); +ה-CEO השבועי מתעורר בלי `issueId` וכותב לקובץ בלבד ([CLAUDE.md](../../CLAUDE.md) "Scheduled Jobs"). +**הפרה ידועה:** — + +### INV-LRN2: האחריות לאיכות יושבת במקור — quality-at-source (engineering →G4) +**כלל:** האחריות לאיכות-הידע (לקחים, הלכות, metadata של פריטים מואנדקסים) נאכפת **קרוב +ככל האפשר לנקודת-היצירה/הקליטה** — בעת ניסוח-ההחלטה, בעת לכידת-הפידבק, ובעת קליטת-פריט — +**לא** מתוקנת בדיעבד במורד-הזרם (re-OCR, טלאי-קריאה, ניחוש בזמן-חיפוש). פריט-ידע חסר-שלמות +מסומן ומדווח בנקודת-הכניסה, לא מתקבל בשקט. +**מקורות:** Martin Fowler — *Data Mesh* (quality-at-source: domain owns data quality at +the point of creation) · DAMA-UK *Six Primary Dimensions for Data Quality* (2013, +completeness) · ISO 8000 (Data quality) | סטטוס: verified +**אכיפה:** חוזה-שלמות בקליטה ([01-ingest.md](01-ingest.md) §2, [02-data-model.md](02-data-model.md)) ++ "אין בליעה שקטה" (חוקה §6); לכידת-פידבק מובנית בנקודת-ההערה (`record_chair_feedback`, +`tools/workflow.py:348`); לקחים נשמרים מבני ולא ad-hoc (`lessons.py`, +[legal-decision-lessons.md](../legal-decision-lessons.md)). +**הפרה ידועה:** ראה [INV-G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) +(ערן סופר 8046/24 אונדקס עם `headnote`/`summary`/`tags` ריקים — שלמות לא נאכפה במקור) → +ממצא ל-[audit](../audit-report.md). + +### INV-LRN3: ידע נלכד באופן מובנה — לא ad-hoc (engineering →G9) +**כלל:** פידבק ולקחים נלכדים ב**מבנה דטרמיניסטי ועקיב** — `chair_feedback` (עם `category` +ו-`block_id`), `decision_lessons` (עם `category`/`source`), ו-`CONTENT_CHECKLISTS` בקוד — +כך שהלמידה **עמידה וניתנת-לביקורת**, לא פזורה בהערות חופשיות. מקור-הלקח (`source:"curator"` +מול פידבק-יו"ר) משומר לעקיבוּת. +**מקורות:** ISO 15489-1:2016 (records reliability/authenticity) · DAMA-UK *Six Primary +Dimensions for Data Quality* (2013) · ISO 8000 (Data quality) | סטטוס: verified +**אכיפה:** טבלת `chair_feedback` + `record_chair_feedback`/`list_chair_feedback` +(`tools/workflow.py:348, 393`); `decision_lessons` עם `source`+`category` +(`hermes-curator.md:79-96`); `CONTENT_CHECKLISTS`/`get_lessons_for_outcome` +(`lessons.py:355, 309`). עקיבוּת-מקור קושרת ל-[X5-audit-provenance.md](X5-audit-provenance.md). +**הפרה ידועה:** — + +--- + +## 4. הג'ובים המתוזמנים (תמיכת-תשתית ללולאה) + +| Job (`manifest.ts`) | לוח-זמנים | תפקיד בלולאה | +|---------------------|-----------|---------------| +| `weekly-feedback-analysis` | ראשון 19:00 (`:175-179`) | מסכם פידבק-יו"ר → מעיר CEO לעדכון `legal-decision-lessons.md` (`worker.ts:784-837`) | +| `stale-case-reminder` | יומי 08:00 (`:169-172`) | תזכורת על תיקים תקועים 30+ ימים (`worker.ts:710-780`) — היגיינת-תהליך, לא ידע | +| `sync-case-status` | כל 15 דק' (`:162-166`) | מסנכרן סטטוסי-תיקים legal-ai↔Paperclip (`worker.ts:624`) — תשתית, לא ידע | + +רק `weekly-feedback-analysis` הוא חלק מלולאת-הלמידה; שני האחרים הם היגיינת-תהליך/סנכרון. + +--- + +## 5. הפניות-אחיות + +- [00-constitution.md](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) — + INV-G10 (שערים אנושיים) + [INV-G4](00-constitution.md#inv-g4-חוזה-שלמות-לפני-שמיש--ניתן-לחיפוש) + (quality-at-source) + כלל-ההנדסה §6. +- [04-analysis-writing.md](04-analysis-writing.md) — הכתיבה שהלקחים/הצ'קליסטים מזינים (§3, §5). +- [05-qa-review.md](05-qa-review.md) — שער פידבק-היו"ר (§2.3) שמתחיל את לולאת-הפידבק. +- [01-ingest.md](01-ingest.md) — קליטה אחידה (quality-at-source) לצמיחת-הקורפוס. +- [03-retrieval.md](03-retrieval.md) — האחזור שהקורפוס הגדל מזין. +- [06-export.md](06-export.md) — `mark-final` שמפעיל את Hermes + `ingest_final_version`. +- [X5-audit-provenance.md](X5-audit-provenance.md) — עקיבוּת-מקור של לקחים (`source`). +- הסוכן: [.claude/agents/hermes-curator.md](../../.claude/agents/hermes-curator.md). +- מסמכי-הידע המוסמכים: [legal-decision-lessons.md](../legal-decision-lessons.md) · + [skills/decision/SKILL.md](../../skills/decision/SKILL.md) · + [corpus-analysis.md](../corpus-analysis.md). From b0e4e14832d3cbb7f36b8ac771f3c27efc6cbd92 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 16:41:37 +0000 Subject: [PATCH 17/30] docs(spec): X1-identifiers canonical model Co-Authored-By: Claude Opus 4.8 --- docs/spec/X1-identifiers.md | 156 ++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 docs/spec/X1-identifiers.md diff --git a/docs/spec/X1-identifiers.md b/docs/spec/X1-identifiers.md new file mode 100644 index 0000000..bfeba87 --- /dev/null +++ b/docs/spec/X1-identifiers.md @@ -0,0 +1,156 @@ +# X1 — מודל המזהים הקנוני (Canonical Identifier Model) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) והוא ה-deep-dive על **מזהי הישויות** +של עוזר משפטי. הוא אוכף את [G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה) (מזהה +קנוני מנורמל בכתיבה) ומעמיק את [INV-DM2](02-data-model.md#inv-dm2-מזהה-קנוני-יחיד-לכל-ישות) +מ-[02-data-model.md](02-data-model.md). שני הקבצים חייבים להישאר עקביים: 02 מגדיר *אילו* +שדות מזהים כל ישות; X1 מגדיר את *הצורה הקנונית* של המזהה ו*איך* הוא מנורמל. + +> **TARGET, לא תיאור-מצב.** המודל כאן הוא היעד הקנוני. כל מקום שבו הקוד בפועל +> (`mcp-server/src/legal_mcp/services/db.py`) סוטה ממנו — מתועד כ-**audit-finding** (§4), +> תסמין, לא התנהגות תקינה. כל טענה על הקוד הקיים מצוטטת `file:line` ואינה מונחת כתקינה. + +--- + +## 1. הצורה הקנונית של `case_number` + +מזהה-התיק (`case_number`) הוא **מספר-תיק מנורמל** — לא מחרוזת-ציטוט, לא תווית-תצוגה. הצורה +הקנונית מוגדרת ע"י **נרמול בנקודת-הכתיבה** (write-time canonicalization), כך שכל הרשומות +חולקות פורמט יחיד והשוואה היא תמיד שוויון-מחרוזת מול הצורה הקנונית. + +**הנרמול הקנוני (TARGET — מופעל בכתיבה):** + +| צעד | פעולה | דוגמה | +|------|--------|--------| +| trim | הסרת רווחים מקיפים | `" 8137/24 "` → `"8137/24"` | +| prefix-strip | הסרת קידומת-הליך לפני הספרה הראשונה ("ערר", "בל\"מ", "עע\"מ") | `"ערר 8137/24"` → `"8137/24"` | +| separator | איחוד מפריד `/` → `-` | `"8137/24"` → `"8137-24"` | +| padding | צורת-מספר אחידה (סדרתי-שנה, ומחוז כשקיים) | `"8126-25"` ↔ `"8126-03-25"` | + +> סוג-ההליך (`proceeding_type ∈ {ערר, בל"מ}`) הוא **חלק מהמפתח הקנוני** — לא חלק ממחרוזת +> ה-`case_number`. הקידומת "ערר"/"בל\"מ" מהכותרת נשללת מהמספר ונשמרת בעמודה ייעודית +> (`cases.proceeding_type`, `db.py:912`). כך "ערר 8137/24" ו-"בל\"מ 8137/24" הם שתי +> רשומות מובחנות בעלות אותו `case_number=8137-24` ו-`proceeding_type` שונה. + +**נרמול-בכתיבה הוא המנגנון הראשי; התאמה-סלחנית-בקריאה היא נוחות משנית בלבד.** כלל-ההנדסה +"נרמול לא תיקון-תסמין" (חוקה §6) קובע: מתקנים את הנתון במקור, לא מטליאים בקריאה. אם רשומה +נשמרה בצורה לא-קנונית — היעד הוא לנרמל אותה במיגרציה/בכתיבה, **לא** לסמוך על מנוע-קריאה +שיגשר על הפער. ההתאמה-הסלחנית (§3) קיימת כדי לבלוע *קלט-משתמש* רב-צורני (כותרת Paperclip), +לא כדי לתרץ נתון-מאוחסן לא-קנוני. + +--- + +## 2. שני מרחבי-מזהים: `cases` מול `case_law` + +`case_number` מופיע בשתי טבלאות נפרדות עם **שני מרחבי-מזהים שונים** ו**ללא FK חוצה-טבלאות** +ביניהן. בלבול בין השניים הוא כשל-שורש: תיק חי אינו תקדים, ולהפך. + +| ממד | `cases` (תיק חי) | `case_law` (קורפוס פסיקה) | +|------|------------------|---------------------------| +| תפקיד | הערר שבטיפול כעת (1xxx/8xxx/9xxx) | תקדים — פסיקה חיצונית **וגם** החלטות-ועדה | +| מפתח קנוני | `(case_number, proceeding_type)` | `(case_number, source_kind, proceeding_type)` — ראה להלן | +| אילוץ-ייחודיות | `uq_cases_number_proc` על `(case_number, proceeding_type)` (`db.py:923-924`) | שני partial unique לפי `source_kind` (`db.py:904-909`) | +| מורשת (הוסרה) | `case_number TEXT UNIQUE NOT NULL` (`db.py:76`), הוסר V15 (`db.py:921-922`) | `case_number TEXT UNIQUE NOT NULL` (`db.py:368`), הוסר V15 (`db.py:902-903`) | +| FK חוצה | **אין** — `cases` ו-`case_law` הם מרחבים נפרדים | **אין** | + +**`case_law` — מזהה מודע-source_kind.** ה-V15 החליפה את `UNIQUE(case_number)` הגלובלי בשני +partial unique indexes (`db.py:904-909`): + +- **`internal_committee`** (החלטות-ועדה פנימיות): `UNIQUE(case_number, proceeding_type)` + — `uq_case_law_internal_number_proc`, `WHERE source_kind = 'internal_committee'`. +- **חיצוני** (`external_upload` / `cited_only` / `nevo_seed`): `UNIQUE(case_number)` + — `uq_case_law_external_number`, `WHERE source_kind <> 'internal_committee'`. + +לכן המזהה הקנוני של `case_law` הוא הטריפלט **(`case_number` מנורמל, `source_kind`, +`proceeding_type`)** — עקבי עם [02-data-model §2א](02-data-model.md#2א-case_law--החוזה-הקונקרטי). + +**אין הצמדה חוצה-טבלאות.** כשהחלטת-תיק מ-`cases` מצוטטת בהמשך כתקדים, היא נכנסת ל-`case_law` +כרשומה *חדשה* (`source_kind='internal_committee'`) — לא כ-FK ל-`cases`. שני המרחבים נשארים +עצמאיים; הגישור ביניהם הוא דרך הקליטה ([01-ingest.md](01-ingest.md)), לא דרך מפתח-זר. + +--- + +## 3. ציטוט מול מזהה — `citation_formatted` הוא תצוגה, לא מפתח + +הציטוט-המלא והמזהה-הקנוני הם **שני שדות נפרדים בכוונה**: + +- **מזהה קנוני** = `case_number` מנורמל (`8126-03-25`) — המפתח שמשמש לחיפוש, ל-upsert, + ולאילוצי-ייחודיות. +- **ציטוט מעוצב** = `citation_formatted` (`db.py:1070`, V19) — מחרוזת-תצוגה לפי כללי-הציטוט + האחיד, למשל: `ערר (ועדות ערר - תכנון ובנייה ת"א-יפו) 81002-01-21 **אברהם אגסי נ' הועדה + המקומית** (נבו 25.9.2025)` (`db.py:1067-1068`). + +הציטוט הוא **שדה נגזר לתצוגה** — מכיל את המזהה אך גם צדדים, ערכאה, ותאריך-פרסום. הוא **לעולם +אינו המפתח**. אחסון מחרוזת-ציטוט בשדה-המזהה שובר את הנרמול ([G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה)), +מערבב תצוגה עם זהות (פוגע ב-1NF — ערך לא-אטומי בשדה-מפתח), ומונע התאמת-שוויון מול המספר +המנורמל. + +--- + +## 4. Invariants של התחום + +### INV-ID1: `case_number` מנורמל בכתיבה — התאמה-סלחנית משנית +**כלל:** `case_number` מנורמל לצורה קנונית יחידה **בנקודת-הכתיבה** (trim · prefix-strip · +`/`→`-` · padding), והשוואה-בקריאה היא שוויון מול הצורה הקנונית. **התאמה-סלחנית-בקריאה היא +נוחות משנית בלבד** — היא בולעת קלט-משתמש רב-צורני, ואינה תחליף לנרמול-בכתיבה ([G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה), +כלל-ההנדסה "נרמול לא תיקון-תסמין", חוקה §6). +**מקורות:** SSOT (Single Source of Truth — normalization principle) · E.F. Codd, First Normal +Form (CACM 13(6), 1970) · Martin Kleppmann, *Designing Data-Intensive Applications* (O'Reilly, +2017) | סטטוס: verified +**אכיפה:** נרמול-בכתיבה בנקודת-הקליטה ([01-ingest.md](01-ingest.md)) + אילוצי-ייחודיות על +המפתח הקנוני (`uq_cases_number_proc`, `db.py:923-924`; partial unique `case_law`, `db.py:904-909`). +**הפרה ידועה:** `_normalize_case_number` (`db.py:1196-1211`) מנרמל **בקריאה בלבד** ("tolerant +lookup", `db.py:1197`), ו-`get_case_by_number` (`db.py:1214-1231`) משווה two-pass (`case_number=$1` +**OR** `replace(btrim(case_number),'/','-')=$2`, `db.py:1223-1224`) — אין מסלול-כתיבה שמקנן את +הערך המאוחסן. תוצאה: `8126-25` לא נמצא מול האמיתי `8126-03-25` (padding לא מכוסה בנרמול-הקריאה) +→ ממצא ל-[audit](../audit-report.md). + +### INV-ID2: אין ציטוט-מלא כמזהה — הציטוט שדה-תצוגה נגזר +**כלל:** אף ישות **אינה** משתמשת במחרוזת-ציטוט-מלאה כמזהה. שדה-המזהה מכיל מספר-תיק מנורמל +בלבד; הציטוט-המלא חי בשדה ייעודי נפרד (`citation_formatted`, `db.py:1070`) ככלי-תצוגה נגזר +([G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה), [INV-DM2](02-data-model.md#inv-dm2-מזהה-קנוני-יחיד-לכל-ישות)). +**מקורות:** SSOT (Single Source of Truth — normalization principle) · E.F. Codd, First Normal +Form (CACM 13(6), 1970) · Martin Kleppmann, *Designing Data-Intensive Applications* (O'Reilly, +2017) | סטטוס: verified +**אכיפה:** הפרדת-שדות ב-schema — מזהה ב-`case_number` (אילוצי-ייחודיות, `db.py:904-909,923-924`), +ציטוט ב-`citation_formatted` בלבד (`db.py:1070`); נרמול-בכתיבה שדוחה מחרוזת-ציטוט בשדה-המזהה. +**הפרה ידועה:** החלטות "סופר" נקלטו עם **ציטוט-מלא מאוחסן כ-`case_number`** (שדה-המזהה מכיל +את מחרוזת-הציטוט במקום מספר-תיק מנורמל) — חיפוש מול המספר המנורמל נכשל, והפער מתגלגל ל-INV-ID1 +(`_normalize_case_number` רק מטליא בקריאה) → ממצא ל-[audit](../audit-report.md). + +--- + +## 5. מצב קיים מול יעד — audit-findings + +ההבדלים בין הקוד בפועל ל-TARGET. **אלו תסמינים, לא התנהגויות תקינות.** כל פריט אומת מול `db.py`. + +- **נרמול בצד-הקריאה בלבד.** `_normalize_case_number` (`db.py:1196-1211`) מתואר במפורש כ- + "tolerant lookup" (`db.py:1197`) — מסיר קידומת לפני הספרה הראשונה, trim, ו-`/`→`-` — אך + **אינו מנרמל את הערך המאוחסן**. `get_case_by_number` (`db.py:1214-1231`) בונה סביבו two-pass + (exact `OR` normalized, `db.py:1223-1224`). **תסמין:** הנרמול חי כתיקון-תסמין בקריאה ולא + כקנוניזציה-בכתיבה, בניגוד ל-[G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה) וכלל-ההנדסה + §6. **יעד:** מסלול-כתיבה שמנרמל את `case_number` (כולל padding) בנקודת-הקליטה; הקריאה הופכת + להשוואת-שוויון פשוטה. +- **padding לא מכוסה אפילו בנרמול-הקריאה.** הנרמול הקיים מטפל ב-prefix/separator/trim בלבד, + לא ב-padding מחוז/שנה — ולכן `8126-25` ↔ `8126-03-25` נכשל גם ב-two-pass. **יעד:** padding + כחלק מהצורה הקנונית (§1), מנורמל בכתיבה לכל הרשומות. +- **ציטוט-מלא כ-`case_number` (מורשת).** השדה המקורי `case_number TEXT UNIQUE NOT NULL` + (`cases` `db.py:76`, `case_law` `db.py:368`) לא אכף צורה — מה שאפשר אחסון מחרוזת-ציטוט בשדה + זה (החלטות "סופר"). הוחלף ב-partial unique מודע-`source_kind` ב-V15 (`db.py:902-909`), אך + **ללא ולידציית-צורה בכתיבה**. **יעד:** ולידציית-כתיבה שדוחה ערך שאינו מספר-תיק מנורמל ומפנה + ציטוט ל-`citation_formatted`. +- **שני מרחבי-מזהים, סיכון-בלבול בקוד-קריאה.** `get_case_by_number` (`db.py:1214`) פונה + ל-`cases` בלבד; `get_case_law_by_citation` (`db.py:2503`) פונה ל-`case_law` בלבד — נכון, אך + שמות-הפונקציות אינם מבדילים את מרחב-המזהים בבירור. **יעד:** תיעוד מפורש (קובץ זה) + עקביות + שמות שמשקפת `cases` מול `case_law` כשני מרחבים נפרדים ללא FK. + +--- + +## 6. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — [G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה) + (מזהה קנוני מנורמל בכתיבה) + כלל-ההנדסה "נרמול לא תיקון-תסמין" (§6). +- [02-data-model.md](02-data-model.md) — [INV-DM2](02-data-model.md#inv-dm2-מזהה-קנוני-יחיד-לכל-ישות) + (מזהה קנוני יחיד) + החוזה הקונקרטי של `case_law`; X1 הוא ה-deep-dive על אותו מזהה. +- [01-ingest.md](01-ingest.md) — נקודת-הכתיבה שבה הנרמול-בכתיבה צריך להיאכף. +- [X5-audit-provenance.md](X5-audit-provenance.md) — עקיבוּת-מקור (הציטוט כשדה-תצוגה נגזר). From f0fd405f4e9fcdd8d959a39623a75aca10b3bf79 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 16:47:19 +0000 Subject: [PATCH 18/30] docs(spec): X2-multi-company sync rules Co-Authored-By: Claude Opus 4.8 --- docs/spec/X2-multi-company.md | 157 ++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 docs/spec/X2-multi-company.md diff --git a/docs/spec/X2-multi-company.md b/docs/spec/X2-multi-company.md new file mode 100644 index 0000000..15277d9 --- /dev/null +++ b/docs/spec/X2-multi-company.md @@ -0,0 +1,157 @@ +# X2 — מודל רב-החברתי וכללי ה-Sync (Multi-Company & Sync) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) והוא ה-deep-dive על **המבנה הרב-חברתי** +של עוזר משפטי — שתי החברות (CMP/CMPA), 14 הסוכנים, ואיך שינוי-הגדרות מפושט מ-Master ל-Mirror. +הוא אוכף את [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) (מקור-אמת +יחיד — אין מסלולים מקבילים מתפצלים) בהקשר של תצורת-סוכנים: שתי החברות הן שתי העתקות של אותה +מערכת, ואסור להן להתפצל (drift). + +> **invariant פרויקטלי-תפעולי.** ה-invariants כאן הם **עובדות על איך המערכת *הזו* מנוהלת** +> רב-חברתית — לא תאוריה הנדסית כללית ולא תוכן משפטי. אין סמכות חיצונית ל"איך מסנכרנים +> CMP↔CMPA"; לכן הם נושאים שדה `מקור-סמכות` = הראנבוקים והקוד של הפרויקט עצמו ([CLAUDE.md](../../CLAUDE.md), +> [HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md), [scripts/sync_agents_across_companies.py](../../scripts/sync_agents_across_companies.py)) +> — **לא** ≥3 מקורות חיצוניים ו**ללא** סטטוס verified/UNVERIFIED. אבל כל invariant **נקשר +> לעיקרון הגלובלי שהוא משרת**: כלל אי-ה-drift הוא מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים). + +--- + +## 1. שתי החברות: Master מול Mirror + +Paperclip מחייב `agents.company_id NOT NULL` — אין סוכנים משותפים. כדי לשרת את שני סוגי +העררים, המערכת מורצת כ**שתי חברות** נפרדות, כל אחת עם מערך-סוכנים מלא משלה: + +| ממד | CMP — **Master** | CMPA — **Mirror** | +|------|------------------|-------------------| +| תפקיד | מקור-האמת לתצורת-סוכנים | העתקה מסונכרנת מ-Master | +| COMPANY_ID | `42a7acd0-30c5-4cbd-ac97-7424f65df294` | `8639e837-4c9d-47fa-a76b-95788d651896` | +| סוגי תיקים | רישוי ובנייה | היטל השבחה + פיצויים ס'197 | +| טווח-מספרים | **1xxx** | **8xxx, 9xxx** | +| CEO Agent ID | `752cebdd-6748-4a04-aacd-c7ab0294ef33` | `cdbfa8bc-3d61-41a4-a2e7-677ec7d34562` | + +(המקור: [HEARTBEAT.md §1](../../.claude/agents/HEARTBEAT.md), שורות 38–44; מזהי-החברות מקודדים גם +ב-[sync_agents_across_companies.py:62-63](../../scripts/sync_agents_across_companies.py).) + +**14 סוכנים = 7 × 2.** כל חברה מחזיקה את אותם 7 תפקידי-סוכן (CEO, writer, analyst, researcher, +qa, proofreader, exporter — ראה [X4-agents.md](X4-agents.md)). מאחר ש-`company_id` הוא `NOT NULL`, +כל תפקיד מיוצג בשתי **רשומות-סוכן נפרדות** — אחת ל-CMP, אחת ל-CMPA. אין רשומה משותפת. + +**Master = CMP, Mirror = CMPA.** התצורה נכתבת ומתוחזקת בחברת ה-Master (CMP, 1xxx), והסנכרון +הוא **חד-כיווני** CMP → CMPA ([sync...py:1-7,361-362](../../scripts/sync_agents_across_companies.py)). + +--- + +## 2. ניתוב לפי חברה — סינון ב-`company_id` + +הזרימה התפעולית נאכפת לפי `$PAPERCLIP_COMPANY_ID` של הסוכן הפועל ([HEARTBEAT.md §1](../../.claude/agents/HEARTBEAT.md)): + +- `42a7acd0…` → הסוכן מטפל **רק** בתיקי 1xxx; `8639e837…` → **רק** בתיקי 8xxx/9xxx (שורות 43–44). +- **אסור** ליצור פרויקט/issue/תוכן לתיק מחוץ לטווח-החברה (שורה 45); issue שמכוון לתיק מחוץ + לטווח → סירוב מנומס ב-comment + העֵרת ה-CEO של החברה הנכונה (שורה 46). +- **CEO שונה לכל חברה** — בחירת ה-CEO ל-wakeup נגזרת מ-`$PAPERCLIP_COMPANY_ID`, **לעולם לא** + UUID hardcoded ([HEARTBEAT.md §4ג](../../.claude/agents/HEARTBEAT.md), שורות 143–150). +- **גבול-חברה נאכף בצד-Paperclip:** wakeup לחברה אחרת נדחה — `Agent key cannot access another + company` ([HEARTBEAT.md §4ג](../../.claude/agents/HEARTBEAT.md), שורה 157). + +--- + +## 3. כלל ה-Sync — אחרי כל שינוי-הגדרות ב-Master + +> **טריגר:** כל שינוי ב-`adapter_config`, `runtime_config`, `budget_monthly_cents`, או skills +> של סוכן ב-Master (UI / SQL / API). מקור: סעיף "Cross-company agent sync" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) +> וב-[root CLAUDE.md](../../../CLAUDE.md). + +הפעולה החובה — קודם בדיקה, אז החלה: + +```bash +PAPERCLIP_BOARD_API_KEY=$(…infisical…) \ + python ~/legal-ai/scripts/sync_agents_across_companies.py --verify # drift report +PAPERCLIP_BOARD_API_KEY=$(…) \ + python ~/legal-ai/scripts/sync_agents_across_companies.py --apply # backup + apply +``` + +**מה הסקריפט עושה** (מאומת מול הקוד): + +- **חד-כיווני CMP → CMPA**, סינכרון של שדות-תצורה מוגדרים: top-level (`budget_monthly_cents`, + `metadata`, `icon`, `title`, `role`), מפתחות `adapter_config` נבחרים (`model`, `effort`, + `timeoutSec`, `maxTurnsPerRun`, נתיבי-instructions, `cwd`…), ו-`runtime_config` כ-full-replace + ([sync...py:66-75,124-160](../../scripts/sync_agents_across_companies.py)). שדות פר-חברה + (`id`, `company_id`, `adapter_type`, `agent_api_keys`, `status`, `spent_monthly_cents`, + `permissions`) **אינם** מסונכרנים ([sync...py:24-29](../../scripts/sync_agents_across_companies.py)). +- **מבוסס-API, לא DB ישיר.** ה-PATCH דרך `PATCH /api/agents/{id}` וה-skills דרך + `POST /api/agents/{id}/skills/sync` עם `Authorization: Bearer` ([sync...py:204-237](../../scripts/sync_agents_across_companies.py)). +- **מסנן skills מקומיים שלא קיימים ב-Mirror.** `desiredSkills` מושוות כ-subset; skills מקומיים + של CMP (למשל `local/eba6210d5a/legal-decision`) שלא קיימים ב-CMPA נשמטים עם אזהרה + ([sync...py:138-154,194-195](../../scripts/sync_agents_across_companies.py)). +- **יוצר revisions.** סנכרון skills עובר דרך endpoint ייעודי שמייצר `skill-sync` revision + ([sync...py:277-284](../../scripts/sync_agents_across_companies.py)). +- **idempotent + אל-כשל.** `--verify`/`--dry-run` כברירת-מחדל, גיבוי `pg_dump` לפני `--apply`, + pre-flight על קבצי-instructions, ו-re-verify אוטומטי אחרי ההחלה ([sync...py:9,163-173,408-465](../../scripts/sync_agents_across_companies.py)). +- **מדלג על סוכן עם `adapter_type` שונה בין החברות.** אם ל-Master ול-Mirror `adapter_type` + שונה → `SKIPPING`, ללא סנכרון ([sync...py:387-389](../../scripts/sync_agents_across_companies.py)). + זו המלכודת ב-INV-MC1 (להלן). + +--- + +## 4. Invariants של התחום (פרויקטלי-תפעולי) + +### INV-MC1: תצורת-סוכן ב-Master מפושטת ל-Mirror — אין drift בין החברות +**כלל:** כל שינוי ב-`adapter_config` / `runtime_config` / `budget_monthly_cents` / skills של +סוכן בחברת ה-Master (CMP) **חייב** להיות מפושט ל-Mirror (CMPA) דרך סקריפט ה-Sync המבוסס-API +(`--verify` ואז `--apply`). שתי החברות **לא מתפצלות** — הן שתי העתקות מסונכרנות של אותה תצורה +(מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) — מקור-אמת +יחיד, אין מסלולים מקבילים מתפצלים; וכלל-ההנדסה "סימטריה", [חוקה §6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)). +**מקור-סמכות:** סעיף "Cross-company agent sync" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) + +ב-[root CLAUDE.md](../../../CLAUDE.md) + +[scripts/sync_agents_across_companies.py](../../scripts/sync_agents_across_companies.py) + +[HEARTBEAT.md §1, §4ג](../../.claude/agents/HEARTBEAT.md). (invariant פרויקטלי-תפעולי — ללא +פרוטוקול ≥3-המקורות; משרת את העיקרון הגלובלי G2.) +**אכיפה:** סקריפט ה-Sync (idempotent, מבוסס-API, גיבוי+re-verify) — מורץ **ידנית** אחרי כל +שינוי-תצורה ב-Master. **אין אכיפה אוטומטית** (ראה §5). +**הפרה ידועה:** הסקריפט **מדלג** על סוכן ש-`adapter_type` שונה בין CMP ל-CMPA +([sync...py:387-389](../../scripts/sync_agents_across_companies.py)). כשמעבירים סוכן ל-`deepseek_local` +ב-Master, ה-Mirror נשאר על ה-adapter הישן והסנכרון מדלג עליו — **חובה להחיל את שינוי ה-`adapter_type` +ידנית בשתי החברות לפני הרצת ה-Sync** ([CLAUDE.md "External adapters — deepseek_local"](../../CLAUDE.md)), +אחרת נוצר drift שקט באותו סוכן. + +### INV-MC2: אין סוכן משותף — רשומה נפרדת לכל חברה +**כלל:** סוכן **לעולם אינו רשומה משותפת** בין החברות. כל אחד מ-7 התפקידים מיוצג בשתי +רשומות-סוכן נפרדות (CMP + CMPA), שכן Paperclip מחייב `agents.company_id NOT NULL`. הסנכרון +מעתיק *ערכי-תצורה* בין שתי רשומות — לא ממזג אותן לרשומה אחת (תואם [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים): +מקור-אמת יחיד לתצורה, גם כשהיא משוכפלת על פני רשומות). +**מקור-סמכות:** סעיף "Cross-company agent sync" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) (14 agents = 7 × 2; +`agents.company_id NOT NULL`) + [sync...py:4-7,83-103](../../scripts/sync_agents_across_companies.py) +(שולף מערכי-סוכן נפרדים לכל `company_id`) + [HEARTBEAT.md §1](../../.claude/agents/HEARTBEAT.md). +(invariant פרויקטלי-תפעולי.) +**אכיפה:** אילוץ `company_id NOT NULL` בצד-Paperclip; הסקריפט מתאים סוכנים בין החברות לפי +`name` ולעולם לא יוצר רשומה משותפת ([sync...py:372,383-385](../../scripts/sync_agents_across_companies.py) +— "we never auto-create"). +**הפרה ידועה:** — + +--- + +## 5. מצב קיים מול יעד — פער אכיפה + +ה-Sync הוא **ידני ולא-נאכף**. הסקריפט עצמו בנוי "אל-כשל" (dry-run כברירת-מחדל, גיבוי, +re-verify), אך **שום מנגנון לא מכריח** הרצה אחרי שינוי-תצורה ב-Master: + +- **drift אם שוכחים.** שינוי `adapter_config`/`runtime_config`/budget/skills ב-CMP בלי הרצת + `--apply` משאיר את CMPA מאחור — שתי החברות מתפצלות בשקט, בניגוד ל-INV-MC1. **יעד:** טריגר/ + בדיקת-בריאות תקופתית שמריצה `--verify` ומדווחת drift (היום ההרצה תלויה בזיכרון המפעיל). +- **מלכודת `adapter_type`-skip.** סוכן עם `adapter_type` שונה בין החברות נשמט מהסנכרון + ([sync...py:387-389](../../scripts/sync_agents_across_companies.py)) — ה-`--verify` ידווח + `SKIPPING`, אך אם המפעיל לא יחיל את שינוי ה-adapter ידנית בשתי החברות, הסוכן יישאר drifted. + **יעד:** אזהרת-SKIPPING שמתבלטת ב-report + צ'קליסט-ידני (כבר מתועד ב-[CLAUDE.md](../../CLAUDE.md)). + +--- + +## 6. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) + (מקור-אמת יחיד, אין מסלולים מקבילים מתפצלים) + כלל-ההנדסה "סימטריה" ([§6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)). +- [X4-agents.md](X4-agents.md) — מפת 7 תפקידי-הסוכן שמשוכפלים על פני שתי החברות. +- [X3-integration-deploy.md](X3-integration-deploy.md) — Paperclip (wakeup, ניתוב comments) ו-deploy; + ה-wakeup-per-company משלים את הניתוב כאן. +- [scripts/sync_agents_across_companies.py](../../scripts/sync_agents_across_companies.py) — מימוש ה-Sync. +- [legal-ai/CLAUDE.md](../../CLAUDE.md) + [root CLAUDE.md](../../../CLAUDE.md) — סעיף + "Cross-company agent sync" + "External adapters — deepseek_local" (מלכודת ה-adapter_type). +- [.claude/agents/HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md) — §1 (סינון-חברה) + §4ג (wake CEO לפי חברה). From a0004f027434c6d7315f4ea21a9f91a0be9551bf Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 16:49:58 +0000 Subject: [PATCH 19/30] =?UTF-8?q?docs(spec):=20constitution=20=E2=80=94=20?= =?UTF-8?q?document=20third=20authority=20model=20(project-operational)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X2/X3/X4 invariants are facts about this system's own integration/ops (no external authority); they use מקור-סמכות=project runbooks, tied to a global engineering invariant. Co-Authored-By: Claude Opus 4.8 --- docs/spec/00-constitution.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/spec/00-constitution.md b/docs/spec/00-constitution.md index 934f068..01389a5 100644 --- a/docs/spec/00-constitution.md +++ b/docs/spec/00-constitution.md @@ -51,8 +51,12 @@ **הפרה ידועה:** <דוגמה מהמערכת, אם יש — מקשר ל-audit; אחרת "—"> ``` -> **שדה המקורות לפי סוג:** invariant הנדסי → `מקורות` = ≥3 סמכויות חיצוניות + `סטטוס`. -> invariant תוכן-משפטי → `מקור-סמכות` = היו"ר + מסמכי-הפרויקט (ללא סטטוס-אימות חיצוני). +> **שדה המקורות לפי סוג invariant (שלושה מודלי-סמכות):** +> 1. **הנדסי** (תאוריה כללית — נתונים/אחזור/ארכיטקטורה) → `מקורות` = ≥3 סמכויות חיצוניות + `סטטוס`. +> 2. **תוכן-משפטי** → `מקור-סמכות` = היו"ר + מסמכי-הפרויקט (ללא סטטוס-אימות חיצוני). +> 3. **פרויקטלי-תפעולי** (עובדות על האינטגרציה/התפעול של *מערכת זו* — אין להן סמכות +> חיצונית, למשל "wakeup דרך API") → `מקור-סמכות` = ה-runbooks של הפרויקט +> (CLAUDE.md, HEARTBEAT.md, סקריפטים), **קשור** ל-invariant הנדסי גלובלי שאותו הוא מיישם. --- From 951f2366e6ee3438a982e281f67c802fdd211117 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 16:53:01 +0000 Subject: [PATCH 20/30] docs(spec): X3-integration-deploy Co-Authored-By: Claude Opus 4.8 --- docs/spec/X3-integration-deploy.md | 212 +++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 docs/spec/X3-integration-deploy.md diff --git a/docs/spec/X3-integration-deploy.md b/docs/spec/X3-integration-deploy.md new file mode 100644 index 0000000..3c8cdea --- /dev/null +++ b/docs/spec/X3-integration-deploy.md @@ -0,0 +1,212 @@ +# X3 — אינטגרציה ו-Deploy (Integration & Deploy) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) והוא ה-deep-dive על **שני ממדי-התפעול** +של עוזר משפטי: (א) **האינטגרציה עם Paperclip** — איך המערכת מעירה סוכנים, איך תגובות-משתמש +מנותבות, ואיך שינוי-סטטוס תיק מתפרסם חזרה; (ב) **מודל ה-Deploy** — שני מודלי-הרצה הדו-קיימים +על שרת Nautilus (Coolify-Docker מול pm2-מקומי) ומחזור-השינוי של legal-ai. + +> **invariant פרויקטלי-תפעולי.** ה-invariants כאן הם **עובדות על איך המערכת *הזו* משתלבת +> ונפרסת** — לא תאוריה הנדסית כללית ולא תוכן משפטי. אין סמכות חיצונית ל"איך מעירים סוכן +> Paperclip" או "איך פורסים את legal-ai"; לכן הם נושאים שדה `מקור-סמכות` = הראנבוקים והקוד +> של הפרויקט עצמו ([root CLAUDE.md](../../../CLAUDE.md), [legal-ai/CLAUDE.md](../../CLAUDE.md), +> [HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md), [MEMORY reference_paperclip_wakeup](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_paperclip_wakeup.md), +> ו-[web/paperclip_api.py](../../web/paperclip_api.py)) — **לא** ≥3 מקורות חיצוניים ו**ללא** +> סטטוס verified/UNVERIFIED. אבל כל invariant **נקשר לעיקרון הגלובלי שהוא משרת**: כלל +> ה-wakeup-דרך-API-בלבד הוא מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) +> (מסלול קנוני יחיד; ה-DB-insert המקביל אסור כי הוא מתפצל מהמסלול שיוצר `heartbeat_run`). + +--- + +## 1. אינטגרציית Paperclip + +עוזר משפטי משתלב עם Paperclip בשלושה כיוונים: **wakeup** (legal-ai/אוטומציה → סוכן), +**ניתוב comments** (משתמש → CEO → סוכן), ו-**webhook יוצא** (legal-ai → פלאגין). + +### 1א. Wakeup — תמיד דרך API, לעולם לא דרך DB + +הנתיב הקנוני היחיד להערת סוכן הוא `POST /api/agents/{agent-id}/wakeup` עם `payload` המכיל +`issueId` ([root CLAUDE.md](../../../CLAUDE.md) "Wakeup API"; [legal-ai/CLAUDE.md](../../CLAUDE.md) +"Wakeup API"; [HEARTBEAT.md §4ד, שורות 152–158](../../.claude/agents/HEARTBEAT.md)): + +```bash +~/legal-ai/scripts/pc.sh POST "/api/agents/$CEO_ID/wakeup" \ + '{"source":"automation","triggerDetail":"system","reason":"...", + "payload":{"issueId":"...","mutation":"comment","commentId":"..."}}' +``` + +- **`POST .../wakeup`, לא `/wake`** — שם-הנתיב מדויק ([legal-ai/CLAUDE.md](../../CLAUDE.md)). +- **חובה `payload.issueId`** — בלעדיו הסוכן מתעורר בלי הקשר (בלי תיק, בלי issue, בלי `cwd` + נכון) ([HEARTBEAT.md שורה 156](../../.claude/agents/HEARTBEAT.md)). +- **אסור `INSERT INTO agent_wakeup_requests` ישיר** — insert ל-DB יוצר רשומת-בקשה בלבד **בלי + `heartbeat_run`**, והסוכן **לא יתעורר לעולם** ([HEARTBEAT.md שורה 158](../../.claude/agents/HEARTBEAT.md); + [reference_paperclip_wakeup](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_paperclip_wakeup.md)). + זהו בדיוק "מסלול מקביל מתפצל" שאסור לפי [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים). +- **CEO לכל חברה** — מזהה-ה-CEO ל-wakeup נגזר מ-`$PAPERCLIP_COMPANY_ID`, לעולם לא UUID + hardcoded; wakeup לחברה אחרת נדחה (`Agent key cannot access another company`) + ([HEARTBEAT.md §4ג](../../.claude/agents/HEARTBEAT.md); ראה [X2-multi-company.md §2](X2-multi-company.md)). + +### 1ב. ניתוב comments — דרך ה-CEO + +תגובת-משתמש על issue ב-Paperclip **אינה** מנותבת ישירות לסוכן-המטרה. הזרימה +([root CLAUDE.md](../../../CLAUDE.md) "Comment routing"; [legal-ai/CLAUDE.md](../../CLAUDE.md)): + +``` +user comment → plugin-legal-ai → ctx.agents.invoke() מעיר CEO + → CEO קורא comment, מחליט ניתוב, יוצר issue לסוכן המתאים +``` + +- ה-CEO הוא נקודת-הניתוב היחידה — סוכן-משנה לא מקבל עבודה ישירות מ-comment. +- כל סוכן **חייב** לקרוא comments אחרונים לפני שהוא מתחיל עבודה ([HEARTBEAT שלבים 2b–2c](../../.claude/agents/HEARTBEAT.md)). + +### 1ג. Webhook יוצא — עדכון סטטוס תיק לפלאגין + +כשסטטוס תיק משתנה דרך `PUT /api/cases/{case_number}`, הבקאנד שולח webhook אסינכרוני +לפלאגין כ-BackgroundTask, fire-and-forget: + +``` +PUT /api/cases/{n} → [BackgroundTask] emit_case_status_webhook() + → POST /api/plugins/marcusgroup.legal-ai/webhooks/case-status + → plugin-legal-ai/onWebhook() → comment בעברית + CEO wakeup (כש-qa_failed) +``` + +מאומת מול הקוד: + +- ה-call-site: [web/app.py:2045-2061](../../web/app.py) — ה-webhook מתוזמן רק כש-`old_status + != new_status`, ו-`company_id` נגזר מ-prefix מספר-התיק (`1`→licensing, `8/9`→betterment). +- המימוש: [web/paperclip_api.py:87-117](../../web/paperclip_api.py) — `emit_case_status_webhook` + קורא ל-`pc_request("POST", "/api/plugins/.../webhooks/case-status", ...)` עם `timeout=5.0`, + בלוק `try/except` שמתעד `logger.warning` ולעולם לא raise (לא חוסם את הקורא). +- אותו דפוס משרת אירועים נוספים: `emit_missing_precedent_webhook` + ([paperclip_api.py:120-165](../../web/paperclip_api.py)) ו-`emit_export_complete_webhook` + ([paperclip_api.py:168+](../../web/paperclip_api.py)). + +### 1ד. כל קריאת-API דרך helper — לא curl/httpx ישיר + +קריאות ל-Paperclip עוברות תמיד דרך helper, לא דרך לקוח גולמי: + +- **bash (סוכנים):** `~/legal-ai/scripts/pc.sh [BODY]` — מוסיף אוטומטית + `Authorization: Bearer`, `X-Paperclip-Run-Id`, `Content-Type`, ו-base URL + ([HEARTBEAT.md §0, שורות 15–32](../../.claude/agents/HEARTBEAT.md); [scripts/pc.sh:8-9,39-40](../../scripts/pc.sh)). +- **Python (FastAPI):** `from web.paperclip_api import pc_request` — בונה headers דרך + `_build_headers` ([paperclip_api.py:47-84](../../web/paperclip_api.py)), משתמש ב-board API key. +- **למה:** ה-skill הרשמי דורש `X-Paperclip-Run-Id` בכל קריאה משנה issue (audit trail); + ה-helper מבטיח עקביות + תאימות ל-board API keys long-lived שלא נושאות JWT claims + ([legal-ai/CLAUDE.md](../../CLAUDE.md) "קריאות API — תמיד דרך helper"). + +--- + +## 2. מודל ה-Deploy — שני מודלים דו-קיימים + +על שרת Nautilus דרים **שני מודלי-הרצה**. ערבוב ביניהם הוא הטעות הנפוצה ביותר +([root CLAUDE.md](../../../CLAUDE.md) "Deploy architecture"; [legal-ai/CLAUDE.md](../../CLAUDE.md) +"ארכיטקטורת Deploy"). + +| ממד | legal-ai (web + web-ui) | Paperclip + legal-chat-service | +|------|--------------------------|--------------------------------| +| מודל | **Coolify-managed (Docker)** | **PM2-managed (Node/Python מקומי)** | +| מחזור-שינוי | commit → push → Gitea Actions build → Coolify redeploy (~2–4 דק') | עריכה → `pm2 restart` | +| Coolify UUID | `gyjo0mtw2c42ej3xxvbz8zio` | — | +| build_pack | **`dockerimage`** (לא `dockerfile`) | — | +| פורטים | Next.js `:3000` (חשוף) + FastAPI `:8000` (פנימי) | Paperclip `localhost:3100`; legal-chat-service `127.0.0.1:8770` (loopback) | +| הרצה מקומית | **אין** — אין venv של Python על ה-host; אסור `uvicorn`/`next dev` לפרוד | יש; מתחזק דרך pm2 | + +### 2א. מחזור-השינוי של legal-ai (Coolify dockerimage) + +שינוי קוד ב-`web/` או `web-ui/` **לא נכנס לתוקף** עד שמריצים את כל הצעדים, בסדר: + +1. `git commit` + `git push origin main` ל-Gitea. +2. Gitea Actions בונה image ודוחף ל-registry (`gitea.nautilus.marcusgroup.org/...`). +3. ה-workflow מפעיל Coolify redeploy דרך API (UUID `gyjo0mtw2c42ej3xxvbz8zio`). +4. ~2–4 דקות end-to-end. בדיקה: `curl -s https://legal-ai.nautilus.marcusgroup.org/api/health`. + +- **אסור** לנסות `uvicorn`/`next dev` לפרוד — הקונטיינר מספק את שני התהליכים; אין סביבת + Python על ה-host ([root CLAUDE.md](../../../CLAUDE.md); [legal-ai/CLAUDE.md](../../CLAUDE.md)). +- **endpoint חדש ≠ זמין ל-UI.** הוספת endpoint ב-`web/app.py` היא תנאי הכרחי אך לא מספיק + לצריכה מה-frontend — חובה `npm run api:types` בתוך `web-ui/` כדי לחדש את ה-OpenAPI types + ([root CLAUDE.md](../../../CLAUDE.md), שורה 89; [legal-ai/CLAUDE.md](../../CLAUDE.md)). + +### 2ב. legal-chat-service ו-host.docker.internal + +legal-chat-service (`127.0.0.1:8770`, pm2) הוא גשר host-side שעוטף את `claude` CLI ב-streaming +לטאב הצ'אט ב-`/training`. הקונטיינר מגיע אליו דרך `host.docker.internal:8770` — ולכן ה-Service +Definition של legal-ai ב-Coolify **חייב** לכלול `extra_hosts: host.docker.internal:host-gateway`, +אחרת ה-proxy יקבל `ConnectError` ([root CLAUDE.md](../../../CLAUDE.md); [legal-ai/CLAUDE.md](../../CLAUDE.md) +"legal-chat-service"). הנחת-היסוד של "קריאות LLM רק ממקומי" נשמרת — ראה +[reference: claude_session local only](../../../.claude/projects/-home-chaim-legal-ai/memory/feedback_claude_session_local_only.md). + +--- + +## 3. Invariants של התחום (פרויקטלי-תפעולי) + +### INV-INT1: wakeup דרך API בלבד — DB-insert אסור +**כלל:** הערת סוכן Paperclip **חייבת** לעבור דרך `POST /api/agents/{agent-id}/wakeup` עם +`payload.issueId`. **אסור** `INSERT INTO agent_wakeup_requests` ישיר — insert ל-DB אינו יוצר +`heartbeat_run`, ולכן הסוכן **לא יתעורר לעולם**. זהו המסלול הקנוני היחיד; ה-DB-insert הוא +מסלול-מקביל-מתפצל אסור (מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) +— מקור-אמת/מסלול קנוני יחיד; וכלל-ההנדסה "סימטריה", [חוקה §6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)). +**מקור-סמכות:** "Wakeup API" ב-[root CLAUDE.md](../../../CLAUDE.md) + ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) + +[reference_paperclip_wakeup](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_paperclip_wakeup.md) + +[HEARTBEAT.md §4ד, שורות 152–158](../../.claude/agents/HEARTBEAT.md). (invariant פרויקטלי-תפעולי — +ללא פרוטוקול ≥3-המקורות; משרת את העיקרון הגלובלי G2.) +**אכיפה:** קריאות-wakeup דרך `pc.sh`/`pc_request` בלבד; `payload.issueId` חובה; בדיקה +ש-`heartbeat_run` נוצר. **אין אכיפה סכמתית** שתחסום insert ישיר ל-`agent_wakeup_requests` — +המניעה היא נוהל (ראה §4). +**הפרה ידועה:** insert ישיר ל-`agent_wakeup_requests` (fallback ישן) → רשומה בלי `heartbeat_run`, +הסוכן נשאר רדום ([reference_paperclip_wakeup](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_paperclip_wakeup.md)). + +### INV-INT2: שינוי-קוד legal-ai נכנס לתוקף רק דרך commit→push→Coolify deploy +**כלל:** שינוי קוד ב-`web/` או `web-ui/` **לא נכנס לתוקף** עד `git commit` + `git push origin main` ++ build ב-Gitea Actions + Coolify redeploy (build_pack `dockerimage`, UUID `gyjo0mtw2c42ej3xxvbz8zio`). +**אין** הרצת `uvicorn`/`next dev` מקומית לפרוד. endpoint חדש ב-`web/app.py` דורש גם +`npm run api:types` ב-`web-ui/` כדי להיחשף ל-UI. +**מקור-סמכות:** "Deploy architecture" ב-[root CLAUDE.md](../../../CLAUDE.md) (UUID, dockerimage, +no local uvicorn, api:types) + "ארכיטקטורת Deploy" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) + +[reference: deployment](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_deployment.md). +(invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות.) +**אכיפה:** pipeline Gitea Actions → Coolify (אוטומטי בדחיפה ל-main); בדיקה ידנית +`curl .../api/health` אחרי deploy. **אין** מסלול-פריסה חלופי. +**הפרה ידועה:** בדיקת שינוי מול הרצה מקומית שלא קיימת — הקוד בפרוד נשאר ישן עד deploy; וכן +drift אפשרי Infisical↔Coolify env (env לא מתעדכן אוטומטית מ-Infisical, ראה +[reference: infisical/coolify drift](../../../.claude/projects/-home-chaim-legal-ai/memory/feedback_infisical_coolify_drift.md)). + +### INV-INT3: כל קריאת-Paperclip דרך helper — לא curl/httpx ישיר +**כלל:** קריאות ל-Paperclip API עוברות **תמיד** דרך helper — `pc.sh` (bash/סוכנים) או +`pc_request` (Python/FastAPI) — ולעולם לא `curl`/`httpx` גולמי. ה-helper מזריק `Authorization`, +`X-Paperclip-Run-Id` (audit), ו-`Content-Type` באופן עקבי, ותומך ב-board API keys long-lived +(מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) — מסלול-גישה +קנוני יחיד ל-Paperclip; ושל [G9](00-constitution.md#inv-g9-עקיבות-מקור--audit-trail-ל-ai) — +audit-trail עקבי). +**מקור-סמכות:** "קריאות API — תמיד דרך helper" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) + +[HEARTBEAT.md §0, שורות 15–32](../../.claude/agents/HEARTBEAT.md) + +[scripts/pc.sh:8-9,39-40](../../scripts/pc.sh) + [web/paperclip_api.py:47-84](../../web/paperclip_api.py). +(invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות.) +**אכיפה:** נוהל + code-review; `pc.sh` ו-`pc_request` הם נקודות-הכניסה היחידות. **אין אכיפה +אוטומטית** שתחסום `httpx.AsyncClient` ישיר ל-Paperclip בקוד חדש. +**הפרה ידועה:** — + +--- + +## 4. מצב קיים מול יעד — פער אכיפה + +האינטגרציה נשענת על **נוהל**, לא על מחסום-קוד: + +- **wakeup (INV-INT1):** אין constraint סכמתי שחוסם insert ישיר ל-`agent_wakeup_requests`; + המניעה היא ידע-נוהל ([HEARTBEAT](../../.claude/agents/HEARTBEAT.md)). **יעד:** wrapper/בדיקת-בריאות + שמסמן בקשות-wakeup ללא `heartbeat_run` תואם. +- **helper (INV-INT3):** אין linter/בדיקה שתתפוס `httpx`/`curl` ישיר ל-Paperclip בקוד חדש. + **יעד:** כלל-lint שמכריח שימוש ב-`pc_request`/`pc.sh`. + +--- + +## 5. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) + (מסלול קנוני יחיד) + [G9](00-constitution.md#inv-g9-עקיבות-מקור--audit-trail-ל-ai) (audit-trail) + + כלל-ההנדסה "סימטריה" ([§6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)). +- [X2-multi-company.md](X2-multi-company.md) — wakeup-per-company + ניתוב לפי `company_id` משלים את §1 כאן. +- [X4-agents.md](X4-agents.md) — מפת הסוכנים שה-CEO מנתב אליהם comments. +- [root CLAUDE.md](../../../CLAUDE.md) + [legal-ai/CLAUDE.md](../../CLAUDE.md) — "Wakeup API", + "Comment routing", "Deploy architecture", "קריאות API — תמיד דרך helper". +- [.claude/agents/HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md) — §0 (pc.sh), §4ג–§4ד (wake CEO + payload). +- [web/paperclip_api.py](../../web/paperclip_api.py) — `pc_request`, `emit_case_status_webhook`. +- [scripts/pc.sh](../../scripts/pc.sh) — helper ה-bash. From 7fd6d8cb9519713bfad592a55bb1742dd52eee08 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 16:56:20 +0000 Subject: [PATCH 21/30] =?UTF-8?q?docs(spec):=20X3=20=E2=80=94=20replace=20?= =?UTF-8?q?out-of-repo=20memory=20links=20with=20plain=20mentions=20(self-?= =?UTF-8?q?containment)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec/X3-integration-deploy.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/spec/X3-integration-deploy.md b/docs/spec/X3-integration-deploy.md index 3c8cdea..3fa35c6 100644 --- a/docs/spec/X3-integration-deploy.md +++ b/docs/spec/X3-integration-deploy.md @@ -9,7 +9,7 @@ > ונפרסת** — לא תאוריה הנדסית כללית ולא תוכן משפטי. אין סמכות חיצונית ל"איך מעירים סוכן > Paperclip" או "איך פורסים את legal-ai"; לכן הם נושאים שדה `מקור-סמכות` = הראנבוקים והקוד > של הפרויקט עצמו ([root CLAUDE.md](../../../CLAUDE.md), [legal-ai/CLAUDE.md](../../CLAUDE.md), -> [HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md), [MEMORY reference_paperclip_wakeup](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_paperclip_wakeup.md), +> [HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md), זיכרון `reference_paperclip_wakeup`, > ו-[web/paperclip_api.py](../../web/paperclip_api.py)) — **לא** ≥3 מקורות חיצוניים ו**ללא** > סטטוס verified/UNVERIFIED. אבל כל invariant **נקשר לעיקרון הגלובלי שהוא משרת**: כלל > ה-wakeup-דרך-API-בלבד הוא מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) @@ -39,7 +39,7 @@ נכון) ([HEARTBEAT.md שורה 156](../../.claude/agents/HEARTBEAT.md)). - **אסור `INSERT INTO agent_wakeup_requests` ישיר** — insert ל-DB יוצר רשומת-בקשה בלבד **בלי `heartbeat_run`**, והסוכן **לא יתעורר לעולם** ([HEARTBEAT.md שורה 158](../../.claude/agents/HEARTBEAT.md); - [reference_paperclip_wakeup](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_paperclip_wakeup.md)). + זיכרון `reference_paperclip_wakeup`). זהו בדיוק "מסלול מקביל מתפצל" שאסור לפי [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים). - **CEO לכל חברה** — מזהה-ה-CEO ל-wakeup נגזר מ-`$PAPERCLIP_COMPANY_ID`, לעולם לא UUID hardcoded; wakeup לחברה אחרת נדחה (`Agent key cannot access another company`) @@ -132,7 +132,7 @@ legal-chat-service (`127.0.0.1:8770`, pm2) הוא גשר host-side שעוטף א Definition של legal-ai ב-Coolify **חייב** לכלול `extra_hosts: host.docker.internal:host-gateway`, אחרת ה-proxy יקבל `ConnectError` ([root CLAUDE.md](../../../CLAUDE.md); [legal-ai/CLAUDE.md](../../CLAUDE.md) "legal-chat-service"). הנחת-היסוד של "קריאות LLM רק ממקומי" נשמרת — ראה -[reference: claude_session local only](../../../.claude/projects/-home-chaim-legal-ai/memory/feedback_claude_session_local_only.md). +זיכרון `feedback_claude_session_local_only`. --- @@ -145,14 +145,14 @@ Definition של legal-ai ב-Coolify **חייב** לכלול `extra_hosts: host.d מסלול-מקביל-מתפצל אסור (מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) — מקור-אמת/מסלול קנוני יחיד; וכלל-ההנדסה "סימטריה", [חוקה §6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)). **מקור-סמכות:** "Wakeup API" ב-[root CLAUDE.md](../../../CLAUDE.md) + ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) + -[reference_paperclip_wakeup](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_paperclip_wakeup.md) + +זיכרון `reference_paperclip_wakeup` + [HEARTBEAT.md §4ד, שורות 152–158](../../.claude/agents/HEARTBEAT.md). (invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות; משרת את העיקרון הגלובלי G2.) **אכיפה:** קריאות-wakeup דרך `pc.sh`/`pc_request` בלבד; `payload.issueId` חובה; בדיקה ש-`heartbeat_run` נוצר. **אין אכיפה סכמתית** שתחסום insert ישיר ל-`agent_wakeup_requests` — המניעה היא נוהל (ראה §4). **הפרה ידועה:** insert ישיר ל-`agent_wakeup_requests` (fallback ישן) → רשומה בלי `heartbeat_run`, -הסוכן נשאר רדום ([reference_paperclip_wakeup](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_paperclip_wakeup.md)). +הסוכן נשאר רדום (זיכרון `reference_paperclip_wakeup`). ### INV-INT2: שינוי-קוד legal-ai נכנס לתוקף רק דרך commit→push→Coolify deploy **כלל:** שינוי קוד ב-`web/` או `web-ui/` **לא נכנס לתוקף** עד `git commit` + `git push origin main` @@ -161,13 +161,13 @@ Definition של legal-ai ב-Coolify **חייב** לכלול `extra_hosts: host.d `npm run api:types` ב-`web-ui/` כדי להיחשף ל-UI. **מקור-סמכות:** "Deploy architecture" ב-[root CLAUDE.md](../../../CLAUDE.md) (UUID, dockerimage, no local uvicorn, api:types) + "ארכיטקטורת Deploy" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) + -[reference: deployment](../../../.claude/projects/-home-chaim-legal-ai/memory/reference_deployment.md). +זיכרון `reference_deployment`. (invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות.) **אכיפה:** pipeline Gitea Actions → Coolify (אוטומטי בדחיפה ל-main); בדיקה ידנית `curl .../api/health` אחרי deploy. **אין** מסלול-פריסה חלופי. **הפרה ידועה:** בדיקת שינוי מול הרצה מקומית שלא קיימת — הקוד בפרוד נשאר ישן עד deploy; וכן drift אפשרי Infisical↔Coolify env (env לא מתעדכן אוטומטית מ-Infisical, ראה -[reference: infisical/coolify drift](../../../.claude/projects/-home-chaim-legal-ai/memory/feedback_infisical_coolify_drift.md)). +זיכרון `feedback_infisical_coolify_drift`). ### INV-INT3: כל קריאת-Paperclip דרך helper — לא curl/httpx ישיר **כלל:** קריאות ל-Paperclip API עוברות **תמיד** דרך helper — `pc.sh` (bash/סוכנים) או From c881665b7c39e1a3f77d5fba609d5205b2be1882 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 16:56:39 +0000 Subject: [PATCH 22/30] =?UTF-8?q?docs(spec):=20constitution=20index=20?= =?UTF-8?q?=E2=80=94=20X3=20enforces=20G2,G9=20(operational)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- docs/spec/00-constitution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/00-constitution.md b/docs/spec/00-constitution.md index 01389a5..73e6eb1 100644 --- a/docs/spec/00-constitution.md +++ b/docs/spec/00-constitution.md @@ -231,7 +231,7 @@ Manual* (2d ed.) | סטטוס: verified | [07-learning.md](07-learning.md) | Hermes · לקחים · לולאת פידבק היו"ר · צמיחת קורפוס (quality-at-source) | G4, G10 | | [X1-identifiers.md](X1-identifiers.md) | מודל מזהים קנוני: נרמול case_number בכתיבה · cases מול case_law · פורמטי ציטוט | G1 | | [X2-multi-company.md](X2-multi-company.md) | CMP/CMPA · 14 סוכנים · כללי sync | G2 | -| [X3-integration-deploy.md](X3-integration-deploy.md) | Paperclip (wakeup, ניתוב comments, webhooks) · Coolify/pm2 | — | +| [X3-integration-deploy.md](X3-integration-deploy.md) | Paperclip (wakeup, ניתוב comments, webhooks) · Coolify/pm2 | G2, G9 (תפעולי) | | [X4-agents.md](X4-agents.md) | מפת הסוכנים (דומיין + סוכני-התהליך) | G10 | | [X5-audit-provenance.md](X5-audit-provenance.md) | audit-trail לשימוש ב-AI · עקיבוּת כל מקור מצוטט · שלמות-רשומה | G5, G9 | From b0351958db0d868c658ec7e228196591a5864e65 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 16:59:31 +0000 Subject: [PATCH 23/30] docs(spec): X4-agents map + reserved process-agents section Co-Authored-By: Claude Opus 4.8 --- docs/spec/X4-agents.md | 140 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 docs/spec/X4-agents.md diff --git a/docs/spec/X4-agents.md b/docs/spec/X4-agents.md new file mode 100644 index 0000000..28a074e --- /dev/null +++ b/docs/spec/X4-agents.md @@ -0,0 +1,140 @@ +# X4 — מפת הסוכנים (Agents Map) + +קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) והוא ה-deep-dive על **מי הם הסוכנים** +של עוזר משפטי, **מה תפקיד כל אחד**, ו**אילו קבצי-ספ כל סוכן חייב לקרוא לפני שהוא פועל**. הוא +מסייע לסוכן לדעת באיזה ספ לקרוא — ומעגן את [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) +(המערכת מסייעת; השערים האנושיים הם invariant): כל סוכן קורא את החוקה תחילה ופועל בתחום-אחריותו, +לא מחליף את שיקול-הדעת האנושי. + +> **invariant פרויקטלי-תפעולי.** ה-invariants כאן הם **עובדות על איך הסוכנים של המערכת *הזו* +> מאורגנים ומופעלים** — לא תאוריה הנדסית כללית ולא תוכן משפטי. אין סמכות חיצונית ל"מי קורא מה +> לפני שהוא פועל"; לכן הם נושאים שדה `מקור-סמכות` = הראנבוקים וקבצי-הסוכן של הפרויקט עצמו +> ([HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md), קבצי הסוכן תחת [.claude/agents/](../../.claude/agents/), +> ו-[החוקה](00-constitution.md)) — **לא** ≥3 מקורות חיצוניים ו**ללא** סטטוס verified/UNVERIFIED. +> אבל כל invariant **נקשר לעיקרון הגלובלי שהוא משרת**: כלל "קרא-לפני-שתפעל" + תחום-אחריות הם +> מופע של [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) (סיוע תחת +> שערים אנושיים) ו-[G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים). + +--- + +## 1. ההפעלה המשותפת — HEARTBEAT.md + +לפני כל עבודה, **כל** סוכן Paperclip עובר את ה-checklist המשותף ב-[HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md): +זיהוי וסינון-חברה (§1), קריאת comments אחרונים (§1.5, 2b–2c), קריאת `heartbeat-context` עם +attachments (§1.5ב), וקריאות-API דרך `pc.sh` בלבד (§0). HEARTBEAT גובר על ה-skill הרשמי של +Paperclip בקונפליקט (project-specific מנצח default), אך אינו מחליף את החוקה — הוא מצטרף אליה: +קודם החוקה (00) + ספ-התחום, אז ה-HEARTBEAT התפעולי. + +**הקשר רב-חברתי.** ל-Paperclip אילוץ `agents.company_id NOT NULL` — אין סוכן משותף. לכן כל אחד +מ-7 תפקידי הסוכן-הדומייני מיוצג בשתי רשומות (CMP / CMPA), וסוכן מטפל **רק** בתיקי-החברה שלו לפי +`$PAPERCLIP_COMPANY_ID` (1xxx ל-CMP; 8xxx/9xxx ל-CMPA). ראה [X2-multi-company.md](X2-multi-company.md). + +--- + +## 2. מפת הסוכנים הדומייניים (7 תפקידים × 2 חברות) + +הסט המדויק (`ls .claude/agents/`): `HEARTBEAT.md`, `hermes-curator.md`, `legal-analyst.md`, +`legal-ceo.md`, `legal-exporter.md`, `legal-proofreader.md`, `legal-qa.md`, `legal-researcher.md`, +`legal-writer.md`. התפקיד נלקח מה-frontmatter של כל קובץ; עמודת "ספ לקרוא" מקשרת תפקיד לקבצי-הספ +שהוא אוכף/צורך. + +| סוכן (קובץ) | תפקיד (מה-frontmatter) | ספ-תחום לקרוא לפני פעולה | +|-------------|------------------------|---------------------------| +| [legal-ceo.md](../../.claude/agents/legal-ceo.md) | מנהל תהליך כתיבת החלטות, מתזמר סוכנים, מפקח על התקדמות | **00 + כל הספ** (מתזמר → צריך תמונה מלאה); ניתוב comments → [X3 §1ב](X3-integration-deploy.md) | +| [legal-proofreader.md](../../.claude/agents/legal-proofreader.md) | מגיה — תיקון שגיאות OCR בטקסט עברי לפני ניתוח | [01-ingest.md](01-ingest.md) (קליטה/טקסט-מחולץ) | +| [legal-researcher.md](../../.claude/agents/legal-researcher.md) | חוקר תקדימים — פסיקה, מיפוי תכניות, סיכום פרוטוקולים | [03-retrieval.md](03-retrieval.md) (3 קורפוסים, hybrid/RRF, attribution); קליטת-פסיקה → [01-ingest.md](01-ingest.md) | +| [legal-analyst.md](../../.claude/agents/legal-analyst.md) | מנתח משפטי — חילוץ טענות, ניתוח אסטרטגי, שאלות מחקר | [02-data-model.md](02-data-model.md) + [03-retrieval.md](03-retrieval.md) + [04-analysis-writing.md](04-analysis-writing.md) | +| [legal-writer.md](../../.claude/agents/legal-writer.md) | כותב — כתיבת בלוקי ההחלטה בסגנון דפנה תמיר | [04-analysis-writing.md](04-analysis-writing.md) + [05-qa-review.md](05-qa-review.md) (כותב מול שערי-QA) | +| [legal-qa.md](../../.claude/agents/legal-qa.md) | בודק איכות — שלמות, ניטרליות, כיסוי טענות, משקלות לפני ייצוא | [05-qa-review.md](05-qa-review.md) (שערי QA + שערים אנושיים) | +| [legal-exporter.md](../../.claude/agents/legal-exporter.md) | מייצא — בדיקה סופית, ייצוא DOCX, שמירה מגורסת | [06-export.md](06-export.md) (ייצוא DOCX לפי תבנית דפנה) | +| [hermes-curator.md](../../.claude/agents/hermes-curator.md) | Knowledge Curator (Hermes) — מנתח החלטות סופיות post-export, מציע עדכוני skills/lessons; read-only על תוכן, write רק על comments | [07-learning.md](07-learning.md) (Hermes · לקחים · לולאת פידבק) | + +**הערות על הסט:** + +- **CEO = נקודת-הניתוב היחידה.** תגובת-משתמש על issue מעירה את ה-CEO; הוא מחליט ניתוב ויוצר + issue לסוכן-המשנה — סוכן-משנה לא מקבל עבודה ישירות מ-comment ([X3 §1ב](X3-integration-deploy.md)). +- **Hermes — חיבור ישיר, לא דרך CEO.** מופעל מ"סמן כסופי" ב-UI (`mark-final` → `pc_wake_curator_for_final()`), + לא מ-CEO; ופועל על מודל `deepseek_local` (לא Claude Code) — ראה [X2 INV-MC1](X2-multi-company.md#inv-mc1-תצורת-סוכן-ב-master-מפושטת-ל-mirror--אין-drift-בין-החברות) + למלכודת ה-`adapter_type`-skip בסנכרון. הצעות ה-curator עוברות **אישור-יו"ר ידני** לפני commit + ל-`SKILL.md`/`lessons.md` — מופע של [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant). +- **company_id פר-סוכן.** כל שורה בטבלה מיוצגת פעמיים (CMP + CMPA); ה-CEO לכל חברה שונה + ([X2 §1](X2-multi-company.md)). הסוכן פועל רק בטווח-החברה שלו ([X2 §2](X2-multi-company.md)). + +--- + +## 3. סוכני-התהליך (תת-פרויקט 5) — סעיף שמור (RESERVED) + +> **סטטוס: מתוכנן, טרם נבנה.** הסעיף הזה הוא **מקום שמור מכוון** עבור סוכני-התהליך שיוגדרו +> ב**תת-פרויקט 5** — הם **אינם קיימים כיום** ואין לטעות בהם כמופעלים. הם מתועדים כאן כדי +> שהמפה תהיה שלמה ושכיוון-העבודה יהיה ברור, לא כ-TODO פתוח. + +בניגוד לסוכנים הדומייניים (סעיף 2) שמטפלים בתיקי-עררים, **סוכני-התהליך** הם סוכנים שיקראו את +ספ-המערכת (קבצי 00–07, X1–X5) ו"יעשו את שיעורי-הבית" — יפעלו על *המערכת עצמה*, לא על תיק. שלושה +תפקידים מתוכננים: + +| סוכן-תהליך (מתוכנן) | תפקיד מיועד | +|----------------------|-------------| +| **add-feature** | הוספת יכולת חדשה — קורא את הספ הרלוונטי, מאתר את ה-invariants שחלים, ומיישם בלי לשבור G1–G11 | +| **fix-feature** | תיקון תקלה — מאתר את ה-invariant שהופֵר (מול [audit-report.md](../audit-report.md)) ומתקן במקור, לא בתסמין | +| **spec-guardian** | שמירת עקביות הספ — מאתר drift בין הקוד לספ ובין קבצי-הספ עצמם; סתירה = ממצא ל-audit | + +ההגדרה המלאה (frontmatter, tools, instructions, מיפוי תפקיד→ספ, ושערי-האישור) **תיכתב בתת-פרויקט 5**. +עד אז — אין רשומות-סוכן, אין wakeup, ואין הסתמכות עליהם בזרימה. + +--- + +## 4. Invariants של התחום (פרויקטלי-תפעולי) + +### INV-AG1: כל סוכן קורא את החוקה תחילה, אז את ספ-התחום הרלוונטי — לפני פעולה +**כלל:** כל סוכן (דומייני או תהליך) **חייב** לקרוא את [00-constitution.md](00-constitution.md) +תחילה, ואז את ספ-התחום הרלוונטי לתפקידו (לפי הטבלה בסעיף 2), **לפני** שהוא פועל. ה-checklist +המשותף ב-HEARTBEAT מתבצע בכל ריצה; קריאת-הספ קודמת לעבודה המהותית. סוכן אינו פועל "מהזיכרון" — +המקור הקנוני להתנהגות הוא החוקה + ספ-התחום (מופע של [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) +— המערכת מסייעת תחת שערים אנושיים, והסוכן פועל בגבולות שהחוקה מגדירה). +**מקור-סמכות:** [HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md) (checklist הפעלה משותף) + +קבצי-הסוכן תחת [.claude/agents/](../../.claude/agents/) (frontmatter + instructions) + +[00-constitution.md §7](00-constitution.md#7-אינדקס-הספ) (אינדקס הספ — איזה קובץ אוכף איזה invariant). +(invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות; משרת את העיקרון הגלובלי G10.) +**אכיפה:** נוהל — ה-checklist ב-HEARTBEAT + הפניות-הספ בקבצי-הסוכן. **אין אכיפה אוטומטית** +שתכריח קריאת-ספ לפני פעולה (ראה §5 — זה היעד). +**הפרה ידועה:** — + +### INV-AG2: סוכן דומייני פועל רק בתחום-החברה שלו +**כלל:** סוכן דומייני מטפל **רק** בתיקי-החברה שלו לפי `$PAPERCLIP_COMPANY_ID` (CMP→1xxx; +CMPA→8xxx/9xxx). אסור ליצור פרויקט/issue/תוכן לתיק מחוץ לטווח; issue מחוץ-לטווח → סירוב מנומס +ב-comment + העֵרת ה-CEO של החברה הנכונה (מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) +— הפרדה נאכפת לפי `company_id`, אין מסלולים חוצי-חברה מתפצלים; ראה [X2 §2](X2-multi-company.md)). +**מקור-סמכות:** [HEARTBEAT.md §1](../../.claude/agents/HEARTBEAT.md) (סינון-חברה — כלל-ברזל) + +קבצי-הסוכן (סעיף "סינון תיקים לפי חברה") + [X2-multi-company.md §2](X2-multi-company.md). +(invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות; משרת את העיקרון הגלובלי G2.) +**אכיפה:** סינון-חברה ב-HEARTBEAT + גבול-חברה נאכף בצד-Paperclip (`Agent key cannot access +another company`, [X2 §2](X2-multi-company.md)). +**הפרה ידועה:** — + +--- + +## 5. מצב קיים מול יעד — חיווט הספ לסוכנים + +ספ-המערכת (קבצי 00–07, X1–X5) הוא **חדש** — קבצי-הסוכן וה-HEARTBEAT עדיין **אינם מפנים אליו** +במפורש; הם מפנים ל-CLAUDE.md, למסמכי-`docs/` הישנים, ול-skills. זהו פער אמיתי: + +- **קיים:** HEARTBEAT אוכף checklist הפעלה (סינון-חברה, comments, pc.sh) אך **לא** מחייב קריאת + `00-constitution.md` או ספ-התחום. +- **יעד:** לחווט את HEARTBEAT וקבצי-הסוכן כך שיחייבו במפורש את INV-AG1 — קריאת החוקה + ספ-התחום + הרלוונטי (לפי הטבלה בסעיף 2) לפני עבודה מהותית. זהו תנאי-מוקדם לסוכני-התהליך (סעיף 3), שכל + עבודתם היא "לקרוא את הספ ולעשות שיעורי-בית". + +--- + +## 6. הפניות-אחיות + +- [00-constitution.md](00-constitution.md) — [G10](00-constitution.md#inv-g10-המערכת-מסייעת--שערים-אנושיים-הם-invariant) + (שערים אנושיים) + [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) + (מקור-אמת/הפרדה) + [§7 אינדקס הספ](00-constitution.md#7-אינדקס-הספ). +- [X2-multi-company.md](X2-multi-company.md) — 14 סוכנים = 7 × 2, `company_id` פר-סוכן, כללי sync. +- [X3-integration-deploy.md](X3-integration-deploy.md) — wakeup, ניתוב comments דרך CEO, webhooks. +- ספ-התחום שכל סוכן צורך: [01-ingest.md](01-ingest.md), [02-data-model.md](02-data-model.md), + [03-retrieval.md](03-retrieval.md), [04-analysis-writing.md](04-analysis-writing.md), + [05-qa-review.md](05-qa-review.md), [06-export.md](06-export.md), [07-learning.md](07-learning.md). +- [.claude/agents/HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md) + קבצי-הסוכן תחת + [.claude/agents/](../../.claude/agents/) — frontmatter (תפקיד) + instructions (סינון-חברה, זרימה). From 8c4b8cf19ef6d47c2ba782a05a8cc197bbf1de68 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 17:05:43 +0000 Subject: [PATCH 24/30] 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) — קליטה שמייצרת את הקטעים שאליהם פותרים ציטוטים. From 380998da175e873d19e53a7061f30ec97fcda916 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 17:09:33 +0000 Subject: [PATCH 25/30] =?UTF-8?q?docs(spec):=20X5=20=E2=80=94=20file:line/?= =?UTF-8?q?name=20precision=20(log=5Fsearch=5Fbg,=20user=20param,=20active?= =?UTF-8?q?=5Fdraft=5Fpath)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec/X5-audit-provenance.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/spec/X5-audit-provenance.md b/docs/spec/X5-audit-provenance.md index 93113dd..b3fe9a6 100644 --- a/docs/spec/X5-audit-provenance.md +++ b/docs/spec/X5-audit-provenance.md @@ -49,13 +49,13 @@ Track Changes). הקובץ אוכף את 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 — +6. **telemetry של חיפושים.** `telemetry.log_search_bg` (ב-search.py) → מפעיל את `log_search` האסינכרוני → `search_logs` + (`telemetry.py:105,161`, `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)` +8. **טבלת `audit_log` (פעולה כללית).** `log_action(action, case_id, document_id, details, user)` (עמודת-DB: `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). @@ -141,7 +141,7 @@ retrieved_case_law_ids}`) — הטבלה כבר תומכת ב-`details JSONB` + תומך בקישור, אך אין בדיקה שכל ציטוט בטקסט אכן פתיר ל-`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`; + `active_draft_path` הופך מקור-בפועל בלי re-sync לבלוקים (`db.py:189`; [06-export INV-EX1](06-export.md#inv-ex1-ייצוא-דטרמיניסטי-ומשוחזר-מהבלוקים--docx-הוא-נתון-נגזר-g2)). **יעד:** re-sync מהבלוקים או חוזה מפורש + health-check לגילוי drift. - **telemetry בולעת שגיאות בשתיקה (תיעוד, לא הערכה).** `log_search` swallow מכוון From e3880aef4e794b9500195afc37b0477b36d251a9 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 17:15:00 +0000 Subject: [PATCH 26/30] =?UTF-8?q?docs(spec):=20sign-off=20fixes=20?= =?UTF-8?q?=E2=80=94=2006=20index=20row=20(G2,G9),=20refresh=20stale=20?= =?UTF-8?q?=C2=A77=20note,=20fix=20X3=20G9=20anchor=20niqqud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- docs/spec/00-constitution.md | 5 ++--- docs/spec/X3-integration-deploy.md | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/spec/00-constitution.md b/docs/spec/00-constitution.md index 73e6eb1..98d72d5 100644 --- a/docs/spec/00-constitution.md +++ b/docs/spec/00-constitution.md @@ -216,8 +216,7 @@ Manual* (2d ed.) | סטטוס: verified ## 7. אינדקס הספ -> הערה: נכון לעכשיו קיימת רק החוקה (קובץ זה). הקבצים 01–07 ו-X1–X5 ייכתבו בהמשך — -> הקישורים אליהם הם הפניות-קדימה, לא קישורים שבורים. +> הערה: כל קבצי הספ (00, 01–07, X1–X5) קיימים. החוקה היא שער-הכניסה; כל קובץ-תחום כפוף לה. | קובץ | תפקיד | אוכף invariants | |------|--------|-----------------| @@ -227,7 +226,7 @@ Manual* (2d ed.) | סטטוס: verified | [03-retrieval.md](03-retrieval.md) | 3 קורפוסים + כלי-חיפוש · hybrid/RRF · attribution · eval harness | G4, G5, G6, G7, G8, G9 | | [04-analysis-writing.md](04-analysis-writing.md) | חילוץ טענות · 12 בלוקים · סגנון דפנה (מצטט block-schema.md) | G11 | | [05-qa-review.md](05-qa-review.md) | שערי QA + שערים אנושיים (אישור הלכה, בחירת תוצאה, פידבק) כ-invariant | G10, G11 | -| [06-export.md](06-export.md) | ייצוא DOCX לפי תבנית דפנה | — | +| [06-export.md](06-export.md) | ייצוא DOCX לפי תבנית דפנה | G2, G9 | | [07-learning.md](07-learning.md) | Hermes · לקחים · לולאת פידבק היו"ר · צמיחת קורפוס (quality-at-source) | G4, G10 | | [X1-identifiers.md](X1-identifiers.md) | מודל מזהים קנוני: נרמול case_number בכתיבה · cases מול case_law · פורמטי ציטוט | G1 | | [X2-multi-company.md](X2-multi-company.md) | CMP/CMPA · 14 סוכנים · כללי sync | G2 | diff --git a/docs/spec/X3-integration-deploy.md b/docs/spec/X3-integration-deploy.md index 3fa35c6..299b253 100644 --- a/docs/spec/X3-integration-deploy.md +++ b/docs/spec/X3-integration-deploy.md @@ -174,7 +174,7 @@ drift אפשרי Infisical↔Coolify env (env לא מתעדכן אוטומטית `pc_request` (Python/FastAPI) — ולעולם לא `curl`/`httpx` גולמי. ה-helper מזריק `Authorization`, `X-Paperclip-Run-Id` (audit), ו-`Content-Type` באופן עקבי, ותומך ב-board API keys long-lived (מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) — מסלול-גישה -קנוני יחיד ל-Paperclip; ושל [G9](00-constitution.md#inv-g9-עקיבות-מקור--audit-trail-ל-ai) — +קנוני יחיד ל-Paperclip; ושל [G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai) — audit-trail עקבי). **מקור-סמכות:** "קריאות API — תמיד דרך helper" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) + [HEARTBEAT.md §0, שורות 15–32](../../.claude/agents/HEARTBEAT.md) + @@ -201,7 +201,7 @@ audit-trail עקבי). ## 5. הפניות-אחיות - [00-constitution.md](00-constitution.md) — [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) - (מסלול קנוני יחיד) + [G9](00-constitution.md#inv-g9-עקיבות-מקור--audit-trail-ל-ai) (audit-trail) + + (מסלול קנוני יחיד) + [G9](00-constitution.md#inv-g9-עקיבוּת-מקור--audit-trail-ל-ai) (audit-trail) + כלל-ההנדסה "סימטריה" ([§6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)). - [X2-multi-company.md](X2-multi-company.md) — wakeup-per-company + ניתוב לפי `company_id` משלים את §1 כאן. - [X4-agents.md](X4-agents.md) — מפת הסוכנים שה-CEO מנתב אליהם comments. From ebfda74575ad35c795030d8aca68241d4987e716 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 17:23:14 +0000 Subject: [PATCH 27/30] =?UTF-8?q?docs(spec):=20X1=20=E2=80=94=20canonical?= =?UTF-8?q?=20case=5Fnumber=20=3D=20official=20assigned=20number=20(no=20m?= =?UTF-8?q?onth=20invention);=20mixed-form=20reconciliation=20is=20a=20mig?= =?UTF-8?q?ration=20task?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec/X1-identifiers.md | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/docs/spec/X1-identifiers.md b/docs/spec/X1-identifiers.md index bfeba87..8970fb4 100644 --- a/docs/spec/X1-identifiers.md +++ b/docs/spec/X1-identifiers.md @@ -25,7 +25,12 @@ | trim | הסרת רווחים מקיפים | `" 8137/24 "` → `"8137/24"` | | prefix-strip | הסרת קידומת-הליך לפני הספרה הראשונה ("ערר", "בל\"מ", "עע\"מ") | `"ערר 8137/24"` → `"8137/24"` | | separator | איחוד מפריד `/` → `-` | `"8137/24"` → `"8137-24"` | -| padding | צורת-מספר אחידה (סדרתי-שנה, ומחוז כשקיים) | `"8126-25"` ↔ `"8126-03-25"` | + +> **הצורה הקנונית = המספר הרשמי שהוקצה ע"י הוועדה, נשמר ככתבו** — לרבות מקטע-החודש **כשהוקצה** +> (למשל `8126-03-25`). מספרי-מורשת מסוימים הוקצו **ללא** חודש (למשל `8126-25`); המערכת **אסור** +> שתמציא או תוסיף (pad) מקטע-חודש שמעולם לא הוקצה. הנרמול-בכתיבה הוא **פורמט-בלבד ודטרמיניסטי** +> (trim · `/`→`-` · prefix-strip) — הוא **אינו מוסיף ואינו מסיר** מקטע-חודש. הפורמט המועדף +> מכאן-ואילך כולל את החודש. > סוג-ההליך (`proceeding_type ∈ {ערר, בל"מ}`) הוא **חלק מהמפתח הקנוני** — לא חלק ממחרוזת > ה-`case_number`. הקידומת "ערר"/"בל\"מ" מהכותרת נשללת מהמספר ונשמרת בעמודה ייעודית @@ -90,8 +95,10 @@ partial unique indexes (`db.py:904-909`): ## 4. Invariants של התחום ### INV-ID1: `case_number` מנורמל בכתיבה — התאמה-סלחנית משנית -**כלל:** `case_number` מנורמל לצורה קנונית יחידה **בנקודת-הכתיבה** (trim · prefix-strip · -`/`→`-` · padding), והשוואה-בקריאה היא שוויון מול הצורה הקנונית. **התאמה-סלחנית-בקריאה היא +**כלל:** `case_number` מנורמל לצורה קנונית יחידה **בנקודת-הכתיבה** בנרמול **פורמט-בלבד +ודטרמיניסטי** (trim · prefix-strip · `/`→`-`) — הנרמול **אינו ממציא ואינו מוסיף** מקטע-חודש +שלא הוקצה. הצורה הקנונית היא **המספר הרשמי שהוקצה** (עם חודש כשהוקצה, למשל `8126-03-25`), +והשוואה-בקריאה היא שוויון מול הצורה הקנונית. **התאמה-סלחנית-בקריאה היא נוחות משנית בלבד** — היא בולעת קלט-משתמש רב-צורני, ואינה תחליף לנרמול-בכתיבה ([G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה), כלל-ההנדסה "נרמול לא תיקון-תסמין", חוקה §6). **מקורות:** SSOT (Single Source of Truth — normalization principle) · E.F. Codd, First Normal @@ -102,8 +109,12 @@ Form (CACM 13(6), 1970) · Martin Kleppmann, *Designing Data-Intensive Applicati **הפרה ידועה:** `_normalize_case_number` (`db.py:1196-1211`) מנרמל **בקריאה בלבד** ("tolerant lookup", `db.py:1197`), ו-`get_case_by_number` (`db.py:1214-1231`) משווה two-pass (`case_number=$1` **OR** `replace(btrim(case_number),'/','-')=$2`, `db.py:1223-1224`) — אין מסלול-כתיבה שמקנן את -הערך המאוחסן. תוצאה: `8126-25` לא נמצא מול האמיתי `8126-03-25` (padding לא מכוסה בנרמול-הקריאה) -→ ממצא ל-[audit](../audit-report.md). +הערך המאוחסן. בנפרד מכך: כשאותו תיק נקלט גם בצורה ללא-חודש וגם עם-חודש (סחף-הזנה, למשל `8126-25` +מול `8126-03-25` המתייחסים לתיק אחד), הצורה **עם-החודש (הרשמית) היא הקנונית** והרשומה החסרה +מתואמת אליה — זו **בעיית-תיאום (reconciliation)**, לא חולשה בנרמול (הנרמול אינו אמור לפדד חודש). +תיאום רשומות-מורשת מעורבות-צורה הוא **פריט ניקיון-נתונים/מיגרציה חד-פעמי** (ראה +[gap-audit / תת-פרויקט 2](../audit-report.md)), לא אלגוריתם-padding בזמן-ריצה → ממצא +ל-[audit](../audit-report.md). ### INV-ID2: אין ציטוט-מלא כמזהה — הציטוט שדה-תצוגה נגזר **כלל:** אף ישות **אינה** משתמשת במחרוזת-ציטוט-מלאה כמזהה. שדה-המזהה מכיל מספר-תיק מנורמל @@ -129,11 +140,12 @@ Form (CACM 13(6), 1970) · Martin Kleppmann, *Designing Data-Intensive Applicati **אינו מנרמל את הערך המאוחסן**. `get_case_by_number` (`db.py:1214-1231`) בונה סביבו two-pass (exact `OR` normalized, `db.py:1223-1224`). **תסמין:** הנרמול חי כתיקון-תסמין בקריאה ולא כקנוניזציה-בכתיבה, בניגוד ל-[G1](00-constitution.md#inv-g1-מזהה-קנוני-מנורמל-בכתיבה) וכלל-ההנדסה - §6. **יעד:** מסלול-כתיבה שמנרמל את `case_number` (כולל padding) בנקודת-הקליטה; הקריאה הופכת - להשוואת-שוויון פשוטה. -- **padding לא מכוסה אפילו בנרמול-הקריאה.** הנרמול הקיים מטפל ב-prefix/separator/trim בלבד, - לא ב-padding מחוז/שנה — ולכן `8126-25` ↔ `8126-03-25` נכשל גם ב-two-pass. **יעד:** padding - כחלק מהצורה הקנונית (§1), מנורמל בכתיבה לכל הרשומות. + §6. **יעד:** מסלול-כתיבה שמנרמל את `case_number` (פורמט-בלבד: trim/prefix-strip/`/`→`-`, + **ללא המצאת חודש**) בנקודת-הקליטה; הקריאה הופכת להשוואת-שוויון פשוטה. +- **רשומות-מורשת מעורבות-צורה (בעיית-תיאום, לא padding).** כשאותו תיק נקלט גם כ-`8126-25` + וגם כ-`8126-03-25` (סחף-הזנה), ה-two-pass אינו מזהה אותם כתיק אחד. **יעד:** תיאום חד-פעמי + של הרשומות לצורה הרשמית עם-החודש (הקנונית) במסגרת ניקיון-נתונים/מיגרציה + ([gap-audit / תת-פרויקט 2](../audit-report.md)) — **לא** אלגוריתם-padding בזמן-ריצה שממציא חודש. - **ציטוט-מלא כ-`case_number` (מורשת).** השדה המקורי `case_number TEXT UNIQUE NOT NULL` (`cases` `db.py:76`, `case_law` `db.py:368`) לא אכף צורה — מה שאפשר אחסון מחרוזת-ציטוט בשדה זה (החלטות "סופר"). הוחלף ב-partial unique מודע-`source_kind` ב-V15 (`db.py:902-909`), אך From 28bed4906c46d915d4546a7d6bb90df7e1903181 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 17:27:06 +0000 Subject: [PATCH 28/30] =?UTF-8?q?docs(spec):=20gap-audit=20=E2=80=94=2023?= =?UTF-8?q?=20findings=20mapped=20to=20invariants=20+=20proposed=20fix-uni?= =?UTF-8?q?ts=20(sub-project=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec/gap-audit.md | 125 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 docs/spec/gap-audit.md diff --git a/docs/spec/gap-audit.md b/docs/spec/gap-audit.md new file mode 100644 index 0000000..6537de4 --- /dev/null +++ b/docs/spec/gap-audit.md @@ -0,0 +1,125 @@ +# 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 + 01–07 + X1–X5) מול הקוד הקיים, +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 אחרי ייצוב-הספ. From d72d5429ed2bae239d91b8f458d7161f0a9fe391 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 17:38:31 +0000 Subject: [PATCH 29/30] tasks(legal-ai): 8 fix-unit tasks (59-66) + 23 GAP subtasks from gap-audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Granularity (epic-per-fix-unit + subtask-per-gap) and dependency-aware/WSJF prioritization both backed by ≥3 authoritative sources (SAFe/Pichler/OWASP/CVSS; Wake-INVEST/Cohn/Agile-Alliance/Atlassian/SAFe). Co-Authored-By: Claude Opus 4.8 --- .taskmaster/tasks/tasks.json | 342 +++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) diff --git a/.taskmaster/tasks/tasks.json b/.taskmaster/tasks/tasks.json index 20dca90..e60a0b0 100644 --- a/.taskmaster/tasks/tasks.json +++ b/.taskmaster/tasks/tasks.json @@ -2000,6 +2000,348 @@ "testStrategy": "document_list על 7 וריאציות פורמט של תיק קיים → כולן מחזירות את אותם מסמכים; תיק לא-קיים אמיתי עדיין מחזיר 'לא נמצא'.", "subtasks": [], "updatedAt": "2026-05-30T11:54:34.291Z" + }, + { + "id": "59", + "title": "[FU-1] איחוד מסלול ה-ingest למסלול קנוני אחד", + "description": "מאחד את ingest_precedent ו-ingest_internal_decision למסלול קנוני יחיד; מבטל את האסימטריות.", + "details": "מכסה GAP-01,02,04,05. מספק INV-ING1/ING3/G2/G4. severity: Critical. סוג: קוד. יסוד — FU-2/FU-3/FU-7 תלויים בו. מקור: docs/spec/gap-audit.md + 01-ingest.md.", + "testStrategy": "", + "status": "pending", + "dependencies": [], + "priority": "high", + "subtasks": [ + { + "id": 1, + "title": "[GAP-01] מסלול ingest קנוני יחיד", + "description": "ביטול שני המסלולים המקבילים (precedent_library.py:88 vs internal_decisions.py:73); כל סוג = פרמטרים, לא פונקציה נפרדת.", + "dependencies": [], + "details": "INV-ING1/G2", + "status": "pending", + "testStrategy": "", + "parentId": "59" + }, + { + "id": 2, + "title": "[GAP-02] תזמון חילוץ metadata לכל סוג", + "description": "קריאה ל-request_metadata_extraction גם במסלול הפנימי (היום רק halacha, internal_decisions.py:208).", + "dependencies": [], + "details": "INV-ING3/DM1", + "status": "pending", + "testStrategy": "", + "parentId": "59" + }, + { + "id": 3, + "title": "[GAP-04] ולידציית enums אחידה", + "description": "הוספת ולידציית practice_area/source_type למסלול הפנימי (כמו precedent_library.py:131-134).", + "dependencies": [], + "details": "INV-G4", + "status": "pending", + "testStrategy": "", + "parentId": "59" + }, + { + "id": 4, + "title": "[GAP-05] איחוד staging/derivation/citation-guard/multimodal/fallback", + "description": "שאר 6 האסימטריות → פרמטרים של המסלול הקנוני (01-ingest §4).", + "dependencies": [], + "details": "INV-ING1", + "status": "pending", + "testStrategy": "", + "parentId": "59" + } + ], + "updatedAt": "2026-05-30T17:37:34.741136+00:00" + }, + { + "id": "60", + "title": "[FU-2] ingest idempotent + מזהים קנוניים", + "description": "מפתח-upsert דטרמיניסטי + נרמול case_number בכתיבה + תיאום מספרים מעורבים.", + "details": "מכסה GAP-03,06,07,08,13. מספק INV-ING2/G3/G1/ID1/ID2/DM2/DM1. severity: Critical. סוג: קוד + מיגרציית-נתונים (דורש אישור-עלות). תלוי ב-FU-1.", + "testStrategy": "", + "status": "pending", + "dependencies": [ + "59" + ], + "priority": "high", + "subtasks": [ + { + "id": 1, + "title": "[GAP-03] קליטה idempotent (upsert על מפתח קנוני)", + "description": "קליטה חוזרת = עדכון, לא כפילות.", + "dependencies": [], + "details": "INV-ING2/G3", + "status": "pending", + "testStrategy": "", + "parentId": "60" + }, + { + "id": 2, + "title": "[GAP-06] נרמול case_number בכתיבה", + "description": "היום רק תיקון-קריאה (_normalize_case_number, db.py:1196-1211).", + "dependencies": [], + "details": "INV-G1/ID1", + "status": "pending", + "testStrategy": "", + "parentId": "60" + }, + { + "id": 3, + "title": "[GAP-07] תיאום מספרי-תיק מעורבים (with-month canonical)", + "description": "מיגרציה חד-פעמית; הצורה עם-חודש קנונית (החלטת-יו\"ר).", + "dependencies": [], + "details": "INV-ID1", + "status": "pending", + "testStrategy": "", + "parentId": "60" + }, + { + "id": 4, + "title": "[GAP-08] הסרת ציטוט-מלא כ-case_number", + "description": "רשומות עם ציטוט מלא כמזהה (legacy).", + "dependencies": [], + "details": "INV-DM2/ID2", + "status": "pending", + "testStrategy": "", + "parentId": "60" + }, + { + "id": 5, + "title": "[GAP-13] שדה searchable מפורש", + "description": "דגל 'עבר חוזה-שלמות' מובחן מ-extraction_status.", + "dependencies": [], + "details": "INV-DM1", + "status": "pending", + "testStrategy": "", + "parentId": "60" + } + ], + "updatedAt": "2026-05-30T17:37:34.741136+00:00" + }, + { + "id": "61", + "title": "[FU-3] re-index בשינוי תוכן", + "description": "embedding מתעדכן אוטומטית בשינוי תוכן (כיום trigger-dependent, לא GENERATED).", + "details": "מכסה GAP-09. מספק INV-DM3/G6. severity: High. סוג: קוד + מיגרציה (re-embed). תלוי ב-FU-1.", + "testStrategy": "", + "status": "pending", + "dependencies": [ + "59" + ], + "priority": "medium", + "subtasks": [ + { + "id": 1, + "title": "[GAP-09] re-index/re-embed בשינוי תוכן", + "description": "embedding לא GENERATED בניגוד ל-tsvectors; נקודת-drift.", + "dependencies": [], + "details": "INV-DM3/G6", + "status": "pending", + "testStrategy": "", + "parentId": "61" + } + ], + "updatedAt": "2026-05-30T17:37:34.741136+00:00" + }, + { + "id": "62", + "title": "[FU-4] בידוד-קורפוס בכל מסלול query", + "description": "אכיפת source_kind בכל פילטר (כולל halacha_filters); חסימת חיפוש ללא תחום.", + "details": "מכסה GAP-10,12. מספק INV-RET1/G5. severity: Critical. סוג: קוד. ללא תלות — דחוף (דליפה פעילה).", + "testStrategy": "", + "status": "pending", + "dependencies": [], + "priority": "high", + "subtasks": [ + { + "id": 1, + "title": "[GAP-10] סינון source_kind ב-halacha_filters (#56)", + "description": "db.py:3168/3401 — halacha_filters בלי source_kind בעוד chunk_filters עם.", + "dependencies": [], + "details": "INV-RET1/G5", + "status": "pending", + "testStrategy": "", + "parentId": "62" + }, + { + "id": 2, + "title": "[GAP-12] חסימת חיפוש ללא practice_area", + "description": "search_decisions מזהיר אך לא חוסם (search.py:45-49).", + "dependencies": [], + "details": "INV-RET/G5", + "status": "pending", + "testStrategy": "", + "parentId": "62" + } + ], + "updatedAt": "2026-05-30T17:37:34.741136+00:00" + }, + { + "id": "63", + "title": "[FU-5] eval-harness + נראות backlog", + "description": "מדידת precision/recall על gold-set + חשיפת backlog הלכות בבדיקת-בריאות.", + "details": "מכסה GAP-11,14. מספק INV-RET4/G8/QA1/G10. severity: High. סוג: קוד + החלטת-יו\"ר (בניית gold-set). תלוי ב-FU-2.", + "testStrategy": "", + "status": "pending", + "dependencies": [ + "60" + ], + "priority": "medium", + "subtasks": [ + { + "id": 1, + "title": "[GAP-11] eval-harness precision+recall + gold-set", + "description": "כיום רק telemetry.log_search_bg; איכות-אחזור לא נמדדת.", + "dependencies": [], + "details": "INV-RET4/G8", + "status": "pending", + "testStrategy": "", + "parentId": "63" + }, + { + "id": 2, + "title": "[GAP-14] נראות backlog הלכות", + "description": "ספירת pending_review בבדיקת-בריאות (10/19 התגלה במקרה).", + "dependencies": [], + "details": "INV-QA1/G10", + "status": "pending", + "testStrategy": "", + "parentId": "63" + } + ], + "updatedAt": "2026-05-30T17:37:34.741136+00:00" + }, + { + "id": "64", + "title": "[FU-6] שערי-QA נאכפים בקוד", + "description": "export חוסם בקוד על כשל-QA קריטי; תיקון neutral_background critical-but-passes.", + "details": "מכסה GAP-15,16. מספק INV-QA3/EX3/G10. severity: Critical. סוג: קוד. ללא תלות — מהיר.", + "testStrategy": "", + "status": "pending", + "dependencies": [], + "priority": "high", + "subtasks": [ + { + "id": 1, + "title": "[GAP-15] export חוסם QA קריטי בקוד", + "description": "export_docx (drafting.py:384) לא קורא ל-validate_decision — ניתן לעקיפה.", + "dependencies": [], + "details": "INV-QA3/EX3", + "status": "pending", + "testStrategy": "", + "parentId": "64" + }, + { + "id": 2, + "title": "[GAP-16] תיקון neutral_background critical-but-passes", + "description": "fallback בלוק-ו ריק מסומן critical אך passed=True (qa_validator.py:70).", + "dependencies": [], + "details": "QA-gate", + "status": "pending", + "testStrategy": "", + "parentId": "64" + } + ], + "updatedAt": "2026-05-30T17:37:34.741136+00:00" + }, + { + "id": "65", + "title": "[FU-7] audit-trail + provenance", + "description": "כתיבת audit_log בכל פעולה; קישור בלוק→קטעי-מקור; סנכרון DB אחרי עריכה; אימות citation→corpus.", + "details": "מכסה GAP-17,18,19,20. מספק INV-AUD1/2/3/EX1/G9. severity: High. סוג: קוד + backfill קל. תלוי ב-FU-1. (זרע לתת-פרויקט 3/audit-provenance.)", + "testStrategy": "", + "status": "pending", + "dependencies": [ + "59" + ], + "priority": "medium", + "subtasks": [ + { + "id": 1, + "title": "[GAP-17] סנכרון בלוקי-DB אחרי עריכת draft", + "description": "active_draft_path הופך ל'מקור-אמת', בלוקים לא מסונכרנים (db.py:189).", + "dependencies": [], + "details": "INV-EX1/AUD2", + "status": "pending", + "testStrategy": "", + "parentId": "65" + }, + { + "id": 2, + "title": "[GAP-18] כתיבת audit_log בכל פעולה מהותית", + "description": "הטבלה קיימת אך נכתבת כמעט רק ב-case_subtype_override (cases.py:203).", + "dependencies": [], + "details": "INV-AUD1", + "status": "pending", + "testStrategy": "", + "parentId": "65" + }, + { + "id": 3, + "title": "[GAP-19] קישור בלוק→קטעי-מקור", + "description": "decision_blocks שומר model_used אך לא אילו chunks/precedents הזינו.", + "dependencies": [], + "details": "INV-AUD1", + "status": "pending", + "testStrategy": "", + "parentId": "65" + }, + { + "id": 4, + "title": "[GAP-20] אימות citation→corpus", + "description": "decision_paragraphs.citations ללא ולידציה שכל ציטוט מתאים.", + "dependencies": [], + "details": "INV-AUD3", + "status": "pending", + "testStrategy": "", + "parentId": "65" + } + ], + "updatedAt": "2026-05-30T17:37:34.741136+00:00" + }, + { + "id": "66", + "title": "[FU-8] מחסומי-תהליך → מחסומי-קוד", + "description": "אכיפת sync חוצה-חברות; מחסומי-קוד ל-wakeup/API; חיווט הספ לסוכנים.", + "details": "מכסה GAP-21,22,23. מספק INV-MC1/INT1/INT3/AG1. severity: High. סוג: קוד + החלטת-יו\"ר. GAP-23 prereq לתת-פרויקט 5.", + "testStrategy": "", + "status": "pending", + "dependencies": [], + "priority": "medium", + "subtasks": [ + { + "id": 1, + "title": "[GAP-21] אכיפת sync חוצה-חברות", + "description": "sync ידני ולא-נאכף; adapter_type-mismatch מדולג בשקט (sync...py:387-389).", + "dependencies": [], + "details": "INV-MC1", + "status": "pending", + "testStrategy": "", + "parentId": "66" + }, + { + "id": 2, + "title": "[GAP-22] מחסומי-קוד ל-wakeup/API", + "description": "אין אילוץ-schema נגד INSERT ישיר ל-agent_wakeup_requests; אין linter נגד httpx/curl גולמי.", + "dependencies": [], + "details": "INV-INT1/INT3", + "status": "pending", + "testStrategy": "", + "parentId": "66" + }, + { + "id": 3, + "title": "[GAP-23] חיווט הספ לסוכנים", + "description": "HEARTBEAT/agent docs דורשים קריאת 00-constitution + ספ-תחום (prereq לתת-פרויקט 5).", + "dependencies": [], + "details": "INV-AG1", + "status": "pending", + "testStrategy": "", + "parentId": "66" + } + ], + "updatedAt": "2026-05-30T17:37:34.741136+00:00" } ], "metadata": { From 80d1c5ff27210de00782334e6fc93f6ad7c065ae Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 30 May 2026 17:43:12 +0000 Subject: [PATCH 30/30] =?UTF-8?q?tasks(legal-ai):=20reconcile=20#56=20(can?= =?UTF-8?q?cel=E2=86=92superseded=20by=2062.1)=20+=20#57=20(link=20to=20FU?= =?UTF-8?q?-3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- .taskmaster/tasks/tasks.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.taskmaster/tasks/tasks.json b/.taskmaster/tasks/tasks.json index e60a0b0..1c94cd0 100644 --- a/.taskmaster/tasks/tasks.json +++ b/.taskmaster/tasks/tasks.json @@ -1967,10 +1967,10 @@ "id": "56", "title": "[Retrieval finding] halacha_filters לא מסננים source_kind — דליפה חוצת-קורפוסים", "description": "התגלה תוך כדי משימה 53. ב-search_precedent_library_semantic וב-search_precedent_library_lexical (db.py): chunk_filters כוללים cl.source_kind=$sk אבל halacha_filters כוללים רק review_status. תוצאה: search_precedent_library(external) מחזיר גם הלכות internal_committee, ו-search_internal_decisions(internal) מחזיר גם הלכות external. אי-עקביות: chunks מסוננים, halachot לא. כרגע זה דווקא מסייע למציאוּת (לכן לא רגרסיה), אבל לא עקבי. דורש החלטת מדיניות: או לסנן halachot גם לפי source_kind (עקבי, אך 'מסתיר' שכבות), או להשאיר מאוחד במכוון + לתעד. אם משאירים מאוחד — לעדכן docstrings של שני הכלים שזה לא 'corpus נפרד'.", - "status": "pending", + "status": "cancelled", "priority": "low", "dependencies": [], - "details": "db.py: search_precedent_library_semantic (~שורה הקודמת ל-3311), search_precedent_library_lexical (3346). שתי הפונקציות: halacha_filters=['h.review_status IN ...'] — חסר cl.source_kind. נמצא בעת בדיקת רגרסיה למשימה 53.", + "details": "db.py: search_precedent_library_semantic (~שורה הקודמת ל-3311), search_precedent_library_lexical (3346). שתי הפונקציות: halacha_filters=['h.review_status IN ...'] — חסר cl.source_kind. נמצא בעת בדיקת רגרסיה למשימה 53.\n\n[SUPERSEDED 2026-05-30] נבלע ב-FU-4 / תת-משימה 62.1 (GAP-10). נסגר כדי למנוע כפילות-tracker.", "testStrategy": "לאחר החלטה: אם מסננים — search_precedent_library('...substantive...', external) לא מחזיר case_law_id internal; אם משאירים — docstring מעודכן + טסט מאשר התנהגות מכוונת.", "subtasks": [], "updatedAt": "2026-05-30T11:09:30.257989+00:00" @@ -1984,7 +1984,7 @@ "dependencies": [ "55" ], - "details": "מקור: case_law.full_text קיים. נתיב: chunker.chunk_document(_hierarchical) → embeddings → החלפת precedent_chunks לתיק. למחוק chunks ישנים של התיק לפני הוספה. אחרי הרצה — ניתן להסיר את פילטר ה->=50 query (אופציונלי). תיקים מושפעים: SELECT DISTINCT case_law_id WHERE length(trim(content))<50.", + "details": "מקור: case_law.full_text קיים. נתיב: chunker.chunk_document(_hierarchical) → embeddings → החלפת precedent_chunks לתיק. למחוק chunks ישנים של התיק לפני הוספה. אחרי הרצה — ניתן להסיר את פילטר ה->=50 query (אופציונלי). תיקים מושפעים: SELECT DISTINCT case_law_id WHERE length(trim(content))<50.\n\n[קישור 2026-05-30] קשור ל-FU-3 (task 61, GAP-09 re-index). #57 = מיגרציה חד-פעמית של העבר (re-chunk legacy); FU-3 = ה-invariant הקדמי. נשמרים בנפרד במכוון.", "testStrategy": "אחרי re-chunk לתיק לדוגמה: 0 chunks<50 לאותו case_law_id; search_internal_decisions עדיין מחזיר את התיק; ספירת chunks סבירה.", "subtasks": [], "updatedAt": "2026-05-30T11:19:06.142606+00:00"