סוגר את לולאת-הלמידה (INV-LRN4): כל החלטה נסגרת מול הסופי, וכל סופי
מנותח מול הטיוטה. מזין את הטבלאות ש-T15 כבר קורא מהן.
T5 — פנקס-התאמה:
- SCHEMA_V26: טבלת draft_final_pairs (snapshot draft + final + diff + analysis + status).
- db: create/update/list_draft_final_pairs.
- mark-final (app.py): תופס snapshot של הטיוטה (decision_blocks) ברגע החתימה,
לפני שאפשר לדרוס אותו, ופותח שורת-פנקס (status=final_received).
T4 — דיסטילציה אוטומטית:
- learning_loop.process_final_version: משתמש ב-snapshot (לא בבלוקים שאולי השתנו),
מסווג style_method↔substance, שומר הצעה ב-pair (status=analyzed).
**הוסר ה-auto-upsert של style_patterns** — ביטל את ה-bug שדרס את שער-היו"ר
וזיהם סגנון במהות (INV-LRN1 + INV-LRN5).
- LESSONS_PROMPT: הפרדת style_method↔substance מפורשת + לקח מופשט בלבד.
- curator wake + hermes-curator.md: מריץ ingest_final_version ראשון; מציע רק
style_method שלא תועד; substance→מסלול precedent.
INV-LRN1 (שער-יו"ר, אין auto-commit) · INV-LRN4 (ניגוד-אמת) · INV-LRN5 (טוהר).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GAP-57 (אבטחה, CWE-798 / INV-ENV4): ה-default הקשיח
postgresql://paperclip:paperclip@... הוסר מ-3 קבצי web/. נוסף resolver משותף
require_paperclip_db_url() ב-paperclip_api.py שנכשל בקול אם PAPERCLIP_DB_URL לא
מוגדר — במקום ליפול בשקט ל-creds ידועים. Coolify מגדיר את המשתנה (אומת), אז
הייצור לא נפגע. (2 מופעים בסקריפטים מקומיים נותרו ל-FU-15 המלא.)
FU-13 (INV-AG3, GAP-46): יישור הרשאות-סוכן. התברר שהפער שמופה ב-31.5 היה רחב
מדי — יוחס לפי תיאור-תפקיד, לא ההוראות בפועל. הכרעת-יו"ר "היבריד":
- legal-analyst: נוסף aggregate_claims_to_arguments (frontmatter + שלב 7) — הכלי
שמקבץ את הטענות שהוא חילץ לטיעונים משפטיים.
- extract_references/extract_internal_citations הם מטלת-researcher (שכבר מחזיק
אותם), לא analyst — הוסרו מרשימת "החסרים".
- legal-researcher: כבר היה תקין; ה-spec היה מיושן.
עודכנו X4-agents.md (§2א, INV-AG3) ו-gap-audit.md (FU-13 ✅, FU-15 חלקי).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
הספ (docs/spec/, G1–G11) חובר לסוכני Paperclip דרך INV-AG1 אבל לא למסלול
שבו רוב הקוד נכתב בפועל — הסשן האינטראקטיבי של Claude Code. סוגר את הפער
לפני מחזור-2 (FU-9..15), שהוא כולו כתיבת-קוד.
שלוש שכבות אכיפה:
1. תיעוד — CLAUDE.md §"פרוטוקול כתיבת-קוד" + docs/spec בטבלת-הייחוס
2. hook — scripts/spec-guard.sh (PreToolUse על Edit/Write/MultiEdit, רשום
ב-.claude/settings.json) מזכיר פעם-בסשן בכל נגיעה בקובץ-קוד; non-blocking
3. PR — .gitea/PULL_REQUEST_TEMPLATE.md עם סעיף-חובה "Invariants"
המקבילה האינטראקטיבית ל-INV-AG1 שכבר אוכף על הסוכנים (HEARTBEAT §"קריאת-ספ").
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
סוגר את לולאת פידבק-יו"ר→ידע-סוכנים. עד כה resolve רק עדכן את ה-DB; עכשיו
לחיצה ב-/feedback מעירה את ה-CEO שמקפל את הלקח לקובץ לפי הקטגוריה.
- paperclip_client.py: wake_ceo_for_feedback_fold() — יוצר issue ב-Paperclip
עם הלקח + rubric ניתוב (style→SKILL.md, wrong_structure→block-schema,
אחר→lessons.md), מעיר CEO. משכפל את דפוס wake_for_precedent_extraction
- db.py: get_chair_feedback(id) — שליפת הערה בודדת עם case_number/appeal_type
- app.py: resolve endpoint מקבל fold (ברירת מחדל true); BackgroundTask
fire-and-forget; guard — רק עם lesson_extracted. מחזיר fold_queued
- legal-ceo.md: dispatch ל-feedback_fold_ + סעיף "קיפול הערת יו"ר" עם rubric
- frontend: useResolveFeedback מקבל fold; /feedback שולח fold=true עם toast;
drafts-panel שולח fold=false (bookkeeping per-case, בלי קיפול כפול)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
חיווט ספ-המערכת לסוכני-Paperclip כך שכל סוכן חייב לקרוא את 00-constitution
תחילה, ואז את ספ-התחום הרלוונטי לתפקידו (לפי טבלת X4 §2) — לפני עבודה מהותית.
- HEARTBEAT.md: סעיף עליון "קריאת-ספ — קודם החוקה (00), אז ספ-התחום" לפני §0–§8,
עם טבלת תפקיד→ספ ל-8 הסוכנים.
- 8 קבצי-סוכן (ceo/proofreader/researcher/analyst/writer/qa/exporter/hermes):
סעיף "קרא לפני פעולה (INV-AG1)" בראש הגוף.
- X4-agents.md: שדה "אכיפה" של INV-AG1 → "מחוּוט (פרוצדורלי)"; §5 → "בוצע".
אכיפה פרוצדורלית בכוונה — invariant פרויקטלי-תפעולי, אין שער-קוד שמכריח קריאה.
prereq לסוכני-התהליך (תת-פרויקט 5). gap-audit נשמר כ-snapshot (כמו FU-8a).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Six-phase upgrade of /training from a read-only dashboard into a full
Style Studio for managing Daphna's style corpus.
- Upload Sheet on /training: file → proofread preview → commit (no more
CLI-only `upload-training` skill).
- Rich corpus metadata: GET /api/training/corpus returns summary, outcome,
key_principles, page_count, parties (regex), legal_citation, lessons_count.
PATCH endpoint for chair edits. CorpusDetailDrawer with 4 tabs (details
/content/lessons/patterns) replaces the bare table row.
- LLM metadata enrichment: style_metadata_extractor + MCP tools
(style_corpus_enrich, style_corpus_pending_enrichment) fill summary
/outcome/key_principles via claude_session (free, host-side).
- Per-decision lessons: new decision_lessons table + 4 REST endpoints +
LessonsTab in drawer; hermes-curator now auto-posts findings as
decision_lessons(source=curator).
- Curator Portrait tab: prompt rendered with link to Gitea, recent
curator findings, style_analyzer training prompts, propose-change
form that writes proposals to data/curator-proposals/ for manual
chair review (no auto-mutation of the agent file).
- Style chat tab: SSE-streamed conversations with the style agent.
New host-side pm2 service (legal-chat-service, port 8770) wraps
claude CLI with stream-json + --resume continuation; FastAPI proxies
via host.docker.internal. Zero API cost — uses chaim's claude.ai
subscription. chat_conversations + chat_messages persist history.
Architecture: keeps the existing rule that claude_session only runs
on the host (not the container). The new legal-chat-service is the
canonical bridge between the container and the local CLI for the chat
feature; everything else (upload, metadata, lessons) stays within the
container's existing capabilities.
Audit script (scripts/audit_training_corpus.py) included for verifying
which corpus rows still need enrichment.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Same case_number can exist as both a regular appeal (ערר) and an
extension-of-time request (בל"מ), and we were inferring the difference
from appeal_subtype prefixes — fragile, and case-number lookups
weren't disambiguated. Now stored as a first-class field on both
case_law (corpus) and cases (live cases), with partial unique indexes
on (case_number, proceeding_type).
- SCHEMA_V15: column + CHECK constraints + backfill from
appeal_subtype LIKE 'extension_request_%' + partial unique indexes
replace the old global UNIQUE(case_number).
- derive_proceeding_type() centralizes the inference rule
(extension_request_* → בל"מ; subject regex fallback; default ערר).
- Metadata extractor prompt asks Claude to populate the new field
explicitly; apply_to_record writes it for internal_committee rows.
- internal_decision_upload, case_create, case_update accept an
optional proceeding_type; FastAPI request models expose it.
- Wizard + edit dialog get a sided Select; case header renders the
resolved label (ערר / בל"מ).
- Uploaded the 2 staged בל"מ decisions on betterment levy:
8126/24 (סופר נוח, 13 chunks), 8047/23 (הרנון, 48 chunks).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four parallel sub-agents closed the remaining critical gaps from the
26/05 Stage A/B sprint. Each block independently tested; aggregated here.
## #30/#31 finalizers (sub-agent A)
* Auto-derive practice_area in case_create from case_number prefix
(1xxx→rishuy_uvniya, 8xxx→betterment_levy, 9xxx→compensation_197);
default for CaseCreateRequest is now "" (the DB constraint catches
any stray "appeals_committee").
* practice_area.py: derive_subtype now handles axis-B domain values
(rishuy_uvniya/betterment_levy/compensation_197) without parsing the
case number; new helper derive_domain_practice_area().
* Halacha re-extraction verified unnecessary — all 6 reclassified
records already had is_binding=false and approved halachot.
* Regression tests: 6 cases in tests/test_corpus_constraints.py
covering practice_area enum, internal-committee chair/district,
external-upload arar prefix, MCP guard.
* UI: district input → Select dropdown (7 districts) in
precedent-edit-sheet.tsx, preserving legacy free-text values.
## #37 בל"מ subtypes (sub-agent B)
* 3 new appeal_subtypes: extension_request_{building_permit,
betterment_levy,compensation}. APPEALS_COMMITTEE_SUBTYPES extended,
SUBTYPES_BY_AREA mappings added.
* New helpers: is_blam_subject(), is_blam_subtype(),
derive_subtype_with_blam(case_number, subject, practice_area).
case_create now uses it to auto-detect "בקשה להארכת מועד" subjects.
* 3 methodology templates under docs/methodology/extension-request-*.md.
* paperclip_client.py mapping updated for the 3 new subtypes
(extension_request_building_permit→CMP, the other two→CMPA).
* Frontend: bilingual "בל"מ" badge + filter dropdown on cases list +
detail header; appeal-type-bars collapseBlam() merges בל"מ into its
parent domain for aggregate bars.
* Wizard auto-detects בל"מ from subject during case creation.
* 3 Berlinger cases (1017/1018/1019-03-26) migrated to
appeal_subtype=extension_request_building_permit via psql.
## #35 missing_precedents feature (sub-agent C)
* Schema V13: missing_precedents table (citation, case_id, party,
legal_topic, status, linked_case_law_id, claim_quote, ...) +
FK constraints + 3 indexes. Applied via psql + idempotent migration.
* 6 db.py service functions, 3 MCP tools, 6 FastAPI endpoints
(POST/GET/PATCH/DELETE/upload — upload routes by citation prefix
to ingest_internal_decision or ingest_precedent).
* Next.js page /missing-precedents with 5 status tabs + filters +
sidebar badge counter + detail drawer with metadata edit + smart
upload form that switches fields per committee/court.
* Bootstrap: 7 rows imported from the JSON file
(3 citations × cases, all status=closed with linked_case_law_id).
* legal-researcher.md: new §2ב.5 with missing_precedent_create
usage + dedup semantics + tool grant.
## #36 legal_arguments aggregation (sub-agent D)
* Schema V14: legal_arguments + legal_argument_propositions M:M.
Applied via psql.
* New service argument_aggregator.py with two functions —
aggregate_claims_to_arguments() (Claude CLI / claude_session) and
get_legal_arguments(). Graceful llm_unavailable handling when CLI
is missing (containers).
* 2 MCP tools + 2 API endpoints (POST .../aggregate-arguments as
BackgroundTask, GET .../legal-arguments).
* Frontend: shadcn Accordion + new legal-arguments-panel.tsx with
hierarchical (party → priority badge → arguments) display, "טיעונים"
tab on the case page, "חשב/חשב מחדש" buttons.
* scripts/backfill_legal_arguments.py + SCRIPTS.md entry — dry-run
found 8 candidate cases including 1017/1018/1019.
## Open follow-ups (intentionally deferred)
* npm run api:types in web-ui (CLAUDE.md flow) — recommended before
the next UI commit; not required for backend deployment.
* Run backfill_legal_arguments.py --apply once the container picks up
the new aggregator service.
* webhook on missing-precedents upload-close to Paperclip (optional).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document new daphna-procedural-patterns.md cataloging the
"appraiser clarification request" interim-decision pattern observed in
8174-24 — structure only, not phrasing (case is an outlier example).
- daphna-decision-tree.md §0.5: gating question before main tree
- legal-ceo.md voice docs table: register procedural patterns doc
- legal-writer.md: mandatory consultation when pattern_tag is set,
with explicit warning against copying 8174-24 wording
Approved via interaction request_confirmation (CMPA-15) 2026-05-17.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The legal-analyst agent was generating a longer placeholder form
[ימולא ע"י יו"ר הוועדה — עמדה/הנחיה לגבי סוגיה זו שתשמש את סוכן הכתיבה]
which _is_placeholder() did not match (substring check fails because ] is
further along in the longer form). Result: UI showed "✓ עמדה נקבעה" (green)
for all 4 issues even though no chair direction had been entered.
Fixes:
1. research_md.py: add regex fallback — any text starting with [ימולא is a placeholder
2. legal-analyst.md: template now emits the standard short placeholder only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Model drift (instructions → match DB):
- CEO: claude-sonnet-4-6 → claude-opus-4-6 (DB runs opus; CEO needs opus quality)
- מנתח/כותב/מגיה: claude-opus-4-7 → claude-opus-4-6 (DB runs 4-6; no 4-7 in adapter)
legal-proofreader.md:
- {issue-id} placeholder → $PAPERCLIP_TASK_ID בשני המקומות (done + blocked)
legal-researcher.md:
- הוסף reference ל-HEARTBEAT.md בראש הקובץ
legal-qa.md:
- הבהרת שיטת בדיקת corpus_queries_logged: grep ידני בלבד, לא validate_decision
CLAUDE.md (curator):
- הוסף תהליך אישור הצעות curator: comment → חיים מאשר → commits ל-SKILL.md/lessons.md
maxConcurrentRuns CEO: כבר 2 ב-DB — לא נדרש שינוי
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CEO (legal-ceo.md):
- הסרת company UUID ו-project UUID קשוחים בדוגמת יצירת issue
- שימוש ב-$PAPERCLIP_COMPANY_ID לחברה
- project_id נשלף דינמית מה-issue ההורה דרך $PAPERCLIP_TASK_ID
researcher (legal-researcher.md):
- הוסף mcp__legal-ai__search_internal_decisions לרשימת tools
- הוסף סעיף 2ב.2א המסביר את ההבדל: search_decisions = דפנה בלבד;
search_internal_decisions = כל ועדות הערר בכל המחוזות
- הוראות מתי להשתמש + אזהרת היררכיה (ועדת ערר < מחוזי)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CEO wakes for weekly-feedback-job via agents.invoke without issueId,
so $PAPERCLIP_TASK_ID is empty. Removed steps 4-5 (comment + close
issue) from handler — now file-write only with stdout logging.
Also commits pending docs and agent instructions from prior session.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If pc_wake_ceo fails, the endpoint now raises HTTP 502 and skips the
case_update to processing — preventing cases from silently getting stuck
with no CEO running. Also adds `processing` to CEO routing table and
updates case_list docstring with full status list.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A/B test (2026-05-05) showed DeepSeek V4-Pro is 2-3x faster and ~20x cheaper
than Sonnet for style/lexicon pattern analysis, with comparable quality.
Adds adapters/deepseek-paperclip-adapter/ package, documents adapter requirements
(env injection, run-id headers), updates CLAUDE.md with adapter integration notes,
and records lessons from ערר 1200-25 (block order for 1xxx, "להלן מתוך" pattern,
expanded factual background, bridge planning analysis, flat heading structure).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The curator's promptTemplate (stored in DB) now teaches Hermes how to
post issue_thread_interactions instead of free-text comments. Three
patterns supported, curator picks per context:
- ask_user_questions for filtering findings (multi-select)
- request_confirmation for accept/reject of a single proposal
- suggest_tasks for proposing follow-up issues
Verified end-to-end on CMP-71: curator hit a real obstacle (couldn't
read the final DOCX from its container) and chose request_confirmation
on its own to ask the user how to proceed — exactly the conversational
behavior we want.
Paperclip auto-wakes the curator with $PAPERCLIP_APPROVAL_ID when the
user responds. The new prompt has a §B branch that handles the second
wake (read response → act → close).
The UI side was already built in d099470 (mirror Paperclip interactions
in case page) — now Hermes-side agents produce interactions too, not
just claude_local agents.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous F2 stage in legal-ceo.md fired after the first DOCX export
— too early, since the user often iterates with עריכה-* uploads after
the first export. The true "this is dafna's chosen final" signal is the
"סמן כסופי" button in the UI, which calls api_mark_final.
This commit moves the curator wakeup from CEO's instructions to a
direct hook in api_mark_final:
- web/paperclip_client.py: add CURATOR_AGENTS dict (CMP + CMPA UUIDs)
and wake_curator_for_final() helper. Looks up main case issue,
creates a child issue assigned to the curator, tags plugin_state for
case visibility, and triggers wakeup via Paperclip API.
- web/app.py: api_mark_final now calls workflow_tools.ingest_final_version
(so case_law table finally gets populated for search_decisions) and
pc_wake_curator_for_final. Both are best-effort — failure does not
block marking final.
- legal-ceo.md: remove F2 stage, leave only the agents-table reference
noting the curator runs from api_mark_final.
- hermes-curator.md: update activation description to reflect the new
flow.
Result: curator runs only when chaim deliberately clicks "סמן כסופי",
on the actual final file, with no risk of analyzing a draft that will
later change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds new sub-agent "מנהל ידע" (hermes_local adapter) that runs after
each successful export to analyze the final decision and suggest updates
to skills/decision/SKILL.md and lessons. Read-only on case data, write
only on a single comment per run.
- legal-ceo.md: new stage F2 after F (export). Looks up curator by name
in current company, creates async sub-issue, no waiting. Falls back to
silent skip if no curator configured.
- legal-ceo.md: agents table updated with both curator UUIDs (CMP + CMPA).
- hermes-curator.md: role instructions documenting CMP/CMPA split and
what the curator does/does not do.
Stage 1 POC. End-to-end validated on CMP-68 (case 1130-25) with two
substantive findings on style patterns. CMPA agent created with separate
~/.hermes/profiles/curator-cmpa profile (own MEMORY.md focused on
היטל השבחה / פיצויים).
Known gaps to follow up: curator does not auto-close its issue, does
not auto-persist findings to MEMORY.md, comment attribution falls back
to chaim's user (install-key) — these are tracked separately and do
not block validation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings the legal-ai ↔ Paperclip integration in line with the official
Paperclip skill. Net effect: HEARTBEAT.md -47% (370→195 lines), all 14
agents on uniform runtime_config + budget + instructionsBundleMode, and
two cross-company helpers replacing manual SQL.
Highlights:
- HEARTBEAT.md refactor: project-specific only, delegates to the official
paperclipai/paperclip skill (loaded per agent). Adds heartbeat-context
fast-path (§1.7) and PAPERCLIP_WAKE_PAYLOAD_JSON shortcut (§1.5).
- Issue Thread Interactions API: legal-ceo.md now uses
ask_user_questions / request_confirmation / suggest_tasks instead of
free-text comments — gives chair structured UI with idempotency keys.
- pc.sh + paperclip_api.pc_request: every API call goes through helpers
that inject Authorization + X-Paperclip-Run-Id (audit trail).
- sync_agents_across_companies.py: master(CMP)→mirror(CMPA) sync via
Paperclip API, idempotent, with --verify and --apply modes.
- skills/new-company-setup: 11-step blueprint distilling all 11 gaps
into a single onboarding runbook for the next company.
- .taskmaster: 12 tasks covering each gap (one already closed: #29).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When Paperclip wakes the CEO and the model issues an mcp__legal-ai__*
call within ~10s of session init, Claude Code sometimes returns
"No such tool available" because the legal-ai MCP server hasn't
finished bringing up its tool catalog yet. Observed twice today on
CMPA precedent-extraction wakeups (sessions 9989fbaf and a9c61801);
the agent fell back to bash + .venv/bin/python and finished the work,
but the race needed fixing on the server side.
Three changes that close the window:
1. Lazy schema init (services/db.py + server.py)
`init_schema()` was awaited inside the FastMCP lifespan, blocking
the `initialize`/`tools/list` handshake until ~10 CREATE TABLE IF
NOT EXISTS statements ran. Under contention (two CEOs waking at
once for different companies) this stretched. Now the lifespan
returns immediately and `get_pool()` runs the schema migrations
exactly once on first DB access, guarded by an asyncio.Lock.
tools/list is answered in milliseconds regardless of DB state.
2. Lazy heavy imports
- services/embeddings.py: voyageai (~450ms) loaded only inside
_get_client()
- services/extractor.py: google.cloud.vision (~550ms) loaded only
inside _get_vision_client() and _ocr_with_google_vision()
These two were being imported at module top from
legal_mcp.tools.documents -> services.processor -> services.{
extractor,embeddings}, so the FastMCP server couldn't even start
responding until both finished. Cold start dropped from 2.7s to
1.17s end-to-end (init + tools/list response).
3. Agent-side warmup + retry guidance (.claude/agents/legal-ceo.md)
Even with a fast server, the model can still race on the very
first call. The precedent-extraction section now tells the CEO
to call workflow_status as a warmup probe and to retry after a
short sleep if it sees "No such tool available", before falling
back to the python bypass.
Also expanded the precedent-tool whitelists on the sub-agents that
delegate halacha/library work (commits 4a9a6b7 + 7ee90dc added the
tools to the MCP server but only the CEO got them in its allowed
list). Added to: legal-researcher (full extraction set), legal-analyst
(library_get/list + halacha review), legal-writer (library lookups +
halacha_review), legal-qa (library_get + halacha_review), and the two
that the CEO was already missing (halacha_review, halachot_pending).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a precedent is uploaded to the library, the FastAPI container now
fires a Paperclip wakeup so Claude (running locally as the CEO agent)
picks up the new row and runs `precedent_process_pending` for both
metadata and halacha extraction. The user no longer has to remember to
trigger it manually.
Mechanics:
- New `wake_for_precedent_extraction()` in paperclip_client.py creates
(or reuses) a per-company "ספריית פסיקה — תור חילוץ" project, opens
a fresh issue assigned to the company CEO with the case_law_id +
citation in the description, and pings the Board API wakeup endpoint
with `triggerDetail=precedent_library_upload`.
- ingest_precedent's _run() in app.py captures the returned case_law_id
and best-effort calls the wake function (failures are logged, not
surfaced — the upload itself stays clean).
- legal-ceo.md adds the precedent_process_pending tool family and a
new "חילוץ פסיקה אוטומטי" section that tells the CEO to short-circuit
past the heartbeat scan when woken with this trigger.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a third corpus of legal authority distinct from style_corpus
(Daphna's prior decisions for voice) and case_precedents (chair-attached
quotes per case). The new corpus holds chair-uploaded court rulings and
other appeals committee decisions, with binding rules (הלכות) extracted
automatically and queued for chair approval.
Pipeline (web/app.py + services/precedent_library.py):
file → extract → chunk → Voyage embed → halacha_extractor → store +
publish progress over the existing Redis SSE channel.
Schema V7 (services/db.py): extends case_law with source_kind +
extraction status fields under a CHECK constraint pinning practice_area
to the three appeals committee domains (rishuy_uvniya, betterment_levy,
compensation_197). New precedent_chunks (vector(1024)) and halachot
tables (vector(1024) over rule_statement, IVFFlat indexes, gin on
practice_areas/subject_tags). Halachot start as pending_review; only
approved/published rows are visible to search_precedent_library.
Agents: legal-writer, legal-researcher, legal-analyst, legal-ceo,
legal-qa get search_precedent_library. legal-writer prompt explains
the three-corpus distinction and CREAC use; legal-qa now verifies that
every cited halacha resolves to an approved row in the corpus.
UI: /precedents page with four tabs — library / semantic search /
pending review (J/K nav, A/R/E shortcuts, badge count) / stats.
Reuses the existing upload-sheet progress + SSE pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two latent issues surfaced today while watching the case 8174-24
end-to-end run, both worth documenting and engineering around because
they will recur on every future case.
Bug 1 — issue.released flips done→todo
After an agent successfully PATCHes its issue to "done", Paperclip's
internal issue.released action reverts the status to "todo" within
~30 seconds. This triggers a fresh wakeup of the same agent on a
task that is already complete.
Reproduced on CMPA-18 (30/04/26):
18:14:57 agent PATCH → status: done
18:15:35 Paperclip → issue.released → status: todo
18:15:54 new researcher run started
The fix at the right altitude (Paperclip itself) is outside our repo.
Mitigation in HEARTBEAT.md §3 — when an agent boots and finds the
issue in `todo` while expected outputs (file, DB rows) already exist,
it must short-circuit: post a "no change" comment, PATCH back to done,
and exit. Costs ~$0.20 per false wakeup but breaks the loop.
Bug 2 — Bash backtick trap on long comment bodies
Researcher agent built a curl pipeline like:
curl ... -d "$(python3 -c "body = '''...
📁 קובץ מחקר: `/path/to/file.md`
'''")"
The backticks around the file path (markdown convention) get
evaluated by the OUTER bash $(...) as command substitution. Bash
then tries to exec /path/to/file.md, which is not executable, and
prints "Permission denied" — a misleading error since the actual
file ownership is fine. The curl itself succeeded; only the bash
prelude noised up the log.
Fix in HEARTBEAT.md §4א: long bodies must go via Write→tempfile
then `curl -d @file`. Avoids every shell quoting edge case.
Files:
• docs/paperclip-quirks.md — new. Full writeup of both bugs plus
two prior known-quirks (CEO auto-block in_progress, INSERT vs
API for wakeups). Each section: what happens, empirical evidence
from logs, impact, workaround, status.
• .claude/agents/HEARTBEAT.md — added the self-recovery section to
§3 and the temp-file pattern to §4א. The temp-file pattern is the
canonical answer for any agent posting markdown comments —
applies to all 7 agents in this skill set.
• CLAUDE.md — referenced the new doc from the docs index.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two structural gaps in legal-researcher's "שלב 5: דיווח" surfaced while
auditing the case 8174-24 run:
1. **No DB linkage.** The skill told the researcher to post a comment
summarizing precedents but never to call mcp__legal-ai__precedent_attach.
The MCP tool itself wasn't even in the tools frontmatter — so even
a researcher that wanted to write to case_precedents physically
couldn't. Result: 0 rows in case_precedents after a successful
research run, even with 8 precedents identified and verified in
the comment text. The writer then has to grep free-text instead
of querying a structured table.
2. **No persisted file.** Research output existed only as a Paperclip
comment. The writer/QA can't `Read` it from disk; they have to go
through Paperclip API to fetch comment bodies. Compare to the
analyst, which is required to write `analysis-and-research.md`.
Fix:
• Added precedent_attach, precedent_list, precedent_search_library
to the tools frontmatter.
• Rewrote step 5 with explicit ordering: save to disk → attach
verified precedents to DB → update status → email → post comment.
• Documented the precedent_attach call signature inline (case_number,
citation, quote, section_id) so the agent doesn't have to reverse-
engineer it. Includes guidance on which precedents to attach
(verified with quote) vs which to leave for external verification.
Effect: future research runs will populate case_precedents and
data/cases/{N}/documents/research/precedent-research.md, both of which
the writer needs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The retry loop bug we fixed in legal-analyst yesterday existed in every
single sub-agent skill. They all post a comment + wake the CEO + exit,
leaving their own issue in `in_progress`. Paperclip's "in_progress with
no live execution" watchdog then re-wakes them, repeating until something
external transitions the issue. Watched it happen on CMPA-17 (researcher)
today — 4 iterations + manual SIGTERM + manual PATCH.
Same fix applied to all 5 remaining agents:
• legal-researcher.md
• legal-writer.md
• legal-qa.md
• legal-exporter.md
• legal-proofreader.md (file was incomplete — also added the missing
שלב 5: דיווח and wake-CEO sections to bring it to parity with the
other agents)
Each gets a "סגור את ה-issue של עצמך — חובה!" section with two PATCH
templates: one for `done` after a successful run, one for `blocked` if
checks fail or output is incomplete. The section sits before the
wake-CEO block, with an explicit reference to the CMPA-17 incident so
the rule has a concrete anchor.
Result: every agent now has the same close-issue contract. No more
zombie in_progress issues, no more 4× wakeup loops.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two structural bugs surfaced while monitoring the fresh end-to-end
run on case 8174-24:
1. **No appraiser_facts extraction.** legal-analyst.md's "what to
extract" table didn't mention doc_type='appraisal' at all, and
`extract_appraiser_facts` wasn't in its tools frontmatter. The
CEO compounded this by writing in CMPA-16's body that all 3
appraisals were "reference materials, do not extract" — which
is correct for `extract_claims` but wrong for the appraisal-
specific extractor. Result: 0 appraiser_facts in DB after a
full run, even though the user had carefully tagged each
appraisal's `appraiser_side` (committee/appellant) precisely
so detect_conflicts could compare them.
2. **Issue stays in_progress, Paperclip retries forever.** Step 7
("שמירה ודיווח") instructed the analyst to update the *case*
status, post a comment, send email, and wake the CEO — but
never to PATCH the issue itself to `done`. Paperclip's
"in_progress with no live execution" watchdog then re-woke the
analyst, which posted "I'm done" again, which re-triggered
another wakeup. We saw three iterations on CMPA-16 before the
issue finally transitioned. The PATCH pattern was already
documented in HEARTBEAT.md §4ב — the analyst skill just never
referenced it.
Changes:
• legal-analyst.md
- Added mcp__legal-ai__extract_appraiser_facts to tools list.
- Rewrote the "what to extract" table to use doc_type as the
key column and added an `appraisal` row + a callout explaining
why it goes through a different extractor.
- Added explicit step 5 "חלץ עובדות שמאי" with the call.
- Step 7 now PATCHes the issue to `done` (or `blocked` on
failure) before waking the CEO. Refers to the actual incident
so the rule has a concrete anchor.
- Cleaned up the chunking guidance — phase 1 of claude_session
already handles big docs automatically; no need to manually
split.
• legal-ceo.md (analyst issue template section)
- Replaced the generic "list of docs not to extract from" with a
per-doc_type action table that explicitly says
`appraisal → extract_appraiser_facts (NOT extract_claims)`.
- Added an explicit guard: "for every appraisal in the case,
verify the issue body says to run extract_appraiser_facts —
otherwise the writer gets a numbers-free block ז".
- Added the close-the-issue-with-PATCH instruction so the CEO
knows to write that into every analyst issue.
These edits don't affect the run currently in flight (the CEO's
prompt was already cached and the analyst already ran). They take
effect on the next analyst invocation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Until now only legal-writer referenced the voice corpus. Without these
references the qa agent can't validate writer output, the researcher
chooses precedents outside Daphna's canon, and the analyst's claims
classification doesn't match block-zayin rules.
- legal-qa: adds 8th check "voice_compliance" — block ז structure,
block י voice (אכן/אולם, "אנחנו" verbs, no numbered lists), correct
precedent from canon, acceptance template match.
- legal-researcher: must check daphna-precedent-network.md before
proposing any precedent; cross-reference with Daphna's own past
decisions via search_decisions.
- legal-analyst: reads block-zayin-claims.md — its output is the
writer's input for block ז.
- legal-ceo: lists all 6 voice docs and which agent reads each.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three new voice docs based on deep reading of 1033-25 (full-acceptance) and
7 representative cases for block-zayin (claims summary):
- daphna-acceptance-architecture.md: 5 distinct templates for case acceptance
(A: internal flaw + voiding; B: remand to committee; C: corrections in
request; D: substantive 8xxx; E: appraiser remand). Fixes the wrong
reference in architecture-by-outcome that treated full-acceptance as a
variation of partial-acceptance.
- daphna-block-zayin-claims.md: rules for claims summary block — order by
procedural role, neutrality, sub-headings per party, anti-patterns
(numbered lists, evaluation words, premature conclusion).
- daphna-decision-tree.md: operational tool that unifies all 5 voice docs
into a short analytical process. Starts with the decisive question:
"what is the winning evidence?". Decision trees for architecture
selection, opening mode, citation choice, length by weight.
Updates legal-writer.md to read decision-tree first, then the 5 voice docs,
plus block-zayin.md before block ז.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After reading all 23 1xxx decisions from style_corpus DB (in addition to
the 10 training files and 1130-25/1194-25 deep reads), synthesized two
new operational documents:
docs/daphna-precedent-network.md
- Maps each legal issue to the specific precedent Daphna cites
- 9 threshold issues (standing, השפר, סעיף 152, קנייני, פגמי פרסום,
פסילה, עבירות בנייה) with her preferred quotes for each
- 8 substantive issues (תכנון נקודתי vs כולל, חיקוק תכנית, סטייה ניכרת,
62א, חניה, תמ"א 38, תכניות ישנות, שימוש חורג)
- Lists ~30 external precedents she cites consistently + ~15 personal
precedents (her own canon — 1110/20 בעלז, 1112/22 שקופה, 1181/22 אדלר,
1130-25, etc.)
- Distinguishes precedents she cites vs. those she does NOT cite
docs/daphna-architecture-by-outcome.md
- 7 distinct block-yod architectures keyed to outcome type:
1. Pure rejection (short, 555-2000 words)
2. Rejection after complex analysis (2500-4500)
3. Threshold dismissal + merits "ועל מנת לא לצאת בחסר" (mode F)
4. Three or more distinct issues (sub-headings)
5. Partial acceptance (full funnel architecture)
6. Joined appeals
7. Remand follow-up
- Decision tree for the agent (4 questions → architecture choice)
- Internal proportions table (opening 5-10%, doctrine 15-25%, etc.)
- Costs matrix with 6 scenarios
Updated docs/daphna-voice-fingerprint.md with section 6 (additions from
23-file corpus read): 2 new opening modes (F: threshold+merits, G:
remand follow-up), nuanced sub-heading rule, self-citation of full
analytical blocks, 10 new "we" verbs, 11 traditional phrases with
sources, expanded costs matrix, transparency about petition outcomes,
warning that 1015-24 is dissent (not Daphna's voice).
Updated .claude/agents/legal-writer.md to require reading all 4 voice
docs before block-yod (the "voice quartet"), with explicit decision
tree integration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After analyzing all 24 building_permit decisions in style_corpus DB
(not just the 2 local files), refined two anti-patterns:
1. Sub-headings: actually permitted when block-yod handles 3+ distinct
legal issues (e.g., 1079-24 had "הבקשות לפסילה" / "מעמד המבקשת
וזכות עמידה" / "עותרים ציבוריים"). The earlier rule of "no
sub-headings except academic cases" was too strict — based only on
small local sample.
2. Paragraph numbering: discovered it's an evolutionary pattern, not
a static rule. Pre-2025 decisions had sequential paragraph numbers
(1, 2, 3 throughout); recent decisions (1126-25, 1128-25, 1130-25,
1194-25) abandoned it for narrative flow. The agent should NOT add
paragraph numbers — the new style.
The (1)...(2)...(3)... in-paragraph enumeration ban remains absolute —
0/33 final decisions used it. Distinction now made explicit:
in-paragraph enumeration ≠ paragraph-level numbering (former always
forbidden; latter is evolutionary).
Updated:
- docs/daphna-voice-fingerprint.md — corpus stats, refined anti-patterns
- .claude/agents/legal-writer.md — checklist with new distinctions
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Synthesized two voice documents from corpus reading:
- docs/voice-1130-25.md: deep read of case 1130-25 block-yod (5000 words),
extracting the 9-movement funnel architecture, 8 reasoning templates,
10 'we' verbs with their distinct functions, the 'akhen...ulam' pattern,
pacing/silence principles, and the deliberative meta-narrative.
- docs/daphna-voice-fingerprint.md: cross-corpus synthesis of 10 finals
(1 planning + 9 appraisal levy). Identifies 10 invariants, 5 opening
modes mapped to outcome certainty, mandatory ברמ 3644/13 preamble for
shamai cases, copy-paste templates, and 7 anti-patterns to avoid.
Updated .claude/agents/legal-writer.md:
- Added voice docs as MUST-READ before block-yod (was missing the deep
voice layer; only had surface style_guide patterns)
- Replaced the ' (1)...(2)...(3)...' enumeration template with the 5 opening
modes (the enumeration was a known anti-pattern Daphna always removes)
- Added the 'we' verbs catalog with explicit functions
- Made 'אכן...אולם' pattern mandatory for issues with substantial
counter-arguments (was vaguely 'אמנם...אולם')
- Added mandatory ברמ 3644/13 preamble for 8xxx shamai cases
- Added self-citation triple-mode (refer/defer/distinguish) — Daphna's
emerging practice of building personal jurisprudence
- Added 8-item anti-patterns checklist for post-write review
- Replaced block-yod-alef section with proper 4-paragraph closing
template (process narrative → outcome → costs → date)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Paperclip auto-blocks any in_progress issue without a live execution path
within ~1 minute of the run finishing. When the CEO ends a run with an
@chaim question pending, the main case issue was staying in_progress and
getting auto-blocked, flooding the case timeline with "automatically
retried continuation" system comments (7 occurrences on 2026-04-16).
Add an explicit status protocol to the CEO instructions:
- in_review at the end of any run that leaves a pending @chaim question
- in_progress when resuming from user_commented (also at start of comment routing)
- done only after final export
Applied at all three @chaim waiting points (stages B/C) and at the top
of comment routing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes critical bug in 1033-25: user-uploaded עריכה-*.docx files were
orphaned on disk while exports kept rebuilding from stale DB blocks.
New architecture:
- User-uploaded DOCX becomes the source of truth (cases.active_draft_path)
- System edits via XML surgery with real Word <w:ins>/<w:del> revisions
- User can Accept/Reject each change from within Word
Components:
- docx_reviser.py: XML surgery for Track Changes (15 tests)
- docx_retrofit.py: retroactive bookmark injection with Hebrew marker
detection + heading heuristic (9 tests)
- docx_exporter.py: emits bookmarks around each of the 12 blocks
- 3 new MCP tools: apply_user_edit, list_bookmarks, revise_draft
- 4 new/updated endpoints: upload (auto-registers active draft),
/exports/revise, /exports/bookmarks, /exports/{filename}/retrofit,
/active-draft
- DB migration: cases.active_draft_path column
- UI: correct banner using real v-numbers, "מקור האמת" badge,
detailed upload toast with bookmarks_added/missing_blocks
- agents: legal-exporter (3 export modes), legal-ceo (stage G for
revision handling), legal-writer (revision mode)
Multi-tenancy:
- Works for both CMP (1xxx cases) and CMPA (8xxx/9xxx cases)
- New revise-draft skill added to both companies
- deploy-track-changes.sh syncs skills CMP ↔ CMPA
- retrofit_case.py: one-off retrofit of existing files
Tests: 34 passing (15 reviser + 9 retrofit + 4 exporter bookmarks + 6 e2e)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- legal-analyst: opus 4.6 → opus 4.7
- legal-proofreader: opus 4.6 → opus 4.7
- legal-writer: sonnet 4.6 → opus 4.7 (complex block writing benefits from stronger model)
- block_writer MODEL_MAP: updated opus ID to 4.7
Opus 4.7 brings: high-res images (2576px), better file-based memory,
improved DOCX generation, and task budgets for agentic loops.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New issues created by the CEO via curl were missing plugin_state records,
causing them to be invisible in the legal-ai UI. Added iron rule: after every
POST to create an issue, INSERT into plugin_state with the case number.
Also fixed 8070-25 CMPA issues directly in DB (3 records added).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CEO agent now sends email via notify.py when awaiting human response
- CEO creates child issues (parentId) instead of flat disconnected issues
- Fix notify.py email address to chaim+paperclip@marcus-law.co.il
- Move Paperclip UI assets (RTL CSS + Hebrew JS) into repo under scripts/
- Add deploy.sh script to push assets to live Paperclip instance
- Fix comment box positioning: newest comment on top, input below it
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CEO was ignoring the focused wake reason and doing a full heartbeat
scan of all cases/issues before getting to the actual comment. Added
step 0: check $PAPERCLIP_WAKE_REASON first — if user_commented, skip
directly to comment handling. Don't scan other cases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CEO was missing get_chair_directions, record_chair_feedback,
list_chair_feedback, and search_case_documents. Without these tools
it couldn't read or update chair directions when processing draft
annotations.
Now the CEO will:
1. Read existing chair_directions via MCP tool
2. Record each draft annotation as chair_feedback
3. Update analysis-and-research.md
4. Post summary for user review before routing to writer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the user writes editing instructions inside a draft DOCX, the CEO
must not just forward them as a checklist. Instead:
1. Read analysis-and-research.md + existing chair_directions
2. Translate draft annotations into methodological structure (syllogism)
3. Update chair_directions with the new analysis
4. Post summary to user and WAIT for approval
5. Only after approval → create issue for writer
This gives the user a chance to verify the CEO understood correctly
before the writer starts working.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Even when the user asks to edit specific paragraphs in an existing
draft, the CEO must first analyze through the methodology: identify
which legal issue the edit serves, build syllogistic structure,
reference specific source documents, and state the review standard.
Without this, the writer gets a technical checklist instead of
methodological guidance.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agent JWT cannot wake other agents directly (returns "Agent can only
invoke itself"). The correct pattern: create an issue + assign to the
target agent → Paperclip triggers wakeup automatically.
Also documented all correct API routes in HEARTBEAT.md:
- POST /api/issues/{id}/comments (not /issues/)
- POST /api/companies/{company-id}/issues (not /api/issues)
- PATCH /api/issues/{id}
- POST /api/agents/{id}/wakeup (self only, with payload.issueId)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>