Commit Graph

95 Commits

Author SHA1 Message Date
44f6915f0c feat(status): single source of truth for the case-status model + canonicalize intermediates
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 11s
Fixes the case where the H1 chip, the pipeline stepper and the manual-changer
dropdown disagreed (e.g. 1043-02-26 on analyst_verified): the status sat between
the canonical 10 and a legacy bucket, so each surface read a different list.

Root fix — ONE authoritative status model + canonicalize the real intermediate
states (chair-approved):

- New mcp-server/.../case_status_model.py — the single registry: ordered
  StatusDef list (key/label/description/phase/selectable/terminal/on_enter),
  the 5 phases, STATUS_ORDER, phase_of/label_of, and a drift assertion that the
  CaseStatus enum matches it. `on_enter` is the seam for a status to *do*
  something on entry, declared next to its definition (no dispatcher wired yet).
- models.CaseStatus enum: analyst_verified + research_complete promoted to
  first-class canonical statuses (the agents set them — they belong in the
  model, not a legacy fallback).
- tools/cases.py: the forward-only STATUS_ORDER guard now derives from the
  registry instead of an inline list.
- GET /api/status-model exposes the model so the frontend mirror can be
  generated against it.
- web-ui case-status.ts mirrors it: the two intermediates moved from the
  legacy map into CASE_STATUSES / PHASES(thinking) / STATUS_LABELS /
  STATUS_DESCRIPTIONS; status-badge icons+tones extended. So the chip (status),
  stepper (its phase) and dropdown (now includes it) all agree.

Invariants: G2 — collapses the scattered status definitions (enum, inline
STATUS_ORDER, two frontend label maps) onto one backend authority + a documented
frontend mirror; no parallel status list remains. Agent-prompt alignment +
backfill follow in a separate change. py_compile + tsc + eslint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 19:31:21 +00:00
dfb2ffe7ce feat(analysis): דגל "לא-נותח" + ניתוח-מחדש מאחד + בדיקת-השפעה (#201)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
Lint — undefined names / undefined-names (pull_request) Successful in 11s
WS2b של עיצוב-מחדש זרימת-העבודה. מאפשר ניתוח כתב-הערר לבד ואז איחוד
מסמך-עיקרי שנוסף מאוחר, בלי force-delete גורף, עם diff ליו"ר.

- SCHEMA_V49: documents.claims_extracted_at + claims_extraction_status
  (אירוע-חילוץ per-מסמך, לא נגזר מ-doc_type), + אינדקס חלקי
  idx_documents_claims_pending. idempotent (ADD COLUMN IF NOT EXISTS).
  V48 שמור ל-#357.
- claims_extractor חותם את המסמך אחרי שמירת/אי-מציאת טענות.
- db.primary_docs_not_analyzed (is_primary V47 + claims_extracted_at IS NULL)
  + mark_document_claims_extracted; _row_to_doc חושף claims_analyzed.
- reanalyze_claims (כלי-MCP): snapshot→חילוץ-מאחד רק למסמכים חדשים/לא-נותחו
  (store_claims מחליף per-source ⇒ טענות אחרות נשמרות)→aggregate force=True
  (מסלול קנוני, מוחק רק legal_arguments)→_impact_diff before↔after ליו"ר.
- workflow_status חושף primary_docs_not_analyzed + next-step.
- ספ: 02-data-model §2ג (דגל לא-נותח), 04-analysis-writing §1.3.
- 5 בדיקות-יחידה ל-_impact_diff/_snapshot (פונקציות טהורות).

Invariants:
- G1 (נרמול-במקור): claims_extracted_at נחתם בנקודת-החילוץ, claims_analyzed
  נגזר ממנו — לא תיקון-בקריאה.
- G2 (מסלול קנוני יחיד / merge-not-fork): reanalyze מרחיב את מסלול
  claims_extractor+argument_aggregator הקיים, לא forks; האיחוד דרך
  store_claims per-source; aggregate force=True מוחק רק נגזר (legal_arguments).
- INV-DM (מודל-נתונים): דגל-אירוע per-מסמך, אינדקס חלקי, מקור-אמת יחיד.
- G10 (שער-אנושי): בדיקת-ההשפעה מוצגת ליו"ר, לא מוחלת אוטומטית.
- INV-TOOL idempotency: SCHEMA_V49 idempotent; ניתוח-מחדש חוזר על מסמך
  כבר-נותח הוא refresh נקי (store_methods replace per-source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 12:19:28 +00:00
9e6a441076 Merge remote-tracking branch 'origin/main' into rebase-358
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
# Conflicts:
#	docs/spec/04-analysis-writing.md
2026-06-30 12:16:41 +00:00
8d704e6e40 Merge remote-tracking branch 'origin/main' into rebase-357
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
# Conflicts:
#	mcp-server/src/legal_mcp/services/db.py
2026-06-30 12:09:49 +00:00
faee621f0b Merge pull request 'feat(drafting): טיוטת-ביניים דטרמיניסטית — Opus 4.8 + effort per-בלוק (#204)' (#356) from worktree-agent-a0d2502e8308bc27c into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m27s
G12 Leak-Guard / leak-guard (push) Successful in 4s
Lint — undefined names / undefined-names (push) Successful in 10s
2026-06-30 12:03:42 +00:00
aa2548ff5d feat(analysis): סיכום-מנהלים מזוקק של טענות הצדדים — מסמך הכנה לדיון (#202, WS3)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
מסמך פרוזה נפרד מטיוטת-הביניים (החלטת-יו"ר): תמצית מוקפדת של טענות+תשובות
להכנת היו"ר לדיון. נגזר מ-legal_arguments/claims הקיימים — אותו מקור-אמת,
ללא חילוץ-מחדש (G2). מתוחם לטענות-התיק בלבד (INV-AH).

- כלי-MCP חדש summarize_party_claims (drafting.py + server.py); ייצור עובר
  claude_session נעוץ Opus 4.8 + effort=high (זיקוק/סינתזה); local-only.
- שירות party_claims_summary.py: בחירת-מקור arguments→claims, פרומפט מעוגן-מקור,
  שמירה ל-data/cases/{n}/documents/research/ (git + S3).
- ייצוא DOCX בסגנון-תבנית דפנה (build_party_claims_summary_docx) + endpoints
  read/download/export-docx ב-web/app.py (מגישים בלבד — אין claude CLI בקונטיינר).
- _next_version הוכלל ל-prefix-scoped (תאימות-לאחור) למניעת התנגשות-גרסאות.
- ספ 04-analysis-writing.md §1.3 + 8 בדיקות אופליין (מקור/נעיצת-מודל/markdown→docx).

Invariants: G2 (מקור-אמת יחיד, בלי מסלול מקביל) · X9 TOOL1/TOOL4 (envelope +
extract↔serve) · INV-AH (עיגון-מקור, abstention על טענה לא-ברורה) ·
claude_session local-only · G9 (provenance — נשמר durably + git/S3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 11:22:20 +00:00
24e3e2fe80 feat(analysis): analyze_protocol — comparative hearing-protocol analysis (WS4 #203)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
מנתח פרוטוקול-דיון מול כתבי-הטענות: אילו טענות ירדו/חוזקו/עלו-חדשות + חידוד
שאלות משפטיות, ומחלץ נתוני כותרת (א–ד). התוצאה נכנסת לידע-התיק.

- protocol_analyzer.py — ניתוח השוואתי דרך claude_session (Opus 4.8, effort=high,
  מקומי בלבד); שער anti-hallucination (quote-or-retract) ב-_normalize_change;
  חילוץ א–ד + הזנת hearing_date חזרה ל-cases (לא דורס קלט-יו"ר).
- claims_extractor: protocol → claim_type='protocol'; extract_claims קולט protocol.
- block_writer._build_claims_context מחריג claim_type='protocol' מבלוק ז (INV-WR4).
- db: טבלת protocol_analysis (V47, idempotent per-doc) + replace/list helpers.
- tools/drafting + server: analyze_protocol + get_protocol_analysis (extract/get).
- ספ 04-analysis-writing §1.3 + 15 בדיקות.

Invariants: G1 (נרמול-במקור: hearing_date לעמודה הקנונית), G2 (ידע-תיק נגזר
ממקור-האמת protocol+legal_arguments, לא מסלול מקביל), INV-TOOL3 (idempotent
per document_id), INV-TOOL4 (extract/get symmetry), INV-AH (quote-or-retract),
INV-WR4 (בלוק ז = טענות-כתב מקוריות בלבד), claude_session local-only, G12 (נקי
מ-Paperclip — leak-guard עובר).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 11:21:13 +00:00
aad2de9205 feat(drafting): טיוטת-ביניים דטרמיניסטית — נעיצת Opus 4.8 + effort per-בלוק (#204)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
מנגנון-הייצור (claude_session→claude -p) לא העביר model/effort: BLOCK_CONFIG
הגדיר temp+model per-בלוק שהיו מטא-דאטה-מת (block_writer.py:447 העביר רק
prompt/timeout/tools). הייצור רץ על מודל ברירת-המחדל של ה-CLI עם פרומפטים-חופשיים
→ מקור אי-העקביות בטיוטת-הביניים.

תיקון (עקביות-מבנית, WS5):
- נעיצת model="claude-opus-4-8" (GENERATION_MODEL) לכל בלוקי-ה-AI + effort per-בלוק
  שמועבר בפועל דרך claude_session.query → `claude -p --model --effort`.
  מפת-effort: ה=medium · ו=medium · ז=high · ח=medium · ט=high · יא=high · י=xhigh.
- ניסוח-פתיחה קבוע לבלוק-ה (תמיד "לפנינו ערר…", סוף ל-OR "עניינה של החלטה זו").
- מבנה-פנימי קבוע לבלוק-ה; סדר-בלוקים בייצוא כבר קבוע (_INTERIM_BLOCK_ORDER).
- הסרת שדות temp/model המתים מ-BLOCK_CONFIG; "temp" deprecated (Opus 4.7/4.8 דוחים
  temperature→400). decision_blocks.temperature נשמר=0 לתאימות-עמודה בלבד.
- timeout נגזר מ-effort (לא מפיצול sonnet/opus שכבר לא קיים).
- עדכון endpoint reference /api/settings/mcp/blocks + blocks-tab + McpBlock לחשוף effort.
- ספ: docs/block-schema.md §3 (effort מחליף temperature), docs/spec/06-export.md
  (דטרמיניזם-מבני של טיוטת-הביניים).

G2 (מקור-אמת יחיד) — אין מסלול-ייצור מקביל: write_interim_draft מתזמר את אותו
write_and_store_block→write_block של ההחלטה המלאה.
claude_session-local-only — הייצור נשאר דרך גשר-ה-CLI, לא SDK/קונטיינר.
INV-EX1 — סדר-ייצוא דטרמיניסטי נשמר; G11 — עקרונות-הכתיבה ל-12 הבלוקים נשמרים.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 11:18:56 +00:00
7a7249af38 feat(documents): מסמך-עיקרי — is_primary נגזר + רשימה קנונית (#200)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
WS2 (#200): מבחין בין מסמך-עיקרי (מסמך-מהות שהיו"ר עוקבת אחריו לניתוח)
למשני. רשימה קנונית מאושרת-יו"ר: appeal/response/objection/protocol/
appraisal/decision; כל doc_type אחר = משני.

- db.PRIMARY_DOC_TYPES = מקור-אמת יחיד (tuple) + is_primary_doc_type().
- SCHEMA_V47: documents.is_primary BOOLEAN GENERATED ALWAYS AS
  (doc_type = ANY(PRIMARY_DOC_TYPES)) STORED — Postgres גוזר, אין מסלול-
  כתיבה מקביל, לא יכול לסטות מ-doc_type (G1/G2/INV-DM7, כמו ה-tsvectors).
  אידמפוטנטי (ADD COLUMN IF NOT EXISTS, ביטוי קבוע) + אינדקס חלקי
  idx_documents_primary לשאילתת "עיקריים שטרם-נותחו" (#201).
- _row_to_doc גוזר גם בקריאה (רשומות טרום-מיגרציה) + מוסיף doc_category
  ∈ {primary, secondary}. נחשף דרך document_list / case_get / API.
- 02-data-model.md §2ג מתעד את השדה הנגזר.

Invariants: G1 (נרמול-במקור — נגזר, לא נכתב), G2/INV-DM7 (מקור-אמת
יחיד, אין עמודה כפולה/מסלול מקביל), INV-DM3 (דפוס GENERATED drift-free),
INV-DM6 (ללא enum-TEXT חדש — סיווג נגזר). §6 (אין בליעה שקטה).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 11:16:26 +00:00
14ab7b0cae feat(learning): lesson synthesis — merge overlapping lessons into richer super-lessons (#158 / INV-LRN8)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
הפתרון-האמיתי לחיתוך-השקט limit=15 (#157): במקום ערימת לקחים גולמיים חופפים, ממזגים
לקחי-סגנון דומים ל"לקח-על" אחד עשיר ומוכלל — הסט שזורם לכותב קטֵן ומשתבח (Authorial
Style Profiling). מחקה את סינתזת-הקנוני (V41/INV-LRN6) על אותה טבלה (G2, אין מאגר-מקביל).

מנגנון (services/lesson_synthesis.py, מסלול-יחיד):
- אשכול greedy לפי cosine (LESSON_SYNTH_CLUSTER_THRESHOLD) בתוך shard practice_area+category.
- מיזוג ע"י claude_session מעוגן-מקור (INV-AH, סגנון-בלבד INV-LRN5, abstain) + שער-drift
  (cosine מול centroid ≥ LESSON_SYNTH_DRIFT_FLOOR).
- לקח-על נכתב source='synthesis' + synthesized_from; המקורות→review_status='superseded'.
- שער מדורג-הפיך (הכרעת-יו"ר): מקורות approved → לקח-על approved (זורם), veto-יו"ר משחזר
  את המקורות (db.revert_lesson_synthesis, מחובר ל-PATCH lessons).
- idempotency: lookup-cosine מול synthesis קיים לפני INSERT.

נגזרות: SCHEMA_V46 (embedding vector(1024) + synthesized_from + ivfflat); כלי-MCP
lesson_synthesize_pending; scripts/backfill_lesson_synthesis.py (--dry-run/--apply, audit CSV);
config LESSON_SYNTH_*; spec INV-LRN8; SCRIPTS.md.

get_recent_decision_lessons ללא שינוי — superseded יוצא (מסנן approved), synthesis נכנס.
UI badges (synthesis/superseded) נדחים לשער-העיצוב (מוגנים ב-fallback, ללא קריסה).

בדיקות: py_compile ✓ · leak-guard G12 ✓ · smoke-test טהור לאשכול/cosine/centroid ✓.
אימות functional מלא (dry-run מול DB+voyage+claude CLI) — בהוסט אחרי-deploy, כמו V41.

Invariants: G2 (מסלול-יחיד, אותה טבלה), INV-AH (עיגון+drift), INV-LRN1/G10 (שער מדורג-הפיך),
INV-LRN5 (סגנון-בלבד), INV-LRN8 (חדש). depends-on #157/#159.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 22:24:42 +00:00
8b23542dec feat(learning): chair-feedback style corrections auto-flow to the writer (P1 #6)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
The chair-feedback chain was DEAD: 27 feedback rows captured, 0 ever became a
lesson the writer reads (no weekly-analysis job ever existed). The chair's own
corrections — the most authoritative signal of all, and exactly "learn from every
returned draft" — went nowhere.

decision_lessons can't carry them: it's FK-coupled to a style_corpus row (a signed
final), which a case-in-progress doesn't have. So chair STYLE feedback instead
rides the discussion_rules channel that already reaches the writer for all blocks —
the same path /training promote uses.

- db.append_global_rule: the locked read-modify-write append, extracted from web
  `_append_methodology_override` into one shared impl (G2). The web function is now
  a thin wrapper that seeds defaults; chair-feedback calls it directly.
- record_chair_feedback (MCP tool): a STYLE-category correction (style/wrong_tone/
  wrong_structure) with a lesson_extracted flows immediately to discussion_rules.
  The chair IS the gate — no separate approval (INV-LRN1 graduated gate). SUBSTANCE
  feedback (missing_content/factual_error/other) is case-specific → recorded only.

Invariants: INV-LRN1 (chair-authored style = highest authority, flows; substance
not auto-flowed), G2 (single append impl shared by promote + feedback), INV-LRN5
(style channel only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 22:06:58 +00:00
be774ab87e fix(learning): honest 3-channel curator card + capture curator findings (source='curator')
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 9s
תיקוני-מבנה ללולאת-הלמידה (TaskMaster #157):

1. כרטיס ה-curator (/training טאב "אוצֵר") ספר decision_lessons WHERE source='curator'
   — ערך שאף מסלול-קוד לא כתב → תמיד 0. עוצב-מחדש (דרך שער-העיצוב Claude Design, אושר)
   להציג ביושר את שלושת ערוצי-ההזנה לכותב: דיסטילציה→appeal_type_rules (180, זורם),
   פאנל→decision_lessons (81, ממתין), אוצֵר→source='curator'. get_curator_stats שוכתב.

2. drift ספ↔סוכן: 07-learning.md §1.1 + INV-LRN3 קבעו שהאוצֵר רושם ממצאים כ-decision_lesson
   source='curator', אך הסוכן כתב comments בלבד — הממצאים האיכותיים אבדו. נוסף כלי-MCP
   record_curator_findings + §A.5b ב-hermes-curator.md (read-only נשמר; הצעה מגודרת-שער).

3. get_recent_decision_lessons(limit=15) חתך בשקט — נוסף WARN על מה שנחתך (חוקה §6);
   הפתרון האמיתי = סינתזת-לקחים (TaskMaster #158).

Invariants: מקיים INV-LRN1/G10 (שער-יו"ר), INV-LRN3 (לכידה מובנית), INV-IA2/IA5 (מקור-אמת
יחיד), G12 (leak-guard עובר), G2 (אין מסלול מקביל — אותם מאגרים). פער-מימוש פתוח מתועד:
§A לא רץ אוטומטית על mark-final (pipeline-wake exits) → נדחה לתכנון נפרד.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 21:20:05 +00:00
70f93c3bd4 feat(digests): case-contextual digest radar — surface unlinked digests as chair leads (X12)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
Lint — undefined names / undefined-names (pull_request) Successful in 14s
A digest pointing at a ruling we don't hold yet ("unlinked") was captured globally
(missing_precedents inbox) but never surfaced IN THE CONTEXT of the case being
decided — so a relevant ruling known only via a digest could fall through the cracks
at the moment it matters. Adds the case-contextual radar:

- db.search_digests_semantic: new `linked_only` filter (False = unlinked-only target set).
- digest_library.case_digest_radar(case_number): builds the case topic from title +
  appeal_subtype + the analyst's claims, embeds once, matches against UNLINKED digests,
  and returns leads enriched with the underlying ruling's gap status + suggested action
  (new_lead / gap_open / fetched / available_link).
- MCP tool `digest_radar` + endpoint GET /api/cases/{n}/digest-radar (for the agent and
  the future case-page lead).

INV-DIG1 preserved: radar only — every lead points at the underlying RULING (fetch /
upload / link), never cites the digest. Read-only.

Validated on 8124-09-24 (היטל השבחה): 5 on-topic unlinked digests, scores 0.64–0.72.
The visible case-page panel is a separate UI change → goes through the design gate.

Invariants: G2 (reuses the one digest semantic-search + gap/citation resolvers),
INV-DIG1 (no digest citation), INV-DIG3 (gap surfacing). No schema change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:45:14 +00:00
ccc5a73bc8 feat(x11): treatment-aware citation authority wired into research agents (#154)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
The internal citation graph fed only RANKING (raw in-degree), and the per-citation
TREATMENT was never classified — so a precedent distinguished N times got the same
authority boost as one followed N times (INV-COR2 violation), and the signal never
reached the agents' reasoning. Wires the full path:

Phase 1 — scripts/classify_citation_treatments.py: classify each linked edge's
  treatment (followed/distinguished/…) from its match_context via
  corroboration.classify_treatment (Opus 4.8 @ xhigh, local), filling
  precedent_internal_citations.treatment. Idempotent.
Phase 2 — db.refresh_verified_layer: count only NON-negative treatments toward
  verified/cite_count (INV-COR2/COR4). Unclassified counts as neutral-positive so
  the signal degrades gracefully before classification runs.
Phase 3 — db.citation_authority(ids): per-precedent {total, positive, negative,
  unclassified, by_treatment}. Surfaced as `cited_by` in search_precedent_library
  hits and precedent_library_get, and `treatment` per incoming citation.
Phase 4 — legal-researcher/analyst/writer prompts: weigh & ARGUE authority
  ("הלכה שאומצה ב-N החלטות ועדת-ערר"), flag distinguished/overruled, never invent
  the count (INV-AH; writer is read-only of the analyst).

Auto-approval stays kill-switched off (chair gate preserved, INV-G10). No schema
change (treatment column already existed). Operational: run the classifier +
refresh_verified_layer over the 379 edges, then sync agents across companies.

Invariants: G2 (one classifier + one authority query, reused), INV-COR2/COR3/COR4
(negative never corroborates; point-specific; ≥N), INV-G10 (no auto-approval),
INV-AH (no invented numbers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:04:04 +00:00
4ca907b97f feat(principles): retroactive cull (Phase C) + source-derived terminology (Phase D, #152)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 11s
Phase C — scripts/cull_principles.py: re-adjudicates every existing 'original'
principle with the SAME panel regime (panel_keep_score → classify → apply_cap),
reversible (CSV backup + rejected canonical recoverable), usage-throttled.
panel_extraction.panel_keep_score + apply_cap (shared, G2). Dry-run on 3
decisions: 37→15 survive.

Phase D — services/principles.py: source-derived label הלכה (binding court) /
כלל פרשני (committee) / עיקרון (persuasive); umbrella עקרונות משפטיים. Wired into
canonical_halacha_get/list (principle_class+principle_label). UI string changes
deferred to the Claude Design gate. spec INV-LRN7; SCRIPTS.md; 7 new tests; 428 green.

Phase E needs no new code — synthesis already targets pending_synthesis, which the
cull leaves only on survivors (rejected canonicals → 'rejected').

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:14:59 +00:00
338a8a947f feat(principles): canonical_statement synthesis service + throttled backfill (Phase E groundwork, #152)
Grounded (INV-AH) multi-instance synthesis with drift guard + chair gate
(pending_review, G10). Single path used by backfill, MCP tool, nightly drain.
HELD from production run pending the principles-redesign (rename+cull, #152).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:57:48 +00:00
7c39c685e5 feat(halachot): canonical lookup-before-insert + MCP tools (Phase 3+4, V41)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
store_halachot_for_chunk: לפני כל INSERT — חיפוש cosine ב-canonical_halachot (≥0.85).
  עיקרון קיים → instance_type='citation' (אין canonical חדש).
  עיקרון חדש → instance_type='original' + יצירת canonical אוטומטית + עדכון instance_count.

config: HALACHA_CANONICAL_LOOKUP_ENABLED=true, HALACHA_CANONICAL_THRESHOLD=0.85.

db.list_halachot: פרמטר instance_type חדש לסינון.
db.list_canonical_halachot: שאילתת רשימה לפי practice_area/status.
db.update_canonical_statement: עדכון ניסוח קנוני ע"י היו"ר.

tools/precedent_library.py:
  halachot_pending: ברירת-מחדל instance_type='original' (תור ריאלי).
  halacha_review: פרמטר canonical_statement חדש (עריכת ניסוח העיקרון).
  canonical_halacha_list: כלי MCP חדש — רשימת עקרונות קנוניים.
  canonical_halacha_get: כלי MCP חדש — עיקרון + אינסטנסים.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 17:36:09 +00:00
4994ae0cba Merge pull request 'feat(plans): משיכת תב"ע מ-מנהל-התכנון (mavat) — Phase C backend-slice' (#292) from worktree-plan-fetch-mavat into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m28s
G12 Leak-Guard / leak-guard (push) Successful in 5s
Lint — undefined names / undefined-names (push) Successful in 10s
2026-06-17 11:14:44 +00:00
a55ffd59eb feat(plans): משיכת תב"ע מ-מנהל-התכנון (mavat) — Phase C backend-slice
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
ליבת-המשיכה למרשם-התכניות (V38): מספר-תכנית → זהות+תוקף מ-mavat דרך
גשר-Camoufox הקיים (G2 — אותו שירות/פורט/סוד כמו X13, בלי חדשים).

- court_fetch_service/mavat_client.py (חדש): דרייבר Camoufox מול mavat —
  עוקף F5-ASM (דפדפן-JS), search→auto-nav ל-SV4, לוכד GET /rest/api/SV4/1,
  מפענח planDetails (E_NAME/AUTH/ENTITY_SUBTYPE/GOALS) + rsInternet
  (פרסום-לאישור→ED_PUBLICATION_FILE=י"פ + DETAILS→תאריך/עמוד). מלכודת-
  דרייבר: init-script window.onerror swallow. reCAPTCHA נשאר דלוק (token).
- court_fetch_service/server.py: POST /plan-fetch (אותו Bearer).
- services/plans_fetch.py (חדש): צד-קונטיינר — httpx לגשר, מנרמל שדות.
- tools/plans.py + server.py: כלי-MCP plan_fetch (מועמד, לא כותב).
- web/app.py: POST /api/plans/fetch (503 גשר-למטה, 404 לא-נמצא).

אומת חי מול mavat: 101-1031020→י"פ 13697 (עמ' 8758, 30/07/2025),
101-1053933→י"פ 13836. מקור-אמת עשיר מתב"ע-עכשיו (שחסר י"פ).

INV-AH: כל ערך נושא source_url; שדה-חסר ריק לא מומצא. G10: מחזיר
מועמד בלבד — שער-יו"ר (review_status) נשמר. G2: מרחיב גשר+מרשם קיימים.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:09:39 +00:00
32db9621b6 feat(ui): עיצוב מחדש של טאב הסקירה בדף תיק
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
- הסרת כרטיס "סקירת התיק" הדל (3 אלמנטים בלבד)
- כפתור "התחל תהליך" עלה לפס-הפעולות בראש הדף (ליד "העלאת מסמכים")
- כרטיס חדש AgentActivityPreview: 4 הissues האחרונים + קישור לטאב מלא
- "תוצאה צפויה" ירדה לשורת מטא-דאטה בראיל הסטטוס (מוצגת רק אם נקבעה)
- תיקון באג: expected_outcome לא נשמר — guard if expected_outcome: הוחלף ב-is not None

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:06:10 +00:00
ba542f9c21 refactor(cases): צמצום תפריט-סטטוס 17→10 + מקור-אמת יחיד (UI-B1/G2)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 11s
תפריט הסטטוס-הידני הכיל 17 סטטוסים שמתוכם ~9 דקורציה טהורה — שלבי-ביניים
שאף קוד בפייפליין לא קבע ושום לוגיקה לא הסתעפה לפיהם, עם רשימות כפולות
לא-עקביות ב-6+ קבצים (UI-B1) ו-exported כסטטוס-רפאים (באג agent-audit).

הליבה (10): new, processing, documents_ready, outcome_set, direction_approved,
qa_review, drafted, exported, reviewed, final.

- SSoT חדש web-ui/src/lib/api/case-status.ts (רשימה/שלבים/תוויות/statusLabel);
  כל הצרכנים (badge/changer/timeline/guide/donut/kpi/compose) מייבאים משם.
- statusLabel() מבטיח תווית עברית תמיד — גם לערך-מורשת (נפילה עברית, לא סלאג).
- בקאנד: STATUS_ORDER 10, models.CaseStatus מיושר, set_outcome קובע
  outcome_set/direction_approved (במקום in_progress) כמו endpoint ה-web.
- exported מוקשח אחרי export-DOCX מוצלח (forward-only); widget "נכשל ב-QA"
  עודכן ל-qa_review (הסטטוס שנקבע בפועל בכשל-QA).
- scripts/backfill_case_status_trim.py: מיפוי שורות-מורשת לסטטוס-הליבה הקודם.

Invariants: UI-B1 (מקור-אמת יחיד)  · G2 (אין מסלול מקביל)  · GAP-42 (חלקי).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 09:47:13 +00:00
4be9cf8543 feat(plans): מרשם-תכניות קנוני (V38) + נוסח-ציטוט אחיד דטרמיניסטי לבלוק ט
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 4s
Lint — undefined names / undefined-names (pull_request) Successful in 10s
מוסיף ישות קנונית לתכניות בניין-עיר (תב"ע) שחוזרות בין תיקים — SSOT לזהות+תוקף
(פרסום למתן תוקף ברשומות + מס' ילקוט-הפרסומים) + משפט-ייעוד — במקום גזירה-מחדש
מהשומות בכל תיק. בלוק ט מצטט את התוקף בנוסח אחיד דטרמיניסטי (format_plan_citation),
כך שתאריך-פרסום/מס'-ילקוט לעולם לא מהוזים ע"י ה-LLM.

- DB: טבלת plans (V38) + CRUD + _normalize_plan_number (G1) + format_plan_citation;
  upsert idempotent (G3) עם כלל-מיזוג: תוקף מאושר לא נדרס — סתירה נרשמת ב-discrepancies
  (G10 / אין בליעה שקטה).
- services/plans_extractor.py: חילוץ עובדתי (claude CLI מקומי) → pending_review.
- block_writer.py: _build_plans_registry_context מזריק משפטי-ציטוט מאושרים בלבד לבלוק ט;
  תכניות חסרות/לא-מאושרות מסומנות במפורש (לא נבלעות).
- tools/plans.py + server.py: extract_plans / plan_get / plan_search / plan_list /
  plan_upsert / plan_review (שער-יו"ר G10), עם extract/get-symmetry (X9).
- scripts/backfill_plans_registry.py: ייבוא מקורפוס-ההחלטות (טיוטות + סופיי-דפנה).
- docs: block-schema (בלוק ט), SKILL, spec 02-data-model + 04.

Invariants: G1/INV-DM2/X1 (מזהה מנורמל בכתיבה) · G2/INV-DM6 (מקור-אמת יחיד, appraiser_facts
ללא שינוי) · G3 (upsert) · INV-DM4/G9 (provenance) · INV-DM5/G10 (review_status) ·
INV-AH (ציטוט דטרמיניסטי) · G5 (lookup לא קורפוס) · G11/block-schema (נוסח-הציטוט) · X9.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 13:46:26 +00:00
959cb093b4 feat(learning): אינדיקציית-תיק למצב למידת-קול + חילוץ-הלכות אחרי החלטה סופית
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
אחרי העלאת החלטה סופית והרצת שני הפייפליינים האוטומטיים (למידת-קול,
חילוץ/אימות-הלכות), התיק לא הציג אם כל תהליך בוצע/הצליח/למה-נכשל. במיוחד
תקלת chair_name ריק (2026-06-12) שמפילה בשקט את העתק-ה-case_law → חילוץ-הלכות
לא מתחיל בכלל, בלי שזה גלוי. כעת מוצגות שתי אינדיקציות ליד כפתורי-ההרצה.

Backend (גזירה ממקור-יחיד, ללא מסלול-מעקב מקביל):
- SCHEMA_V36: draft_final_pairs.learning_run (JSONB) — שדה-תיעוד על פנקס-ההתאמה
  (INV-LRN4), חותם את תוצאת-הריצה של פייפליין-הלמידה (succeeded/failed+סיבה+at).
- set_learning_run_outcome() — חיתום הצלחה/כישלון על ה-pair האחרון.
- case_learning_status() — גזירה read-only מ-draft_final_pairs/style_corpus/
  decision_lessons/case_law/halachot: בוצע? הצליח? למה-לא? כמה הלכות חולצו.
- final_learning_pipeline.py — חותם outcome בהצלחה וב-except (surfaced, לא בלוע).
- חשיפה: case_get מוסיף learning_status (→MCP + /api/cases/{case}/details) +
  endpoint ייעודי GET /api/cases/{case}/learning-status (אותה פונקציה — בלי כפילות).

UI (אושר דרך שער-העיצוב Claude Design — כרטיס 21-final-learning-status):
- useCaseLearningStatus (api/learning.ts) — hook + polling עדין בזמן in-flight.
- LearningStatusBadges — 2 שורות (למידת-קול / חילוץ-הלכות) עם badge + תת-שורה
  (מס' לקחים · רישום-קורפוס / מס' הלכות + פירוק אושרו/ממתינות/נדחו / סיבת-כישלון).
- שילוב ב-drafts-panel תחת "החלטה סופית של היו״ר" + אינוולידציה בכפתורי-ההרצה.

אומת מול ה-DB החי: הצליח+5 הלכות (8174-12-24) · נכנס-אך-pending (1200-12-25) ·
לא-נכנס-לקורפוס (8125-09-24) · round-trip חיתום-כישלון. tsc/eslint נקיים.

Invariants: G1 (נרמול-במקור — גזירה, לא טלאי), G2 (אין מסלול מקביל — שדה על
הפנקס הקיים + exposer יחיד), INV-LRN4 (פנקס-ההתאמה), INV-IA1 (מקור-אמת יחיד).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 10:50:12 +00:00
242e6cfd11 fix(learning): chair_name במקור — סופי-ועדה תמיד נכנס לקורפוס-הפסיקה (TaskMaster #134)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
הבאג: שלב-הלמידה (ingest_final_version → ingest_internal_decision) מוסיף כל
סופי כתקדים ציטוטי ב-case_law (source_kind=internal_committee), אך נכשל
בשקט (non-fatal warning) כש-cases.chair_name ריק — בגלל constraint
case_law_internal_chair_check. כך סופיים של 1194/1200/8070 לא נכנסו
לקורפוס-הפסיקה. שורש: (1) chair_name לא נקבע בפתיחת תיק; (2) מסלול-ה-MCP
העביר chair גולמי בעוד מסלול-ה-UI (web/) כבר פתר אותו דטרמיניסטית —
**מסלולים מקבילים מתפצלים (הפרת INV-G2)**; (3) הכשל נבלע (נגד §6).

תיקון-שורש (3 שכבות):
1. **SoT יחיד (INV-G2):** `config.committee_chair_for_case` — המקום היחיד
   שגם web/app.py וגם tools/workflow.py + db.create_case גוזרים ממנו chair
   (לפי תחילית מספר-התיק; override ל-env). web/ אחוד אליו (הוסרה הכפילות).
2. **נרמול-במקור (INV-G1):** `db.create_case` קובע chair_name תמיד לא-ריק;
   `cases.case_create` חושף param. `ingest_final_version` גוזר chair מה-SoT
   במקום הערך הגולמי → ה-constraint לא נופל.
3. **נראות (§6/feedback_silent_swallow):** כשל-העתק מוחזר ב-result
   (`internal_corpus_error`) ו-`final_learning_pipeline` מדפיס אזהרה — לא
   נבלע. backfill ל-11 תיקים עם chair ריק. `audit_corpus_integrity`:
   נוספו CHECK_D (תיקים מוכרעים ללא chair) + CHECK_E (סופי-final חסר
   מקורפוס-הפסיקה) — שניהם 0 כעת.

invariants: מקיים INV-G1 (נרמול בכתיבה), INV-G2 (מסלול-יחיד, אוחד web↔MCP),
§6 (אין בליעה שקטה). בדיקות: py_compile + 14 pytest (chair_seed_gate,
audit_provenance) + integration של create_case (default+override) + הרצת
ה-audit החי (A–E=0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 07:25:54 +00:00
e8bcb9c1ea fix(cases): מספור 5-ספרתי לבל"מ — סיווג, ולידציה, וחיפוש פסיקה-חסרה
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
נוהל-יו"ר (2026-06-11): מבנה מספר-תיק = <סידורי>-<חודש>-<שנה>, ואורך הסידורי
מקודד את סוג-ההליך — 4 ספרות = ערר, 5 ספרות = בל"מ. הספרה הראשונה ממשיכה
לקבוע תחום בשני האורכים (1→רישוי, 8→היטל, 9→פיצויים). הכלל חד-כיווני:
5-ספרתי הוא תמיד בל"מ; 4-ספרתי אינו מחייב ערר (בל"מ-מורשת מזוהה מהנושא).

הבאג שדיווח עליו היו"ר: חיפוש פסיקה-חסרה לפי מספר-תיק החזיר 404 על כל ערך
שאינו תיק קיים — שבר את הטבלה תוך כדי הקלדה ועל מספרי 5-ספרות.

תיקונים:
- web/app.py: GET /api/missing-precedents — מסנן case_number שלא תאם תיק מחזיר
  רשימה ריקה (200), לא 404. סמנטיקה תקינה ל-collection-filter.
- missing-precedents/page.tsx: debounce (350ms) על שדות-הסינון — קוורי אחד
  אחרי שמפסיקים להקליד, לא אחד לכל הקשה.
- practice_area.py: regex סידורי \d{4}→\d{4,5}; case_serial_digits() +
  is_blam_by_number() (5⇒בל"מ); derive_subtype_with_blam ו-derive_proceeding_type
  מזהים בל"מ גם מ-5-ספרות (בנוסף לנושא). callers: cases.py, internal_decisions.py.
- proofreader.py: דפוסי חילוץ-שם-קובץ \d{3,4}→\d{3,5}.
- web-ui: practice-area.ts (מראָה ל-backend), schemas/case.ts (regex
  serial-month-year, 4-or-5 ספרות, superRefine 5⇒בל"מ), placeholder בוויזרד.
- תיעוד: docs/spec/X1-identifiers.md §1א + legal-ai/CLAUDE.md.

Invariants: מקיים G1 (נרמול-במקור — ספרה ראשונה כמקור-אמת יחיד לתחום),
G2 (מסלול-סיווג יחיד, אין כפילות), INV-DM/X1 (מפתח קנוני + proceeding_type).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 06:16:42 +00:00
26e0219219 fix(halacha): re-extraction preserves chair-approved halachot (INV-G10, #108)
תיקון data-loss: reset_halacha_extraction ביצע DELETE ללא-תנאי לפני חילוץ-מחדש;
קריסה בין המחיקה לאחסון הראשון מחקה את כל אישורי-היו"ר והשאירה את הרשומה תקועה
status='processing' עם 0 שורות (תקרית עמיאל 8126-03-25, 2026-06-08).

עכשיו המחיקה מחריגה review_status IN ('approved','published') — אישור אנושי לא
נמחק בשקט (INV-G10). ה-dedup-on-insert של store_halachot_for_chunk מדלג על חילוץ
טרי שמשכפל מאושרת שנשמרה, כך שאין כפילות. reset מחזיר {deleted, preserved},
וה-extractor מתעד כמה מאושרות נשמרו (provenance, G9).

עמידות מלאה מול מוות-תהליך (OOM) נשארת ל-X16/#114 (durable resume) — זה תנאי-מקדים.

בדיקה: test_halacha_reextract_preserves_approved.py (offline SQL-capture) מאמת
שה-DELETE מחריג approved/published; 64 בדיקות-הלכה קיימות עוברות.

Invariants: G10 (שער-יו"ר — אישור לא נמחק), G1 (תיקון במקור), G9 (provenance).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 09:08:16 +00:00
1986fe3b14 feat(storage): X14 Phase 2a — route source-document writes through storage.py
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>
2026-06-08 08:00:27 +00:00
f56309da5a feat(X13): auto-trigger court fetch from digests + drain tool
סוגר את הלולאה — יומון שמצביע על פס"ד בית-משפט שלא בקורפוס מזניק אחזור
אוטומטי, וקושר את היומון חזרה אחרי הקליטה (INV-DIG3 + INV-CF2).

- digest_library.try_autolink: בכשל-קישור, אם הציטוט מסווג כפס"ד-בימ"ש
  (supreme/admin) → _enqueue_court_fetch יוצר court_fetch_jobs(pending);
  ועדת-ערר (skip) לא מוזנק. never-raises (לא שובר קליטת-יומון).
- orchestrator.drain_pending(limit): מנקז pending/failed סדרתי (cooldown,
  INV-CF4), fetch+ingest לכל אחד; בהצלחה מקשר את היומון ל-case_law שנקלט.
- כלי-MCP court_fetch_drain + רישום ב-server.py.
- X13 spec: עודכן (הפער ב-INV-CF2 סומן כמתוקן).

נבדק מול ה-DB: עת"מ 46111-12-22 → job tier=admin pending digest-linked;
ערר 1110/20 → לא מוזנק. כלי מקומי בלבד (ingest = claude CLI).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 20:04:12 +00:00
acb8e2c206 Merge pull request 'feat(X13): אחזור-פסיקה אוטומטי מנט המשפט → קורפוס (Tier 0 + scaffold)' (#110) from worktree-court-fetch into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m21s
2026-06-07 18:13:15 +00:00
0990db7a3c feat(X13): auto-fetch court verdicts from נט המשפט → corpus (Tier 0 + scaffold)
תת-מערכת אחזור-פסיקה אוטומטי: כשיומון מצביע על פס"ד בית-משפט, מסווגים את
הערכאה, מורידים מהמקור הציבורי המתאים, וקולטים דרך צינור-הקליטה הקנוני.

- spec-first: docs/spec/X13-court-fetch.md (INV-CF1..CF7) + אינדקס
- מסווג court_citation.py (supreme/admin/skip) + 10 בדיקות (עת"מ 46111-12-22 → admin)
- Tier 0: court_fetch_supreme.py — supremedecisions API (reverse-engineered), httpx
  + browser-headers (אומת 200) + politeness
- תור court_fetch_jobs (SCHEMA_V30) + DB helpers + court_fetch_orchestrator.py
- Tier 1 scaffold: legal-court-fetch-service (aiohttp+Bearer, מראת legal-chat-service)
  + camofox_client (Camoufox open-source) + recaptcha_audio (Whisper מקומי) + pm2
- Tier 2 fallback חינני: manual + missing_precedent (INV-CF2/CF3 — אין drop שקט)
- כלי-MCP court_verdict_fetch / court_fetch_status; SCRIPTS.md

Invariants: מקיים G2 (מסלול-קליטה יחיד, INV-CF1) · G3/G1 (idempotent+נרמול, INV-CF5)
· G4/§6 (אין בליעה שקטה, INV-CF2) · G10 (שער-אנושי, INV-CF3) · G5 (source_type,
INV-CF6) · G9 (provenance+audit, INV-CF7). מקורות INV-CF4: RFC 9309 · Google
crawler · OWASP OAT.

Follow-ups (טרם אומתו חי): live Tier-0 validation · התקנת camofox-browser+whisper
· כיול selectors Tier-1 · COURT_FETCH_SHARED_SECRET (Infisical+Coolify) · טריגר
מ-digest try_autolink (worktree-digests-radar). V30 עלול להתנגש עם digests-radar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:12:13 +00:00
06281996ca feat(digests): Phase 2 — API endpoints + /digests UI (X12)
משטחי-משתמש לקורפוס היומונים: endpoints ב-FastAPI + דף UI נפרד /digests
(לדפדוף, חיפוש, העלאה, וקישור לפסק המקורי). היומון נשאר מקור-משני המצביע
על הפסק — אינו מצוטט בהחלטה (INV-DIG1) ואינו מחלץ הלכות (INV-DIG2).

Backend (container-safe + local split):
- digest_library: פוצל ל-create_pending_digest (CONTAINER-SAFE: stage+
  extract_text+create row 'pending', בלי LLM) ↔ enrich_digest/
  process_pending_digests (local: LLM+embed+autolink). ingest_digest מאחד.
- db.list_pending_digests; MCP digest_process_pending (tool+server) — חלופה
  ל-batch script לריקון התור.
- web/app.py: 10 endpoints /api/digests/* (upload/list/search/queue-pending/
  get/patch/delete/link/relink/unlink). upload=INSERT-only pending (ה-LLM רץ
  מקומית — claude_session local-only). כולם מחזירים dict בדפוס precedent.

Frontend (Next 16, ללא api:types — hooks עם טיפוסים hand-written כמו
precedent-library.ts):
- lib/api/digests.ts — hooks (useDigests/useDigestSearch/useDigestPending/
  useUploadDigest/useLink/Relink/Unlink/Delete/Update).
- דף /digests נפרד (לא כרטיסייה ב-/precedents — לשמור גבול סמכותי/משני,
  INV-DIG1): טאבים יומונים/חיפוש + DigestCard (badge קישור-לפסק) +
  DigestUploadDialog + pending badge. nav + header-context.

אומת: backend round-trip מלא (create_pending→list_pending→process_pending→
search→restore); web-ui מתקמפל (webpack/tsc נקי, route /digests נוצר).
הערה: build דיפולטי (turbopack) נכשל ב-worktree עקב symlink ל-node_modules —
ב-CI/Docker (node_modules אמיתי) עובד; אומת עם --webpack.

Invariants: מקיים INV-DIG1/2 (upload לא מחלץ הלכות, UI מציג "מצביע לא
מצוטט"), INV-DIG3 (link/relink/queue). G4 (אין בליעה — שגיאות→toast/HTTP),
G2 (מסלול נפרד, לא מקביל). X6 (חוזה UI↔API — endpoints בדפוס precedent;
hooks hand-written כמו שאר ה-domain modules).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 18:11:05 +00:00
8171572cdd feat(digests): קורפוס יומונים כשכבת-גילוי (radar) — X12
מאגר חדש ליומוני "כל יום" (עפר טויסטר) כשכבת-גילוי מעל קורפוסי-הפסיקה:
מקור-משני המצביע על פסק הדין המקורי, נקלט לטבלה נפרדת `digests`, נחפש
סמנטית, ומקושר לפסק המקורי בספריית הפסיקה — אך לעולם אינו מצוטט בהחלטה
ואינו מחלץ הלכות.

Phase 0 (spec):
- docs/spec/X12-digests-radar.md — INV-DIG1 (מצביע לא מצוטט) /
  INV-DIG2 (מסלול-קליטה נפרד, לא מקביל — מקיים G2) / INV-DIG3 (קישור-לפסק
  הוא הגשר; חוסר-קישור = פער גלוי). עדכון אינדקס 00/03/README.

Phase 1 (MVP):
- SCHEMA_V30: טבלת `digests` (HNSW על embedding — לא ivfflat, להימנע מ-recall
  cliff בקורפוס קטן/צומח) + GIN/FTS + UNIQUE חלקי ל-idempotent.
- services/digest_metadata_extractor.py — חילוץ-LLM (claude_session local-only,
  ייבוא lazy): תג-מושג, כותרת-הלכה, מראה-מקום, שני-תאריכים מובחנים, תגיות.
- services/digest_library.py — מסלול קצר עצמאי (INV-DIG2): extract→hash→LLM→
  embedding יחיד→autolink. לא משתמש ב-ingest.ingest_document.
- tools/digests.py + רישום 7 כלים ב-server.py (digest_upload/list/get/link/
  relink/delete + search_digests).
- scripts/ingest_digests_batch.py — קליטה ידנית מ-data/digests/incoming.
- legal-researcher.md: שלב 2ב.0 (סריקת-radar לפני אימות) + סעיף-דוח ט +
  3 כלים ב-frontmatter. HEARTBEAT §8: ניתוב יומון→digest_upload.

אומת end-to-end: 4 יומונים נקלטו (מטא-דאטה מדויק), חיפוש סמנטי מדרג נכון
("היטל השבחה"→5160, "תמא 38"→5158), link/relink/autolink/revert + מעטפת-MCP.

Invariants: מוסיף INV-DIG1/2/3 (X12). מקיים G2 (bounded context נפרד, לא
מסלול מקביל), G3 (idempotent upsert), G4 (אין בליעה שקטה — פער-קישור מוצף),
G9 (עקיבוּת — היומון מצביע על מקור עקיב). נוגע G7 (RRF) — נדחה, חיפוש
סמנטי-בלבד בשלב 1 (FTS index מוכן).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 17:49:00 +00:00
e4651a9d06 feat(#99 / T10): get_style_guide — יחסי-זהב נמדדים מהקורפוס לצד היעד
style_distance.measure_corpus_ratios(): מפצל כל החלטה ב-style_corpus לסעיפים
(chunker) ומחשב ממוצע %-סעיף — אגרגט "_all" + פר-תוצאה (כשיש). cached.
get_style_guide מציג שורת "נמדד בפועל" עם ⚠️ על פער מטווח-היעד.

מצב נוכחי: style_corpus.outcome לא מאוכלס → מוצג אגרגט כל-ההחלטות (n=48:
רקע 26.4% / טענות 9.7% / דיון 43.8% / סיכום 20.1%); פיצול לפי-תוצאה future-ready.
המדידה גם מאירה מגבלות זיהוי-סעיפים (כוונת T10 — לסמן פער לבדיקה). חופף-חלקית
ל-T7 שמודד adherence per-draft; זה מודד את הקורפוס. כשל מדידה מוצג, לא נבלע.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 21:01:42 +00:00
420cb819f5 feat(halacha-triage): quality-gated + prioritized review queue + metrics (#84)
Backend for the halacha approval-queue triage (#84). The keyboard UI, batch
actions and defer/reject (#84.4–6) already shipped; this adds the gating,
prioritization and metrics the queue was missing.

db.list_halachot — two opt-in triage controls:
  * exclude_low_quality (#84.1): drop items carrying ANY quality_flag
    (application / quote_unverified / truncated / non_decision / thin /
    nli_unsupported / near_duplicate) — they belong in a 'needs extraction fix'
    bucket, not the chair's approve queue.
  * order_by_priority (#84.3): active-learning order — negatively-treated
    first, then most-uncertain (lowest confidence), then oldest — instead of
    FIFO, so the highest-value decisions surface first.

halachot_pending (MCP) — now gated + prioritized BY DEFAULT; include_low_quality=
true reveals the needs-fix bucket. The agent review path benefits immediately.

GET /api/halachot — same two params, default OFF (non-breaking; the UI opts in).

metrics.halacha_backlog (#84.7) — splits pending into clean vs flagged, adds
deferred, reviewed_total, approve_ratio, and a pending_by_flag breakdown, so the
backlog distinguishes real review work from extraction noise.

Deferred (documented): #84.2 near-duplicate cluster cards and wiring the UI
fetch to the new params require frontend work + an api:types regen AFTER this
deploys (the new query params aren't in prod's OpenAPI until then) — a clean
follow-up. The backend fully supports both now.

Verified against the live DB (read-only):
- pending 177 → gated-clean 110, 0 flagged items leak into the clean queue.
- priority order surfaces the lowest-confidence items first (0.55, 0.55, ...).
- backlog: pending_clean=110 / pending_flagged=67 / approve_ratio=0.916,
  pending_by_flag={nli_unsupported:59, quote_unverified:3, thin:3, truncated:2}.
- pytest tests/test_halacha_quality.py — 52 passed (no regression).

Invariants: G1 (gate at source — SQL filter, not post-hoc); G2 (no parallel
path — same list_halachot); §6 (flagged items routed to a bucket, never dropped).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 20:00:52 +00:00
a2236363d4 feat(mcp): FU-14 GAP-50 — deprecate draft_section לטובת get_block_context
INV-TOOL2. מיפוי הראה ש-6 כלי-הבלוק אינם כפילות מיותרת: write_block/
write_all_blocks/save_block_content/write_interim_draft משרתים זרימות שונות
(CLI/initial-draft מול תהליך-ה-writer "התיקון בקובץ, לא ב-DB"). הכפילות
האמיתית היחידה — draft_section (הקשר לפי-סעיף, granularity ישן) חופף ל-
get_block_context (לפי-בלוק, תואם 12-הבלוקים הקנוני).

הכרעת-יו"ר: draft_section סומן deprecated (docstring ב-server.py + drafting.py
מפנה ל-get_block_context; draft-decision.md עודכן). ללא הסרה, ללא מיזוג כלי-
הכתיבה — שמירת תהליך-הכתיבה המכוון.

בדיקות: 182/182 עוברים. GAP-49+50 סגורים.

Invariants: מקדם INV-TOOL2 + G2. מתועד ב-X9 (נסגר) + gap-audit פרוסה 9.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 18:57:02 +00:00
14568fdd15 feat(mcp): FU-14 GAP-49 — תיקון שם-הכלי המטעה (precedent_search_library)
INV-TOOL2: `precedent_search_library` (שמחפש ציטוטים מצורפים-לתיק) היה הפוך
וכמעט-זהה ל-`search_precedent_library` (ספריית-הפסיקה הסמכותית, מקור CREAC),
מה שסיכן ציטוט מהמקור הלא-נכון בהחלטה. שונה ל-`search_case_precedents` (שם
ברור: case-attached). השם הישן נשמר כ-@mcp.tool() alias deprecated המנתב לחדש
→ אפס שבירה לסוכנים חיים.

docstrings של שני כלי-הפסיקה הובהרו (case-attached מול authoritative).
עודכנו: web/app.py (typeahead), legal-researcher/legal-writer docs, precedent_library docstring.

5 כלי-החיפוש הנותרים (search_decisions/case_documents/find_similar/internal/
precedent_library) מחפשים קורפוסים מובחנים בשמות סבירים — לא בוצע rename המוני
(churn גבוה, ערך נמוך מול הסיכון).

בדיקות: 182/182 עוברים. אחרי deploy — סנכרון cross-company של doc-הסוכן.

Invariants: מקדם INV-TOOL2 + G2. מתועד ב-X9 + gap-audit פרוסה 8.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 18:51:17 +00:00
29af008271 feat(mcp): FU-14 GAP-48 פרוסה 3 — envelope למשפחת drafting (סגירת GAP-48)
הפרוסה האחרונה של GAP-48 (INV-TOOL1). 18 כלי drafting הומרו ל-{status,data,message}
דרך tools/envelope.py — כולל מסלול הפקת-ההחלטה הקריטי.

עיקרון לכלים עם כשל משמעותי (export_docx/revise_draft/apply_user_edit): err()
ברמת-המעטפת — כך שהסוכן והמשתמש רואים את הכשל; failed_gates רוכב ב-data.
שאר הכלים: ok(data=payload) להצלחה, err להיעדר-תיק/קלט-שגוי/חריגה.

6 צרכני-app.py חוּוטו (get_decision_template, apply_user_edit ×2, revise_draft,
list_bookmarks, export_docx) עם envelope_unwrap + בדיקת status=="error"→4xx,
לשמירת חוזה-ה-API (X6) ללא-שינוי. test_export_qa_gate עודכן לחוזה החדש.

בדיקות: 182/182 עוברים (כולל שערי-QA של הייצוא).

GAP-48 סגור: כל ~12 משפחות-הכלים אחידות. נותר ב-FU-14: GAP-49/50 (שובר), GAP-54.

Invariants: משלים INV-TOOL1 + G2. מתועד ב-X9 (נסגר) + gap-audit פרוסה 7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 17:51:56 +00:00
79b9c37301 feat(mcp): FU-14 GAP-48 פרוסה 2 — envelope אחיד ל-11 משפחות-כלים
המשך מיגרציית INV-TOOL1 מעבר למשפחת-החיפוש (#71). הומרו ל-{status,data,message}:
precedent_library, citations, internal_decisions, missing_precedents,
training_enrichment, precedents, legal_arguments, cases, documents, workflow
(~55 כלים). בוטלו 5 עותקי _ok/_err משוכפלים (alias ל-tools/envelope.py — SSoT, G2).

עיקרון: envelope-status = הצלחת-הקריאה-לכלי; תוצאה-עסקית (idempotent_existing,
noop, completed...) נשמרת בתוך data. err רק לכשל אמיתי (not-found/invalid/exception).

תאימות-API: צרכני web/app.py של cases/workflow/precedents חוּוטו דרך
envelope_unwrap + בדיקת status=="error"→4xx — תשובת ה-HTTP זהה, web-ui לא מושפע.
(documents/legal_arguments/citations/... אינם נצרכים מ-app.py — agent-only.)

בדיקות: 182/182 עוברים (test_corpus_constraints עודכן לחוזה החדש).
נותר: משפחת drafting (מסלול הפקת-ההחלטה) בפרוסה נפרדת עם שער טסט-ייצוא.

Invariants: מקדם INV-TOOL1 + G2 (SSoT, ביטול כפילות). מתועד ב-X9 + gap-audit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 17:41:39 +00:00
aa0a736a7b feat(mcp): FU-14 GAP-48 פרוסה 1 — envelope אחיד (SSoT) + משפחת-חיפוש
INV-TOOL1: כלי-ה-MCP החזירו 3 מוסכמות סותרות (raw payload / {error} /
{status,message} אד-הוק) + 5 עותקי _ok/_err משוכפלים. נוצר tools/envelope.py
כמקור-אמת יחיד: ok/empty/err → {status,data,message}, כש-status מבחין
מפורשות הצלחה/ריק/שגיאה.

פרוסה 1 ממירה את משפחת-החיפוש (search_decisions, search_case_documents,
find_similar_cases, search_internal_decisions). web/app.py מפרק את המעטפת
דרך envelope_unwrap כדי לשמר את חוזה-ה-UI↔API (X6) ללא-שינוי — תשובת ה-HTTP
זהה (list על hits, {"message"} על ריק/שגיאה). טסט test_search_domain_scope
עודכן לחוזה החדש (5/5 עוברים).

החלטה: הדרגתי לפי-משפחה ולא big-bang. מפת-צרכנים: server.py pass-through,
web-ui מבודד (/api/*), רק 17 כלים נצרכים ישירות מ-app.py → סיכון מינימלי
לסוכנים החיים. ~73 כלים נותרו לפרוסות הבאות.

Invariants: מקדם INV-TOOL1 (envelope עקבי) + G2 (SSoT, ביטול כפילות _ok/_err).
לא נוגע ב-G1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 16:32:07 +00:00
40c1111e9b feat(mcp): FU-14 GAP-47 (חלק provenance) — draft_section מחזיר document_id+page+score
ה-provenance (document_id, page_number, score) כבר נשלף ב-search_similar אך
נזרק בבניית פלט draft_section. כעת מוחזר לכל קטע ב-case_documents/precedents,
כך שהכותב יכול לעקוב אחורה אל מסמך-המקור והעמוד ולצטטם, ולא לסמוך על תוכן
חסר-מקור. תוספתי בלבד — אין צרכן שמפרסר את מפתחות-הפלט, תואם-לאחור.

נותר ב-GAP-47: העברת הנחיות-יו"ר מ-analysis-and-research.md ל-DB
(get_chair_directions) — שינוי-מסלול גדול יותר, לפרוסה נפרדת.

Invariants: מקיים INV-TOOL4 (מקור-אמת נגיש) + G9 (provenance). לא נוגע ב-G2/G1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 15:58:39 +00:00
701efab726 feat(mcp): FU-14 GAP-51 — איחוד אוצר-המילים של תוצאת-תיק (set_outcome SSoT)
הכרעת-יו"ר: קנוני = 3 תוצאות אמיתיות (rejection/partial_acceptance/full_acceptance);
betterment_levy יוצא מהיותו "תוצאה" ועובר ל-override לפי practice_area.
+ עקרון "אנגלית-ב-DB, עברית-ב-UI": מפת-תוויות SSoT אחת.

lessons.py:
- VALID_OUTCOMES = 3 (הוסר betterment_levy).
- OUTCOME_LABELS_HE (SSoT לתצוגה) + LEGACY_OUTCOME_MAP + canonical_outcome().
- PRACTICE_AREA_OVERRIDES["betterment_levy"] מרכז את כל ה-guidance שהיה מפתוח כ-outcome
  (golden_ratios/opening/summary/discussion/template).
- get_lessons_for_outcome(outcome, practice_area) + format_ratios_comment(..., practice_area)
  מחילים override + מנרמלים legacy.

block_writer.py: STRUCTURE_GUIDANCE קנוני + תווית מ-OUTCOME_LABELS_HE + override betterment.
workflow.set_outcome: קנוני 3 + מיפוי-legacy סלחני; תווית מ-SSoT.
drafting.py: טבלת יחסי-זהב + get_decision_template מודעי-practice_area (override).
web-ui case.ts: הסרת betterment_levy מ-expectedOutcomes (הוא practice_area).
server.py: docstrings קנוניים.

מיגרציה: migrate_gap51_outcomes.py — 9 שורות נורמלו (rejected→rejection וכו'),
גיבוי ב-data/audit/. הקוד canonicalize בקריאה ⇒ backward-compatible גם בלי מיגרציה.

אומת: py_compile (5 קבצים) + בדיקות-יחידה offline (override/legacy/labels) + אימות-DB.
עודכנו X9 §3 + gap-audit (GAP-51 ).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 15:34:49 +00:00
ea8b48c6ac feat(mcp): FU-14 GAP-45 — extraction_status (חשיפת תור-החילוץ הסמוי)
INV-TOOL4 (visibility / persistence). תור בקשות-החילוץ (metadata/halacha) נשמר
ב-case_law.{metadata,halacha}_extraction_requested_at ומרוקן ע"י
precedent_process_pending — אבל לא היה כלי לראות את עומק-התור.

נוסף:
- db.extraction_queue_status() — count + גיל הבקשה הוותיקה לכל kind (read-only).
- plib.extraction_status() — tool wrapper (envelope _ok/_err).
- רישום extraction_status ב-server.py ליד precedent_process_pending.
- precedent_process_pending קיבל _clamp_limit (עקביות עם GAP-53).

תוספתי, read-only, אפס שבירה. עודכנו X9 (INV-TOOL4 ) ו-gap-audit (GAP-45 ).
py_compile עבר על 3 קבצי הקוד.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 15:00:25 +00:00
034b609bd3 feat(mcp): FU-14 GAP-52 — idempotency על case_create/precedent_attach/document_upload
INV-TOOL3 (idempotency על מפתח דטרמיניסטי). כל שלושת הכלים מחזירים את הרשומה
הקיימת במקום ליצור כפילות:

- case_create — מפתח case_number (כבר UNIQUE ב-schema): מחזיר את התיק הקיים
  במקום unique-violation.
- precedent_attach — מפתח (case_id, section_id, citation, quote): צירוף חוזר
  של אותו ציטוט לאותו סעיף מחזיר את הקיים.
- document_upload — מפתח (case_id, SHA-256 של בייטי הקובץ): העלאה חוזרת של אותו
  קובץ מחזירה את המסמך הקיים ו**מדלגת על copy+OCR+embed** (החלק היקר). נוספה
  עמודת documents.content_hash (תוספתי, DEFAULT '') + get_document_by_hash.

נבחרה בדיקת-מפתח ברמת-אפליקציה (SELECT-לפני-INSERT) ולא UNIQUE-constraint —
כדי לא לשבור startup אם קיימים נתונים-כפולים legacy. אין מיגרציה הרסנית.

עודכנו docs/spec/X9 (INV-TOOL3 ) ו-gap-audit (GAP-52 , פרוסה 2).
py_compile עבר על 4 קבצי הקוד. אימות runtime (restart MCP server) נדחה עד
שהחילוץ הפעיל יסתיים.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 14:52:33 +00:00
ebfe7f6a1d feat(mcp): FU-14 פרוסה 1 — get_appraiser_facts (GAP-44) + limit-caps (GAP-53)
תוספתי בלבד, אפס שבירת-תאימות. שני invariants מחוזה-כלי-ה-MCP (X9):

GAP-44 (INV-TOOL4, סימטריית extract/get): נוסף get_appraiser_facts — ה-get
המקביל ל-extract_appraiser_facts. קורא list_appraiser_facts + detect_appraiser_conflicts
מה-DB ללא חילוץ-LLM יקר ולא-דטרמיניסטי. מחזיר count=0 (לא שגיאה) אם טרם חולץ.

GAP-53 (INV-TOOL5, limit-caps / OWASP API4:2023): נוסף _clamp_limit (תקרה 200,
non-positive→max) על ~13 כלי list/search ב-server.py (case_list, search_*,
precedent_library_list, halachot_pending, missing_precedent_list, list_*_citations…).
list_chair_feedback קיבל param limit חדש (server→workflow→db עם LIMIT) — היה ללא תקרה כלל.

לא הוסף get_appraiser_facts ל-frontmatter של סוכנים (INV-AG3 "לא עודף" — ההוראות
עוד לא מפנות אליו; חיווט = follow-up). נותר ב-FU-14: GAP-45/48/49/50/51/52.

עודכנו docs/spec/X9 (INV-TOOL4/5) ו-gap-audit (סטטוס פרוסה 1).

אומת: py_compile על 4 קבצי הקוד. אימות runtime (restart MCP server) נדחה עד
שהחילוץ הפעיל של היו"ר יסתיים.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 14:37:30 +00:00
8a0c206ecd feat(reindex): precedent_reindex MCP tool (GAP-09, FU-3)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 22:09:44 +00:00
677f29ddec feat(audit): blocks_stale drift flag + health-check visibility (GAP-17, FU-7)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 21:36:56 +00:00
1f483383b9 feat(audit): log document_upload/extract_claims/export_docx (GAP-18, FU-7)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 21:31:09 +00:00
0c8d415044 fix(retrieval): scope search_decisions by domain — derive from case, block only on undeterminable case (GAP-12, INV-RET1)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:23:41 +00:00
084b31cd9b fix(qa): enforce critical-QA gate on export + fix neutral_background critical-but-passed (GAP-15/16, INV-QA3/EX3)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 17:58:50 +00:00
58ab003206 fix(retrieval): make decisions findable by name + unhide committee uploads
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m57s
Root cause of "agent can't find the Agasi decision in the corpus" (CMPA-55):
the decision was fully ingested, but the retrieval layer failed on the
realistic agent query — searching by case name.

- RC-A (#52): lexical tsvector covered only chunk content + halacha text,
  so a bare-name query ("אגסי") matched decisions that *cite* the case, not
  the case itself. Add meta_tsv on case_law(case_name, case_number) (SCHEMA
  V20) and OR it into the lexical halacha/chunk SQL with a match boost, so a
  name/number hit surfaces the case's own rows. Agasi: rank 4 → rank 1.
- RC-B (#53): precedent_library_list hard-defaulted source_kind=external_upload
  and never exposed the param, hiding uploaded ערר/בל"מ (internal_committee)
  decisions. Thread source_kind through service → tool → MCP tool (supports
  'internal_committee' / 'all_committees').
- #54: agent instructions (researcher/analyst/writer) — search-by-name
  protocol: add content/case-number, search both corpora, use all_committees
  before declaring "not in corpus".
- #55: chunker produced tiny fragment chunks ("דיון", "החלטה") from header
  keywords matched mid-sentence. Anchor SECTION_PATTERNS to line start +
  merge sub-min sections; exclude <50-char fragments at query time (484
  existing fragments hidden; full re-chunk tracked as #57).

Tests: scripts/test_retrieval_by_name.py (name ranks case above citer +
substantive regressions); chunker unit checks (0 tiny chunks). New findings
filed as tasks #56 (halacha source_kind leak) and #57 (re-chunk migration).

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