PR #203 הוסיף `COPY scripts/SCRIPTS.md` ל-Dockerfile אך .dockerignore
מחריג את כל scripts/ — ה-build נכשל ב-"/scripts/SCRIPTS.md: not found".
חריגה ממוקדת בדיוק כמו docs/ ו-skills/docx/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
מגיש את scripts/SCRIPTS.md כדף ב-/scripts: שם · סוג · תפקיד · תזמון
לכל סקריפט בתיקיית scripts/. מקור-האמת היחיד נשאר SCRIPTS.md (G2 — אין
מסלול-תוכן מקביל); עריכה דרך git, לא מה-UI.
- web/app.py: GET /api/scripts/catalog קורא את הקובץ בזמן-ריצה (מחקה את
דפוס get_curator_prompt; HTTPException על כשל — אין בליעה שקטה §6)
- Dockerfile: COPY scripts/SCRIPTS.md (לא הועתק לקונטיינר עד כה)
- web-ui: דף /scripts (AppShell + רכיב Markdown הקיים) + מודול api + קישור ניווט
- SCRIPTS.md: תיעוד ingest_bulletins.py — היה הקובץ היחיד מ-73 שלא תועד
Invariants: G2 (מקור-אמת יחיד), G12 (אין מגע-Paperclip), X6 (UI↔API).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
באג-אינטראקציה שהתגלה לפני ה-flip ל-dual: DualBackend.exists() מחזיר True אם הקובץ על
**הדיסק או** ב-S3. serve_blob בדק backend.exists() ואז הנפיק presigned — כך שתחת dual,
קובץ שקיים-רק-בדיסק (mirror שנכשל / מחוץ לסט-ההגירה) היה מקבל redirect ל-presigned-URL
שמחזיר 404 מ-MinIO, במקום fallback-לדיסק.
תיקון: serve_blob בודק קיום ב-**S3 ספציפית** — `s3 = getattr(backend, "s3", backend)`
(DualBackend.s3, או ה-S3Backend עצמו תחת s3) — כך שקובץ disk-only נופל ל-FileResponse
אמיתי. תואם-לאחור ל-filesystem/s3 (getattr מחזיר את ה-backend עצמו).
invariants: INV-STG6 (presigned רק כשהאובייקט באמת ב-S3) · INV-G10 (אפס שינוי תחת filesystem).
tests: 6 (2 חדשות — dual מ-S3-sub-backend present→redirect / absent→disk-fallback). py_compile OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
מצב --untracked לסקריפט ההגירה: סורק את ה-filesystem לקטגוריות שה-4 endpoints מגישים
אך אינן רשומות בשום עמודת-DB (research/*, proofread/*, drafts/*, exports/*, training/
proofread/*) → מעלה ל-legal-documents עם אותו key יחסי-DATA_DIR. זהו תנאי-הסף שהפאנל
התלת-מודלי זיהה: בלי הקבצים האלה ב-MinIO, cutover ל-s3-only היה מחזיר 404 על הגשתם.
dry-run אומת: 144 קבצים / 83.9MB, 0 חסרים, 0 outside. הפיך (העתקה אדיטיבית, דיסק שלם).
refactor קטן: הלולאה הראשית עובדת על work-list אחיד (DB-tracked או filesystem-scan).
invariants: G2 (אותו key/bucket scheme) · INV-STG1/3 · INV-G10 (dry-run/הפיך, אפס שינוי
בייצור — רק העלאה לדליות; cutover עדיין נעול-אדם).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
אודיט #122 חשף ש-process_final_version מחשב diff+analysis אך משליך אותם כשאין
draft_final_pair במצב final_received — קרה ל-5 תיקים סופיים היסטוריים שקדמו למנגנון
ה-snapshot ב-mark-final (pair ראשון 2026-06-06), ולכל קריאת ingest_final_version ישירה.
התוצאה: הפרת INV-LRN4 בפועל (סופי שלא הושווה/נשמר).
התיקון: create-or-update — כשאין pair, פותחים אחד מ-decision_blocks החיים (status→analyzed)
כך שהדיסטילציה נשמרת כ-הצעה ברשם. לתיקים חדשים אין שינוי-התנהגות (תמיד יש pair
מ-mark-final → רק ה-update רץ). זה keystone שמאפשר backfill (#125.2) דרך הפייפליין הקיים.
caveat מתועד בלוג: לתיק היסטורי ה-draft = blocks נוכחיים (אולי נערכו אחרי-חתימה),
לא snapshot-אמיתי.
Invariants:
- INV-LRN4 (מקיים) — כל סופי מקבל pair ומנותח; אין סופי "פתוח".
- INV-LRN1/G10 (נשמר) — הדיסטילציה נשמרת כ-הצעה (analyzed) בלבד; שער ה-promote הידני
לקיפול ל-appeal_type_rules לא נעקף.
- G2 (מקיים) — אותו פנקס draft_final_pairs, לא מסלול מקביל.
- G1 (מקיים) — נרמול במקור (הרשם) במקום תיקון-בקריאה.
ref: data/audit/learning-loop-activity-20260611.md · TaskMaster legal-ai #122/#125.1
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
חילוץ החלטת-ה-dedup ל-helper טהור ובדיק `halacha_quality.dedup_action()` (skip/flag/keep),
ושני שיפורים על מסלול ה-dedup-on-insert:
#82.4 — merge-with-provenance, לא blind-drop: כשמדלגים על כפילות-סמנטית (cosine≥0.93),
מאחדים את ה-`cites` של השורה הנכנסת אל השכן הקנוני ששורד (במקום לאבד אותם). זהו שדה-ה-
provenance היחיד שקיים בהכנסה; בחירת-קנוני + מיזוג-corroboration מלא שייכים למסלול ה-
reconimation הלא-מקוון (#82.7 / #84.2, שם לשורות כבר יש provenance מצטבר) — מתועד בקוד.
#82.6 — over-merge guard: ההחלטה PAIRWISE מול שכן יחיד הקרוב ביותר, ורק השורה הנכנסת
מודלגת אי-פעם (אף שורה קיימת לא ממוזגת/נמחקת). אין connected-components closure בהכנסה,
לכן שרשרת A~B~C לא קורסת לשורה אחת גם כש-A,C מובחנים. מתועד ב-dedup_action + נבדק.
invariants: G1 (provenance נשמר במקור, לא אובד) · G2 (לוגיקת-החלטה ב-helper יחיד בדיק,
refactor משמר-התנהגות) · INV-G10 (אין auto-merge של שורות קיימות; tail→flag→סקירת-יו"ר).
tests: 6 חדשות (skip/flag/keep/over-merge/boundaries) + 59 בדיקות-הלכה קיימות עוברות.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
המשך אבחנת ה-heartbeat-timeout (#120): שלב 4 בזרימת חילוץ-הספרייה של ה-CEO הורה
להריץ `corroboration_rebuild()` עם ארגומנט ריק = `build_all()` שעובר על כל הקורפוס
עם קריאת-LLM (Opus xhigh) לכל ציטוט-נכנס = שעות → חורג מ-heartbeat של שעה
(timeout/process_lost). זה רץ בכל ריצת חילוץ-ספרייה, מיד אחרי החילוץ — כך שגם אחרי
שהחילוץ הפך לתיק-בודד (#120), שלב הקורובורציה היה ממשיך ל-timeout.
תיקון: הפרומפט מורה כעת `corroboration_rebuild(case_law_id="<issue>")` — מסוקף לתקדים
הבודד (bounded, דקות), עם אזהרה מפורשת לא להריץ ריק. ה-backfill המלא של כל-הקורפוס
נשאר ב-pipeline המקומי הדורבילי (scripts/final_halacha_pipeline.py, X16 checkpoint/resume).
קובץ-פרומפט בלבד — נקרא חי דרך instructionsFilePath מהעץ הראשי (אין deploy ל-Coolify;
דורש git pull בעץ הראשי אחרי מיזוג). leak_guard עובר.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ריצת-הפאנל החיה חשפה Fleiss κ=-0.07 למרות 97.5% הסכמה-גסה (28/40 פה-אחד, 11/40 רוב).
זה אינו חוסר-אמינות אלא **פרדוקס-הקאפא**: ה-marginal של is_holding מוטה קיצונית
(≈הכול True, כמו 93/100 ה-keep בתוויות-האנוש), וכש-Pe→1 גם κ→0 (Feinstein & Cicchetti
1990, "high agreement, low kappa").
- gwet_ac1(): מדד הסכמה עמיד-שכיחות (Gwet 2008) — אותו Pa כמו Fleiss, אומדן-מקריות שונה
(2·p·(1-p)). הופך לכותרת; Fleiss κ עדיין מודווח לשקיפות + raw 3/3.
- consensus-vs-HUMAN: כשקיים תיוג-יו"ר, הדוח מודד התאמת-הקונצנזוס מולו (תוקף חיצוני).
אימות בפועל על 100 תוויות-היו"ר: 29/29 = 100% התאמה.
invariants: ללא שינוי בהתנהגות-הכתיבה; מטריקה בלבד. tests: 21 (3 חדשות, כולל מקרה-פרדוקס מפורש).
מקור: Gwet 2008 (AC1) · Feinstein & Cicchetti 1990.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
חילוץ-הלכות מוגבל למקטעי הנמקה/הכרעה בלבד (INV-LRN2 quality-at-source). הפער שנסגר:
מסלול ה-fallback (כשה-chunker לא תייג שום מקטע כ-extractable, כותרות לא-תקניות →
הכול 'other') נפל קודם ל**כל** ה-chunks — והחזיר בדיוק את המקטעים שהמסנן הראשי מחריג
(רקע עובדתי + טענות הצדדים). בלבול Facts↔Reasoning הוא מחלקת-השגיאה הדומיננטית
(LegalSeg), כך שהזנת עובדות לחילוץ פוגעת ישירות ב-precision.
- NON_REASONING_SECTIONS = (facts, appellant_claims, respondent_claims, intro)
- _select_extractable_chunks(): מרכז את מדיניות-הבחירה (primary + fallback) בפונקציה
אחת המשמשת גם את הבחירה הראשית וגם את ה-re-read לקביעת-סטטוס (G2 — מקור-אמת יחיד,
אין מסלול מקביל). ה-fallback מחריג את NON_REASONING_SECTIONS ועדיין מגיע להנמקה
שנחתה תחת 'other'.
invariants: G1 (נרמול-במקור, לא תיקון-בקריאה) · G2 (אין מסלול מקביל) · INV-LRN2 (quality-at-source).
tests: 4 חדשות (primary/fallback-excludes-args/all-nonreasoning/disjoint-sets) + 61 בדיקות-הלכה קיימות עוברות.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
מפריד בין ריקון-באקלוג המוני לבין חילוץ per-upload, ומסיר את ה"פקק" שגרם
timeout/process_lost ב-heartbeat של ה-CEO.
הבעיה (אבחנה 2026-06-11): לחיצת "חלץ הלכות" על תיק בודד יצרה issue (CMP-165)
שהורה ל-CEO להריץ precedent_process_pending(halacha) — בולען סינכרוני שמרוקן את
כל התור ההיסטורי (147 ממתינים, שעות) בתוך heartbeat שחסום לשעה. תוצאה: timeout
כל שעה → process_lost בפירוק קבוצת-התהליכים → retry → סטורם, והתיק הבודד (FIFO
אחרון) לא טופל. לא OOM, לא קוד שבור — אי-התאמה ארכיטקטונית.
התיקון:
1. per-upload (web/paperclip_client.py, wake_for_precedent_extraction): גוף ה-issue
+ תיאור-הפרויקט מורים כעת להריץ precedent_extract_metadata +
precedent_extract_halachot ל-case_law_id של ה-issue **בלבד** — עם אזהרה
מפורשת לא להריץ process_pending. reextract_halachot כבר מנקה requested_at
ומסמן completed → התיק לא יחזור לתור הלילי.
2. הוראות ה-CEO (.claude/agents/legal-ceo.md): אותו שינוי — חילוץ תיק-בודד, לא
ריקון-תור. (צריך sync_agents_across_companies.py --apply אחרי מיזוג.)
3. ריקון-באקלוג (scripts/drain_halacha_queue.py): שער חלון-לילה 23:00–05:00 שעון
ישראל (zoneinfo, DST-safe — המכונה UTC). מחוץ לחלון ===SKIP===; נעצר ===STOP===
כשהחלון נסגר, השאר ממשיך בלילה הבא (FIFO + per-chunk checkpoint). env:
HALACHA_DRAIN_WINDOW_START/_END/_TZ.
4. cron (scripts/legal-halacha-drain.config.cjs): UTC band 20:00–03:00 שמכסה את
חלון-ישראל בשני מצבי-DST; הסקריפט גוזם לחלון המדויק. ירייה שעתית מחדשת
one-shot שמת (advisory-lock → חפיפה בטוחה).
רשת-ביטחון: request_halacha_extraction עדיין מסמן requested_at, כך שאם wakeup
ל-CEO נכשל — הדריינר הלילי יתפוס את התיק (בלילה, חסום), אך שום נתיב יומי לא
מרוקן את כל התור.
Invariants: מקיים G12/INV-PORT1 (paperclip_client = shell; leak_guard עובר).
נוגע X16 (durability — מתקציב-זמן heartbeat ל-job ייעודי).
בדיקות: py_compile ✓ · window-logic + zoneinfo ✓ (17:00 IDT→False) · leak_guard ✓.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
כל סוכני המערכת טוענים את ה-system prompt מקובץ תחת .claude/agents/ דרך
instructionsFilePath (claude_local + gemini_local), פרט ל-Hermes/curator על
deepseek_local שתמך רק ב-promptTemplate inline ב-DB — מסלול-פרומפט מקביל (הפרת G2),
לא מגורסת ב-git, ושני המקורות (DB ↔ hermes-curator.md) כבר התפצלו בתוכן.
מה השתנה:
- adapters/deepseek-paperclip-adapter: buildPrompt קורא instructionsFilePath אם הוגדר
(resolveTemplate; עדיפות file > promptTemplate > DEFAULT). הקובץ עובר renderTemplate
כך ש-{{wakeReason}}/{{#taskId}}/… ממשיכים לעבוד. כשל-רועש אם הקובץ הוגדר ואינו
קריא — לא fallback שקט (כלל-הנדסה §6, feedback_silent_swallow).
- hermes-curator.md הופך ממסמך-תיעוד למקור-האמת בפועל: מיזוג current-from-both —
ה-runbook התפעולי מה-DB (PIPELINE-WAKE/X16 + §A/§B + interactions) + שער
anti-hallucination (INV-AH) וקריאת-ספ (INV-AG1) שהיו רק ב-md ומעולם לא הגיעו
ל-runtime של הרמס. ה-ingest_final_version/lessons הידני הושמט — ה-pipeline (X16)
כבר מריץ אותו durably; הרצה ידנית הייתה כפילה.
נותר תפעולי (לא ב-git): עדכון 2 רשומות deepseek_local ב-Paperclip DB
(instructionsFilePath=.../hermes-curator.md, ריקון promptTemplate) + git pull בעץ
הראשי + pm2 restart paperclip + sync-agents.
Invariants: מקיים G2 (ביטול מסלול-פרומפט מקביל), G12/X15 (מגע-פלטפורמה רק במעטפת
המוצהרת — adapter), INV-AH + INV-AG1 (מגיעים סוף-סוף ל-Hermes), כלל-הנדסה §6
(כשל-רועש). ללא שינוי התנהגות-runtime פרט להוספת שער-ה-AH (כוונה מפורשת, אישור יו"ר).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
המשך ל-#182 — שני האתרים שנותרו עם query() ליצירת-טקסט/ניתוח, ששמרו על
ברירת-המחדל של ה-CLI (כל הכלים פעילים) ולכן חשופים לאותו error_max_turns:
המודל פולט stop_reason:"tool_use", מפיל את --max-turns 1, ומאלץ retry יקר.
- block_writer.py:413 — כתיבת פרוזת בלוק (Opus/Sonnet). יצירת-טקסט טהורה,
אף פעם לא צריך כלי.
- style_analyzer.py:166/183/196 — single/multi-pass + synthesis; הפלט מפוענח
כ-JSON (_parse_and_store_patterns/_extract_json). text→JSON טהור.
מיישר את שני האחרונים לאותו מסלול קנוני (claude_session.query(tools="")).
עכשיו כל קריאות ה-LLM שאינן צריכות כלים מעבירות tools="".
Invariants: מקיים INV-G2 (מסלול קנוני יחיד; סימטריה). אין בליעה שקטה (§6).
ללא שינוי-ספ.
בדיקות: py_compile נקי; 18 בדיקות (block/style/writer) עוברות.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
כל קריאות text→JSON ב-9 המחלצים העבירו את ברירת-המחדל של ה-CLI (כל הכלים
פעילים). המודל פלט מדי פעם stop_reason:"tool_use", מה שמפיל את --max-turns 1
ל-error_max_turns ומאלץ retry — ~$0.12-0.16 לניסיון, × 3. נצפה ב-drain
חילוץ-ההלכות (legal-halacha-drain, 15 כשלי error_max_turns ב-error.log).
התשתית כבר קיימת: claude_session.query מקבל tools="" לנטרול כל הכלים, ושני
מחלצים (digest_metadata_extractor, bulletin_splitter) כבר משתמשים בו. כאן רק
מיישרים את שאר המחלצים לאותו מסלול קנוני — אף קריאת חילוץ/שיפוט/סיווג טהורה
לא צריכה כלי.
מתוקנים (11 קריאות, 9 קבצים): halacha_extractor (×3: extract/NLI/consolidate),
corroboration, claims_extractor, argument_aggregator, appraiser_facts_extractor,
learning_loop, qa_validator, brainstorm, style_metadata_extractor.
Invariants: מקיים INV-G2 (מסלול קנוני יחיד; סימטריה בין מחלצים-אחים) — לא מסלול
מקביל חדש אלא שימוש עקבי בפרמטר הקיים. אין בליעה שקטה (§6) — נתיבי הכשל/retry
נשמרים. ללא שינוי-ספ.
בדיקות: 60/60 ב-tests/test_halacha_coerce.py + test_halacha_quality.py עוברות;
py_compile נקי על כל 9 הקבצים.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
הבאג: start.sh הריץ את uvicorn ברקע, בדק פעם אחת אחרי 2 שניות, ואם הוא מת
רק הדפיס שגיאה והמשיך. Next.js רץ ב-foreground אז הקונטיינר נשאר "חי" עם
backend מת — Docker/Coolify לא מפעילים restart, ו-/api/health מחזיר 503.
זה בדיוק מה שקרה אחרי עדכון הקרנל + reboot ב-2026-06-10: uvicorn לא הצליח
להגיע ל-Postgres בשניות הראשונות וה-backend נשאר מת עד restart ידני.
התיקון: start.sh הוא כעת סופרוייזר אמיתי — כל תהליך (uvicorn ו-node)
רץ בלולאת respawn עם capped backoff (1→2→…→30s, מתאפס אחרי 30s up).
race מול Postgres ב-boot נפתר מעצמו. trap על TERM/INT מבצע shutdown
נקי (PID 1 sh מתעלם מ-SIGTERM בלי trap → redeploy מהיר יותר). אם
סופרוייזר מת באופן בלתי-צפוי — exit 1 כדי ש-Docker יפעיל restart מלא.
Invariants: X3 (integration/deploy), X16 (pipeline-durability — עמידות
הרצה). לא נוגע ב-G1/G2 (אין מסלול מקביל), לא בולע שגיאות (כל restart
מתועד ל-stdout).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
הוחלף ב-legal-analyst-gemini-critique.md (שטן מליץ); 0 סוכנים מצביעים אליו.
שירת רק את ניסוי-ההשוואה החד-פעמי מול Opus (מתועד ב-comparison-opus-vs-gemini.md
ובזיכרון). אין סיבה לשמור קוד מת.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>