מחלץ את דיסציפלינת מניעת-ההזיות לבלוק קנוני אחד (docs/anti-hallucination-gate.md)
ומחיל אותו אחיד על כל הסוכנים — במקום שכל סוכן ימציא אותה מחדש ad-hoc (G2: בלי מסלולים מקבילים).
5 טכניקות, כל אחת מעוגנת במקור מקצועי:
- AH-1 עיגון-מקור (אפס ציטוט מהזיכרון) — Stanford RegLab/Magesh JELS 2025 (כלי-RAG משפטיים הוזים 17-33%)
- AH-2 quote-or-retract + AH-3 abstention — Anthropic Reduce-hallucinations
- AH-4 תיוג-ודאות — NIST AI RMF GenAI Profile + RAGAS
- AH-5 Chain-of-Verification — Dhuliawala et al. arXiv:2309.11495
הפצה DRY: הפניה ב-HEARTBEAT.md (נקרא ע"י כל סוכני Paperclip) + שורה אחידה בבלוק
'קרא לפני פעולה' של כל 8 הסוכנים, עם הערת-יישום לכל תפקיד (writer=read-only, qa=אוכף,
proofreader=אל תתקן לכיוון מונח משפטי, exporter=אפס מהות חדשה).
בנוסף: legal-ceo.md מקבל ידע על 'שטן מליץ (Gemini)' עם מדיניות on-demand טהורה —
לא בפייפליין, מופעל רק לבקשת חיים/דפנה, הפלט=לידים ליו"ר (לא לכותב, human-in-the-loop).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
עותק של legal-analyst שרץ תחת gemini_local (Gemini CLI, gemini-3.1-pro-preview)
לצורך השוואת איכות-ניתוח מול Opus. מצב read-only: שלבי extract/aggregate/case_update
מוחלפים בקריאה בלבד, והפלט נכתב ל-analysis-and-research.GEMINI.md בלבד — כדי לא לדרוס
את ניתוח-Opus הקנוני. אכיפת read-only גם ברמת MCP (excludeTools ב-~/.gemini/settings.json).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
הקישור טיעון↔פרופוזיציות כבר נשמר ב-DB (legal_argument_propositions),
אך ה-UI הציג רק את המספר. מעשיר את get_legal_arguments באותו round-trip
(JOIN ל-claims) להחזיר supporting_propositions = {id, text, source_document},
ועוטף את שורת "מסתמך על N פרופוזיציות" ב-Popover שמציג את הטענות הגולמיות
verbatim עם מקור. שקיפות ועקיבוּת מהטיעון המאוגד חזרה לטענות-המקור.
- supporting_claims נשאר id-only (תאימות לאחור: מונה, צרכני MCP)
- supporting_propositions שדה חדש אופציונלי; fallback לטקסט סטטי כשחסר
- אין מסלול מקביל (G2) — העשרה של אותו endpoint; נרמול-במקור (G1)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
סוגר את הפער שלולאת-צמיחת-הקורפוס (07-learning §1.3) הוגדרה אך לא חווטה: מסלול
/final/upload הכניס רק לקורפוס-הסגנון, וההכנסה ל-case_law הייתה best-effort
שקטה שנכשלה כש-chair_name ריק.
web/app.py — /api/cases/{case}/final/upload עכשיו, סינכרונית:
- קובע chair_name דטרמיניסטית (תיק → ברירת-מחדל-ועדה לפי prefix; לעולם לא ריק →
אילוץ case_law_internal_chair_check תמיד מסופק). לא נשען על חילוץ-LLM —
להחלטות שלנו היו"ר ידוע.
- מכניס את ההחלטה ל-case_law כ-internal_committee (תמיד, לא best-effort) →
ברת-ציטוט בהחלטות עתידיות. מטה-דאטה נוסף מועשר אסינכרונית (Gemini).
- מחלץ את הציטוטים שההחלטה מצטטת (extract_internal_citations), ו**מסמן
אוטומטית** כל ציטוט שאינו בספרייה כ-missing_precedent (open) — dedup מול קיימים.
- התוצאה מוחזרת ב-response (enrolled/linked/missing_flagged) — לא נבלעת בשקט.
הציטוטים-המקושרים מזינים את לולאת-ה-corroboration (X11) — תוקן הניתוק שבו
החלטות שלנו לא היו ב-case_law ולכן לא חיזקו הלכות.
web-ui — toast מציג "נוספה לספרייה · N ציטוטים · M חסרים סומנו".
ספ: 07-learning §0.6 עודכן. אומת ידנית על בל"מ 8126-03-25 (15 קושרו / 6 סומנו).
Invariants: INV-LRN4, X11; G2 (יכולת חסרה, לא מקבילה); feedback_silent_swallow
(כשל-הכנסה צף, לא נבלע); DM7 (סמכות נגזרת).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
style_lesson_panel.py: before writing 2/2-keep lessons, skip any whose normalized
lesson_text already exists on the corpus (any source), and collapse duplicates within
a run. Makes the run-learning button safe to click repeatedly (the curator may re-run
the pipeline) — it converges instead of piling up duplicate decision_lessons.
Verified on בל"מ 8126-03-25: re-running --apply with 7 existing lessons wrote 0
("1 כפילויות דולגו"), count stayed 7.
Invariants: INV-LRN1/G10 unchanged (proposals only, manual fold).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
הכפתורים "הרץ למידת-קול"/"הרץ אימות-הלכות" מעירים את הרמס, ובמקום שהסוכן
(DeepSeek) ירכיב כמה קריאות-כלי (שביר), הוא מריץ עכשיו פקודה דטרמיניסטית אחת.
חדש:
- scripts/final_learning_pipeline.py — (1) ingest_final_version עם נתיב-הסופי
(מדלג אם הזוג כבר analyzed; --force לחידוש), (2) רישום לקורפוס-הסגנון
(idempotent — סוגר את הפער שפאנל-הסגנון דרש corpus_id), (3) style_lesson_panel
--apply. --dry-run להרצה בטוחה.
- scripts/final_halacha_pipeline.py — extract_internal_citations →
corroboration.build_all → halacha_panel_approve --apply. --dry-run / --limit.
briefs הרמס (web/paperclip_client._curator_task_brief) פושטו לפקודה-אחת לכל
task — חסין מול הרצת-סוכן. תוקנו שני הפערים שזוהו: ingest דרש file_path,
ופאנל-הסגנון דרש style_corpus.
נלווה: תיקון help מיושן של halacha_panel_approve (--apply מחווט). SCRIPTS.md.
אומת: שני ה-pipelines רצו dry-run על בל"מ 8126-03-25 (skip-ingest, קורפוס,
פאנלים) בהצלחה. Invariants: INV-LRN1/LRN5/G10 (הפיך, שער-יו"ר ידני נשמר),
INV-DM7. G2 — תזמור של יכולות קיימות, לא מסלול-מקביל.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Restarting/stopping legal-court-fetch-service from its own /pm2/control kills
the process before it can reply — the client got a misleading 502 even though
pm2 performed the restart. Detach the self-action (sleep 1; pm2 ...) so the HTTP
response flushes first, and report success optimistically. Other targets are
unchanged. Own name via COURT_FETCH_SERVICE_PM2_NAME (default legal-court-fetch-service).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
מוסיף מסלול ייעודי לקליטת ההחלטה החתומה של היו"ר, ומפעיל אותו דרך שני
שלבים אוטומטיים מדורגים עם פאנלי-סוכנים (אוטו-אישור + אסקלציה ליו"ר).
Backend (web/):
- POST /api/cases/{case}/final/upload — קליטת final חיצוני: שמירה קנונית
(סופי-{case}.docx + עותק קורפוס-סגנון תחת case_number מלא כדי שבל"מ לא
יתנגש עם ערר באותו מספר), פתיחת draft_final_pairs (final_received). לא נוגע
ב-active_draft ולא מריץ retrofit (נבדל מ-exports/upload ו-mark-final → לא G2).
- POST .../final/run-learning + .../final/run-halacha — שלבים מדורגים שמעירים
worker מקומי (claude/DeepSeek/Gemini מקומיים בלבד) דרך הרחבת
wake_curator_for_final עם param task=learning|halacha.
פאנל-סגנון חדש (scripts/style_lesson_panel.py): שני שופטים (DeepSeek+Gemini)
על-גבי דיסטילציית-ה-Opus; הסכמה 2/2-keep → decision_lesson
(source=panel:deepseek+gemini); substance מדולג (INV-LRN5); הפיך + גיבוי CSV.
פאנל-הלכות: docstring/SCRIPTS.md עודכנו (--apply מחווט).
Frontend (web-ui/): כפתור "העלאת החלטה סופית של היו"ר" + שני כפתורים מדורגים
"הרץ למידת-קול"/"הרץ אימות-הלכות" ב-drafts-panel; כל התוויות בעברית
(badge מקור-לקח: "פאנל: דיפסיק+גמיני", "הרמס (סקירה)"...).
Spec: docs/spec/07-learning.md §0.6. Invariants: INV-LRN1/LRN4/LRN5, G10
(שער-יו"ר ידני להטמעה ל-SKILL.md/lessons.md — הפאנלים יוצרים הצעות בלבד);
G2 (מסלול-סופי הוא יכולת חסרה, לא מסלול-מקביל).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
הדף הציג את התורים באופן לא-אחיד (by_status גולמי), בלי הבחנה בין "ממתין"
(בקלוג: status=pending) ל"בתור" (התור הפעיל: requested_at IS NOT NULL), בלי
הצגת הפריט שרץ כרגע, ובלי שום שליטה בתהליכים.
מה נוסף:
1. כרטיסי-תור אחידים — בתור / ממתין(בקלוג) / בעיבוד / הושלם / נכשל + "רץ עכשיו"
(citation/case_number של הפריט בעיבוד) לכל drain (אחזור-פסיקה, מטא-דאטה,
הלכות, יומונים). שערי-אנוש (אישור-הלכות, פסיקה-חסרה) נשארים מוני-סטטוס.
2. פאנל ניהול-תהליכים בסגנון "שירותי Windows":
- דמון (court-fetch-service/xvfb/chat/reaper): הפעל-מחדש / עצור / הפעל.
- cron drain: "הרץ עכשיו" (pm2 restart) + מתג הפעל/כבה תזמון.
3. כל תגי-הסטטוס מתורגמים לעברית.
מנגנון:
- הפעל/כבה תזמון = דגל ב-DB (טבלה drain_controls). pm2 cron_restart מחיה תהליך
שעוצר ב-stop, לכן ה"כיבוי" האמין הוא דגל שכל drain בודק ב-startup (no-op מיידי
כשכבוי). הקונטיינר כותב/קורא ישירות מ-DB.
- הרץ-עכשיו + restart/stop/start = proxy ל-pm2 דרך endpoint חדש בגשר-המארח
(court_fetch_service /pm2/control), מאובטח Bearer + whitelist ל-legal-* בלבד.
- יומונים: drain_digests הועבר מ-crontab ל-pm2 (legal-digest-drain.config.cjs)
כדי שיופיע ויהיה שליט כמו כל drain. drain_halacha_queue.py הובא לבקרת-גרסאות.
Invariants: מקיים G2 (הרחבת /operations + הגשר הקיים, לא מסלול מקביל) ו-G1
(drain_controls = מקור-אמת יחיד לכיבוי, נורמליזציה במקור ולא תיקון-בקריאה).
אין בליעת שגיאות שקטה (הגשר מחזיר {ok,error}; המוטציות מציגות toast).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the write-side rewiring (INV-STG1) for the call-sites that run in
synchronous contexts, via a new blocking facade in storage.py
(put_bytes_sync / put_file_sync — asyncio.run, or a worker thread when a loop
is already running):
- services/extractor.py: multimodal thumbnail JPEGs → DERIVED (rendered in a
to_thread worker)
- services/docx_reviser.py: track-changes save (_save_docx_xml) + empty-diff
copy (copy_with_revisions) → DOCUMENTS
- services/docx_retrofit.py: in-place retrofit backup → DOCUMENTS
Each site keeps a fallback to a direct disk write when the target path is
outside DATA_DIR (caller-provided). Under the default STORAGE_BACKEND=
filesystem the bytes land exactly where they did before — zero behaviour
change.
Also: mcp_env_catalog MINIO_ENDPOINT default updated to the durable
container-name endpoint (http://minio-bx2ykvw94xbutsex41hz4vv8:9000), matching
the Coolify "Connect to Predefined Network" change made for network durability.
All binary write-sites now flow through storage.py. git-tracked text
(case.json/notes/research-md/draft-md) stays on disk by design (INV-STG7);
court-fetch temp files are ephemeral.
tests: +2 (thumbnail renderer routes through storage; put_bytes_sync
round-trip); 55 storage/docx/track-changes green; 244 collected, no import
breakage.
Keeps G2; completes INV-STG1 write coverage. Spec: docs/spec/X14-storage-minio.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Continue the write-site rewiring onto the unified storage layer (INV-STG1):
- services/processor.py: extracted-text .txt → DERIVED bucket (a derived
artifact; the DB column is the source of truth per INV-STG5, so the write
stays non-fatal)
- services/docx_exporter.py (export_decision): DOCX → DOCUMENTS bucket via
BytesIO → put_bytes, with a fallback to a direct disk write when the caller
passes an output_path outside DATA_DIR
- services/analysis_docx_exporter.py (build_analysis_docx): same pattern;
out_path is always under DATA_DIR
Under the default STORAGE_BACKEND=filesystem the bytes land at the exact
legacy path (put_bytes → DATA_DIR/key), so behaviour is unchanged. The
disk-reading bits that must stay for now (export_dir glob in _next_version)
are kept; storage-native versioning is a cutover concern.
Still on disk (sync call-sites, follow-up Phase 2c): docx_reviser
(track-changes), docx_retrofit backup, and multimodal thumbnails (rendered in
a to_thread). git-tracked text (case.json/notes/research-md/draft-md) stays on
disk by design (INV-STG7).
tests: 38 storage + docx tests green (incl. test_export_qa_gate /
test_docx_exporter_bookmarks which exercise the real export path); 242
collected, no import breakage.
Keeps G2; advances INV-STG1. Spec: docs/spec/X14-storage-minio.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewire the source-document staging writes onto the unified storage layer
(INV-STG1), replacing direct shutil.copy2 calls:
- tools/documents.py: case originals + training-corpus uploads
- services/ingest.py: _stage_file (now async) — covers precedent-library,
internal-decisions, and digests (the canonical intake helper)
- services/digest_library.py: awaits the now-async _stage_file
Each write goes through storage.put_file(..., bucket=DOCUMENTS) with the
DATA_DIR-relative key; the Hebrew original filename rides as object metadata
(INV-STG2), content-type is guessed from the extension. DB path columns are
unchanged (still the absolute dest) — object_key backfill is Phase 3.
Under the default STORAGE_BACKEND=filesystem the bytes land at the exact
legacy on-disk location (put_file → shutil.copy2 to DATA_DIR/key), so this
is zero behaviour change in prod. shutil import dropped where now unused.
tests: +2 staging regression tests (file lands under DATA_DIR at the legacy
path); 20 storage + 22 ingest tests green; 242 collected with no import
breakage.
Derived/export write sites (thumbnails, extracted text, DOCX exports) are
Phase 2b. Keeps G2; advances INV-STG1. Spec: docs/spec/X14-storage-minio.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The single choke-point for all binary file I/O (originals, derived
artifacts, exports), replacing the scattered open()/shutil/Path.write_bytes
calls across ~8 services. Backend chosen by STORAGE_BACKEND:
- filesystem (default): disk under DATA_DIR — byte-for-byte legacy behaviour
- dual: write disk + S3, read S3→disk fallback (migration window)
- s3: MinIO via aioboto3 (lazy import; absent in the filesystem path)
Keys are DATA_DIR-relative POSIX paths; the FS backend ignores the logical
bucket and keeps the existing single tree, so the default backend is zero
behaviour change. S3 maps a governance bucket (documents/immutable/derived)
→ MinIO bucket; presigned URLs are minted against the public endpoint
(browser-reachable) and carry the Hebrew filename via RFC-5987
Content-Disposition.
- config: STORAGE_BACKEND + MINIO_* (endpoint, public-endpoint, creds,
region, 3 bucket names, presign TTL)
- mcp_env_catalog: new "storage" category + 10 specs (X10/INV-ENV1)
- pyproject: aioboto3>=13 (consumed here, deployed with first use)
- tests: 18 unit tests (FS round-trip, key normalization/traversal guard,
bucket resolution, backend selection, dual write-both + S3-down fallback)
No call-sites are rewired yet — that is Phase 2 (106.3). STORAGE_BACKEND
stays filesystem in prod, so behaviour is unchanged.
Invariants: keeps G2 (one storage path replaces scattered I/O); establishes
INV-STG1 (single layer), INV-STG2 (atomic keys, Hebrew name in metadata),
INV-STG3 (governance buckets), INV-STG6 (presigned serving).
Spec: docs/spec/X14-storage-minio.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A single live page for all the background work that downloads/analyses, so the
chair can see what's running instead of guessing.
- court_fetch_service: GET /pm2 (unauthenticated, host-only) → trimmed pm2 jlist
for the legal-* services (status, restarts, mem, cron schedule).
- FastAPI GET /api/operations: aggregates the DB-backed pipelines (court_fetch
jobs, metadata + halacha extraction queues, halacha review gate,
missing_precedents, digests, recent court ingests) and proxies the host /pm2
over the docker bridge (graceful if the host service is down).
- web-ui /operations page (+ src/lib/api/operations.ts hook, nav entry under
admin): services grid (with Hebrew labels + schedules) + pipeline cards +
recent-fetch / recent-ingest lists. Auto-refreshes every 5s.
tsc --noEmit clean; pm2 status carries nothing sensitive and the bind
(10.0.1.1) is host/container-only, so /pm2 needs no secret.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
העלון החודשי "עו"ד על נדל"ן" הוא פרסום נפרד מהיומון היומי (חודשי, רב-נושאי).
לפני תכנון הקטלוג — נוריד את כל הארכיון (~29) לתיקייה. endpoint זה רק מ-stage
את ה-PDF ל-data/bulletins/incoming (ללא DB), dedup לפי content_hash. n8n ימשוך
מ-chaim.marcus@gmail (subject "עו"ד על נדל"ן") וישלח לכאן.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
שורת הניווט הצטמצמה מ-11 קישורים ישירים ל-4 קישורי-עבודה
(בית · מרכז אישורים · הערות יו״ר · ארכיון) + 2 תפריטים נפתחים:
- "פסיקה ▾": ספריית פסיקה · יומונים · פסיקה חסרה · —ניתוח וכיול— ·
מפת הקורפוס · מדגם-זהב
- "סגנון ▾": אימון סגנון · מתודולוגיה
מפת-הקורפוס, מדגם-זהב ומתודולוגיה הורדו-בדרגה מהשורה הראשית לתוך
התפריטים (לפי בקשת היו"ר) — אך כל ה-routes נשמרים, אין שינוי URL.
trigger התפריט מקבל הדגשה + קו-זהב תחתון כשאחד מילדיו פעיל;
badge "פסיקה חסרה" מוצג גם על trigger "פסיקה" וגם בתוך הפריט.
Invariants: מקיים G2 (איחוד מסלולי-ניווט, ללא יצירת מסלול מקביל —
כל הדפים נותרים נגישים, deep-links נשמרים).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>