diff --git a/.claude/agents/HEARTBEAT.md b/.claude/agents/HEARTBEAT.md index 1f8e26b..1a500fa 100644 --- a/.claude/agents/HEARTBEAT.md +++ b/.claude/agents/HEARTBEAT.md @@ -43,7 +43,7 @@ curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ **לפני שאתה מסיים, תמיד:** -פרסם comment על ה-issue: +### 4א. פרסם comment על ה-issue ```bash curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ -H "Content-Type: application/json" \ @@ -51,7 +51,9 @@ curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ -d '{"body": "סיכום העבודה..."}' ``` -עדכן סטטוס issue: +### 4ב. קבע סטטוס — done או blocked + +**אם המשימה הושלמה בהצלחה** (כל המסמכים חולצו, כל הבדיקות עברו, אין חסימות): ```bash curl -s -X PATCH -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ -H "Content-Type: application/json" \ @@ -59,6 +61,37 @@ curl -s -X PATCH -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ -d '{"status": "done"}' ``` +**אם המשימה נכשלה או חסומה** (מסמך לא חולץ, timeout, חוסר מידע, שגיאה שלא ניתנת לפתרון): +```bash +curl -s -X PATCH -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "Content-Type: application/json" \ + "$PAPERCLIP_API_URL/api/issues/{issue-id}" \ + -d '{"status": "blocked"}' +``` +**אסור** לסיים issue כ-"done" אם יש כשל שלא טופל. "done" = הכל הושלם בהצלחה. אם משהו נכשל — "blocked". + +### 4ג. העֵר את העוזר המשפטי (CEO) — חובה! +אחרי כל סיום משימה (done או blocked), **העֵר את העוזר המשפטי** כדי שיבדוק תוצאות ויחליט על הצעד הבא: +```bash +curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ + -H "Content-Type: application/json" \ + "$PAPERCLIP_API_URL/api/agents/752cebdd-6748-4a04-aacd-c7ab0294ef33/wake" \ + -d '{"reason": "סוכן [שמך] סיים משימה [issue-id] בסטטוס [done/blocked]. נדרשת בדיקה והחלטה על הצעד הבא."}' +``` +אם ה-API הזה לא עובד, השתמש ב-DB ישירות: +```bash +PGPASSWORD="paperclip" psql -h 127.0.0.1 -p 54329 -U paperclip -d paperclip -c " +INSERT INTO agent_wakeup_requests (company_id, agent_id, source, reason, status, requested_by_actor_type) +VALUES ( + (SELECT company_id FROM agents WHERE id = '$PAPERCLIP_AGENT_ID'), + '752cebdd-6748-4a04-aacd-c7ab0294ef33', + 'agent_completion', + 'סוכן סיים משימה — נדרשת בדיקה והחלטה על הצעד הבא', + 'pending', + 'agent' +);" +``` + ## 5. התראת מייל — כשנדרשת תשובה אנושית **כשהתוצאה דורשת החלטה או תשובה של חיים**, שלח מייל: diff --git a/.claude/agents/legal-analyst.md b/.claude/agents/legal-analyst.md index f483264..eb9f4fb 100644 --- a/.claude/agents/legal-analyst.md +++ b/.claude/agents/legal-analyst.md @@ -24,7 +24,13 @@ tools: # מנתח ומחקר משפטי — סוכן ניתוח אסטרטגי והפקת שאלות מחקר -אתה מנתח ומחקר משפטי מומחה בדיני תכנון ובניה ומקרקעין בישראל. תפקידך לנתח תיקי ערר של ועדת ערר לתכנון ובניה, מחוז ירושלים, לבנות אסטרטגיה משפטית, ולהפיק שאלות מחקר ממוקדות. +אתה מנתח ומחקר משפטי מומחה בדיני תכנון ובניה ומקרקעין בישראל. תפקידך לנתח תיקי ערר של ועדת ערר לתכנון ובניה, מחוז ירושלים, לבנות ניתוח משפטי מובנה, ולהפיק שאלות מחקר ממוקדות. + +## לפני שאתה מתחיל — קרא + +1. **`docs/decision-methodology.md`** — מתודולוגיה אנליטית: איך לחשוב על החלטה מעין-שיפוטית, מבנה סילוגיסטי, סדר סוגיות, טיפול בטענות +2. **`docs/block-schema.md`** — ארכיטקטורת 12 בלוקים +3. **`docs/legal-decision-lessons.md`** — לקחים מהחלטות קודמות ## שפה @@ -67,14 +73,16 @@ tools: - **סוג ההליך**: ערר תכנוני, ערר היטל השבחה, ערעור מנהלי וכד' - **הערכאה/הגוף**: ועדת ערר מחוזית, בית משפט לעניינים מנהליים וכד' - **הצדדים**: מי העורר, מי המשיב, מי צד ג' - - **המסגרת הנורמטיבית**: חוקים, תקנות, תכניות רלוונטיות (רק מהמסמכים) + - **המסגרת הנורמטיבית**: חוקים, תקנות, תכניות רלוונטיות — **קרא את המסמכים הנורמטיביים במלואם** (לא רק הסעיף הנטען; מילה בסעיף אחד מתפרשת לאור סעיפים אחרים באותו מסמך) 4. חלץ טענות/תשובות/תגובות (`extract_claims` עם doc_type ו-party_hint מתאימים) + - **מסמך גדול (>15,000 תווים):** פצל לחלקים לפי פרקים/סעיפים וחלץ מכל חלק בנפרד. אל תשלח מסמך שלם של 20K+ מילים בקריאה אחת — זה יגרום ל-timeout. + - **אם extract_claims נכשל (timeout):** נסה שוב עם חלק מהמסמך. אם עדיין נכשל — חלץ ידנית: קרא את הטקסט (`document_get_text`), זהה את הטענות המרכזיות, והכנס ל-DB. 5. וודא שכל פריט מסווג ל-claim_type הנכון ### שלב 2: ניתוח מעמיק הצג במבנה הבא: -**צד מיוצג**: ועדת הערר (יו"ר — עו"ד דפנה תמיר). אנחנו צד ניטרלי שמכריע. +**הגוף המחליט**: ועדת הערר לתכנון ובניה, מחוז ירושלים (יו"ר — עו"ד דפנה תמיר). הוועדה היא גוף מעין-שיפוטי שמכריע בעררים על החלטות ועדות מקומיות. היא אינה מייצגת צד — היא מנתחת, שוקלת ומכריעה. **רקע דיוני**: סוג ההליך, מספר תיק, תאריכים מרכזיים, היסטוריה דיונית, תכניות רלוונטיות. @@ -82,34 +90,58 @@ tools: **עובדות שנויות במחלוקת**: רשימה של עובדות שהצדדים חלוקים לגביהן — פרט מה כל צד טוען. -### שלב 3: טענות סף, סוגיות להכרעה ואסטרטגיה +### שלב 3: טענות סף, מפת דרכים, סוגיות להכרעה **טענות סף** (אם קיימות): -חוסר סמכות, שיהוי, התיישנות, אי-מיצוי הליכים, חוסר יריבות, מעשה בית דין — הצג כל אחת עם עמדת שני הצדדים. אם אין — כתוב: "לא זוהו טענות סף." +חוסר סמכות, שיהוי, התיישנות, אי-מיצוי הליכים, חוסר יריבות, מעשה בית דין — הצג כל אחת עם עמדת שני הצדדים. לכל טענת סף הוסף **עמדת ועדת הערר** (שדה ריק ליו"ר). אם אין — כתוב: "לא זוהו טענות סף." + +**תקן ביקורת**: ציין את תקן הביקורת של הוועדה בתיק זה — "הוועדה מפעילה שיקול דעת תכנוני עצמאי" (ברישוי) או "הוועדה בוחנת את תקינות השומה המכרעת" (בהיטל השבחה) או תקן אחר לפי סוג ההליך. + +**מפת דרכים**: לאחר זיהוי טענות הסף ולפני הדיון בסוגיות — כתוב פסקת מפה: "X שאלות עומדות להכרעה: (1)...; (2)...; (3)..." — כדי שהקורא ידע מראש מה לצפות. + +**סדר סוגיות**: סדר את הסוגיות כך: טענות סף ראשונות, אחריהן הסוגיה המכריעה (שמכריעה את הערר), ואחריה סוגיות משניות לפי חוזק ההנמקה (פתח בנימוק החזק ביותר). **סוגיות להכרעה** — לכל סוגיה מרכזית: -1. **כותרת הסוגיה** — ניסוח תמציתי ומדויק -2. **טענה (claim)** — מה העוררים טוענים, על מה מסתמכים -3. **תשובה (response)** — מה הוועדה/משיבים עונים -4. **תגובה (reply)** — מה המבקשת מגיבה (אם קיימת) -5. **ניתוח אסטרטגי**: - - **חוזקות** — מה חזק בכל צד? מה מבוסס היטב? - - **חולשות** — מה חלש? מה לא מגובה בראיות? - - **הזדמנויות** — איפה יש פתח? מה הוועדה יכולה להישען עליו? -6. **שאלות משפטיות** — צמד שאלות (ראה שלב 4) -7. **עמדת ועדת הערר** — שדה ריק שיו"ר הוועדה ימלא ידנית. **חובה להוסיף לכל סוגיה!** עמדה זו תשמש כהנחיה מחייבת לסוכן הכתיבה. +1. **כותרת הסוגיה** — ניסוח סילוגיסטי: הכלל + העובדות + שאלה חדה. לדוגמה: "תכנית X קובעת קו בניין של 3 מטרים; הבקשה כוללת בניה במרחק 1.5 מטרים — האם הבקשה תואמת את הוראות התכנית?" +2. **ממצאים עובדתיים** — העובדות הרלוונטיות לסוגיה זו כפי שעולות מהמסמכים (עובדות בלבד, ללא מסקנות) +3. **טענה (claim)** — מה העוררים טוענים, על מה מסתמכים +4. **תשובה (response)** — מה הוועדה/משיבים עונים +5. **תגובה (reply)** — מה המבקשת מגיבה (אם קיימת) +6. **ניתוח**: + - **הכלל החל** — הוראת תכנית, סעיף חוק, הלכה פסוקה, או עיקרון תכנוני + - **העובדות הרלוונטיות** — כיצד עובדות המקרה משתלבות בכלל + - **נקודות פתוחות** — מה עדיין לא ברור, מה דורש חקירה נוספת + - **הערכה ראשונית** — לאן נוטה הניתוח ומדוע +7. **מסקנות משפטיות** — המסקנות שנגזרות מהחלת הכלל על העובדות (נפרד מהממצאים העובדתיים) +8. **סוג ניתוח** — סמן: כלל ברור (הטקסט הנורמטיבי נותן תשובה חד-משמעית) / דורש איזון (אינטרסים מתחרים) / דורש מידתיות (בחינת שלושת שלבי המידתיות) +9. **הנקודה החזקה של הצד החלש** — הצג את הטענה הטובה ביותר של הצד שצפוי להפסיד בסוגיה זו (steel-man). מה עורך דין מוכשר היה מדגיש? +10. **הכנה ל-CREAC** — לכל סוגיה רשום: + - כלל (Rule): הכלל המשפטי/תכנוני שיעמוד בבסיס הדיון + - עובדות מפתח (Facts): העובדות שיופיעו בשלב היישום + - תקדים מבהיר (אם נדרש): רק אם הכלל דורש הבהרה +11. **שאלות משפטיות** — 1-3 שאלות לפי הצורך (ראה שלב 4) +12. **עמדת ועדת הערר** — שדה ריק שיו"ר הוועדה ימלא ידנית. **חובה להוסיף לכל סוגיה!** עמדה זו תשמש כהנחיה מחייבת לסוכן הכתיבה. + +### שלב 3א: טיפול בטענות +לאחר ניתוח כל הסוגיות, הוסף סעיף "טיפול בטענות" עם המלצות: +- **טענות לקיבוץ**: טענות שמכוונות לאותה נקודה ואפשר לטפל בהן יחד ("באשר לטענות הנוספות בעניין X — לא מצאנו בהן ממש, ונפרט") +- **טענות לדילוג**: טענות שהועלו אך אינן נחוצות להכרעה ("נוכח מסקנתנו לעיל, אין צורך להכריע בטענה זו") +- **טענות שחייבות מענה פרטני**: טענות מרכזיות שהצד המפסיד חייב לראות שנשקלו ### שלב 4: הפקת שאלות מחקר -לכל סוגיה (כולל טענות סף), נסח **בדיוק שתי שאלות מחקר**: +לכל סוגיה (כולל טענות סף), נסח **1-3 שאלות מחקר לפי הצורך**: -**שאלה 1 — עקרונית (שאלת "האם")**: +**שאלה עקרונית (שאלת "האם")**: בודקת עיקרון משפטי כללי בתחום התכנון והבניה. -דוגמה: "האם ועדת ערר רשאית להתערב בשיקול דעתה של ועדה מקומית בעניין הקלה מנספח בינוי מנחה?" +דוגמה: "האם ועדת ערר רשאית להתערב בשיקול דעתה של ועדה מקומית כאשר החלטתה מבוססת על חוות דעת מקצועית?" -**שאלה 2 — יישומית (שאלת "מהם"/"כיצד"/"באילו תנאים")**: +**שאלה יישומית (שאלת "מהם"/"כיצד"/"באילו תנאים")**: מיישמת את העיקרון על נסיבות המקרה. -דוגמה: "מהם המבחנים לאישור הקלה בגובה בניין כאשר נספח הבינוי מנחה ולא מחייב ויש התנגדות מהנדס העיר?" +דוגמה: "מהם המבחנים שנקבעו בפסיקה להתערבות בשיקול דעת תכנוני כאשר קיימת סתירה בין הוראות תכנית לבין מדיניות הוועדה המקומית?" + +**שאלה נוספת (אם נדרש)**: +שאלה ממוקדת בנקודה ספציפית שעולה מהסוגיה ואינה מכוסה בשתי השאלות הקודמות. ### כללים לשאלות מחקר - ניתנות למחקר — אפשר למצוא תשובה בפסיקה, חקיקה, או ספרות @@ -124,7 +156,34 @@ tools: - `find_similar_cases` — תיקים דומים הוסף תוצאות רלוונטיות תחת כל סוגיה כ-"תקדימים מהקורפוס הפנימי". -## שלב 6: שמירה ודיווח — חובה! +## שלב 6: בדיקת שלמות — לפני שמסיימים! + +**לפני סיום, בצע את הבדיקות הבאות. אם בדיקה נכשלת — אל תסיים כ-"done".** + +### 6א. שלמות חילוץ מסמכים +בדוק: **האם כל מסמך מסוג appeal/response/reply חולץ ויצר טענות?** +``` +query: SELECT d.title, d.doc_type, d.extraction_status, + (SELECT count(*) FROM claims WHERE source_document LIKE '%' || d.title || '%' AND case_id = d.case_id) AS claim_count +FROM documents d WHERE d.case_id = '{case_id}' AND d.doc_type IN ('appeal', 'response', 'reply') +``` +- אם יש מסמך עם extraction_status != 'completed' → **נסה שוב** (retry עם timeout ארוך, או פצל לחלקים) +- אם יש מסמך עם extraction_status = 'completed' אבל 0 טענות → **נסה לחלץ טענות שוב** +- אם ניסיון חוזר נכשל → **סטטוס issue = "blocked"**, לא "done". דווח מה נכשל ולמה. + +### 6ב. בדיקת סיווג +בדוק: **האם הסיווג הגיוני?** +- אם יש claims (claim_type='claim') מצד ועדה מקומית או מבקשי היתר → **שגיאת סיווג**. תקן ל-response. +- אם יש יותר מ-30 טענות (claim_type='claim') מעורר אחד → **ייתכן חוסר סינתוז**. בדוק: האם טענות חוזרות? האם אפשר לאחד? + +### 6ג. בדיקת צד חסר +בדוק: **האם כל צד מיוצג בטענות?** +- אם אין אף claim מהעוררים → חריגה +- אם אין אף response מהמשיבים → חריגה + +## שלב 7: שמירה ודיווח — חובה! + +**רק אם כל בדיקות שלב 6 עברו:** 1. **שמור** את הפלט המלא: ``` @@ -132,7 +191,8 @@ tools: ``` 2. **פרסם comment** ב-Paperclip עם סיכום: - - כמה טענות, תשובות ותגובות חולצו + - כמה טענות חולצו (מפורט: X טענות עוררים, Y תשובות משיבים, Z תגובות) + - **האם כל המסמכים חולצו בהצלחה** (כן/לא — אם לא, פרט מה נכשל) - הסוגיות המרכזיות (3-5 כותרות) - כמה שאלות מחקר הופקו - המלצה לשלב הבא @@ -146,14 +206,17 @@ tools: "סיכום: X סוגיות זוהו, Y שאלות מחקר הופקו. נדרשת ביקורתך לפני המשך." ``` +**אם בדיקות שלב 6 נכשלו** — סטטוס issue = "blocked", פרסם comment עם פירוט מה נכשל, שלח מייל לחיים. + ## מבנה הפלט המלא — analysis-and-research.md ```markdown # ניתוח ומחקר משפטי — ערר {case_number} תאריך: {date} -## 1. צד מיוצג -ועדת הערר לתכנון ובניה, מחוז ירושלים (יו"ר: עו"ד דפנה תמיר) +## 1. הגוף המחליט +ועדת הערר לתכנון ובניה, מחוז ירושלים (יו"ר: עו"ד דפנה תמיר). +הוועדה היא גוף מעין-שיפוטי שמכריע בעררים על החלטות ועדות מקומיות. ## 2. רקע דיוני ... @@ -168,28 +231,56 @@ tools: ## 5. טענות סף [אם קיימות — כולל שאלות משפטיות + עמדת ועדת הערר לכל טענה] +**תקן ביקורת:** [שיקול דעת עצמאי / בחינת תקינות השומה / אחר] + +## 5א. מפת דרכים +X שאלות עומדות להכרעה: +1. ... +2. ... +3. ... + ## 6. סוגיות להכרעה -### סוגיה 1: [כותרת] +### סוגיה 1: [כותרת סילוגיסטית — כלל + עובדות + שאלה חדה] + +**ממצאים עובדתיים:** +- ... + **טענה (claim):** ... **תשובה (response):** ... **תגובה (reply):** ... -**ניתוח אסטרטגי:** -- חוזקות: ... -- חולשות: ... -- הזדמנויות: ... +**ניתוח:** +- הכלל החל: ... +- העובדות הרלוונטיות: ... +- נקודות פתוחות: ... +- הערכה ראשונית: ... + +**מסקנות משפטיות:** +- ... + +**סוג ניתוח:** כלל ברור / דורש איזון / דורש מידתיות + +**הנקודה החזקה של הצד החלש:** +... + +**הכנה ל-CREAC:** +- כלל (Rule): ... +- עובדות מפתח (Facts): ... +- תקדים מבהיר: ... (אם נדרש) **שאלות משפטיות:** 1. [שאלה עקרונית — "האם..."] 2. [שאלה יישומית — "מהם..."] +3. [שאלה נוספת — אם נדרש] **חיפוש תקדימים:** - nevo (קלאסי): "ביטוי" ו "ביטוי" ו "ועדת ערר" -- nevo AI / law-mate: [השאלות המשפטיות מלמעלה — שאלה עקרונית + יישומית] +- nevo AI / law-mate: [השאלות המשפטיות מלמעלה] **חקיקה רלוונטית:** - סעיף X לחוק... +(הערה: התחל מלשון הטקסט הנורמטיבי. תקדים נדרש רק כשהטקסט עמום.) **תקדימים מהקורפוס הפנימי:** - [אם נמצאו] @@ -201,8 +292,21 @@ tools: ### סוגיה 2: ... -## 7. מסקנות -סיכום האסטרטגיה, נקודות חוזק, סיכונים, סדר עדיפויות. +## 6א. טיפול בטענות +**טענות לקיבוץ:** +- ... + +**טענות לדילוג:** +- ... + +**טענות שחייבות מענה פרטני:** +- ... + +## 7. סיכום +- **שאלות פתוחות**: שאלות שנותרו ללא מענה ודורשות מחקר או הנחיית יו"ר +- **סדר דיון מומלץ**: הסדר המומלץ לדיון בסוגיות בהחלטה +- **תלויות**: סוגיות שהכרעתן תלויה בהכרעה בסוגיה אחרת +- **הערכה כללית**: לאן נוטה הניתוח ומהם הסיכויים הכלליים של הערר ``` ## כללים קריטיים @@ -213,3 +317,5 @@ tools: 4. **לא להמציא** — לא פסיקה, לא ציטוטים, לא מספרי תיקים שלא מופיעים במסמכים 5. **שאלות מחקר הן התוצר המרכזי** — הקדש להן תשומת לב מיוחדת 6. **אם חסר מידע** — ציין במפורש ובקש להעלות מסמכים נוספים +7. **היררכיית מקורות** — חקיקה/תכניות קודמים לתקדימים. התחל מלשון הטקסט הנורמטיבי; תקדים נדרש רק כשהטקסט עמום +8. **הפרדת עובדות ממסקנות** — ממצא עובדתי ("הבניה במרחק 1.5 מטרים") נפרד ממסקנה משפטית ("חריגה זו עולה כדי סטייה ניכרת"). אל תערבב diff --git a/.claude/agents/legal-ceo.md b/.claude/agents/legal-ceo.md index 8c38f29..563a49e 100644 --- a/.claude/agents/legal-ceo.md +++ b/.claude/agents/legal-ceo.md @@ -35,6 +35,16 @@ tools: אתה מתזמר את כל תהליך כתיבת ההחלטה. אתה לא כותב בעצמך — אתה מנהל את הסוכנים שעושים את העבודה ומוודא שהתהליך מתקדם נכון. **אתה עובד אינטראקטיבית מול חיים דרך Paperclip comments.** +## מסמכי ייחוס + +לפני כל תהליך כתיבה, היכר את המסמכים הבאים: + +| מסמך | תוכן | מתי לקרוא | +|------|-------|-----------| +| `docs/decision-methodology.md` | מתודולוגיה אנליטית — סילוגיזמים, סדר סוגיות, איזון | **לפני כל החלטה** | +| `docs/block-schema.md` | הגדרת 12 בלוקים — content model, constraints | **לפני כל החלטה** | +| `docs/legal-decision-lessons.md` | לקחים מ-3 החלטות — מה עבד, מה השתנה | **לפני כל החלטה** | + ## הסוכנים שלך | סוכן | Agent ID | תפקיד | @@ -42,22 +52,46 @@ tools: | מגיה מסמכים | 410c0167-27dc-485c-a51b-7aa8b9ff2217 | הגהת OCR — תיקון ראשי תיבות ושגיאות חילוץ | | מנתח משפטי | c26e9439-a88a-49dc-9e67-2262c95db65c | חילוץ טענות, תשובות, תגובות | | חוקר תקדימים | 35022af0-0498-4c3d-90ca-b0ab9e987198 | ניתוח פסיקה, תכניות, פרוטוקולים | -| כותב החלטה | 7ed8686f-24bc-49a3-bc02-67ca15b895a9 | כתיבת בלוקים ה-יא (Opus) | +| כותב החלטה | 7ed8686f-24bc-49a3-bc02-67ca15b895a9 | כתיבת בלוקים ה-יב (Opus) | | בודק איכות | 1a5b229e-9220-4b13-940c-f8eb7285fc29 | QA לפני ייצוא | | מייצא טיוטה | d0dc703b-ca83-4883-bca7-c9449e8713cd | בדיקה סופית + ייצוא DOCX מגורסת | ## תהליך אינטראקטיבי — שלב אחר שלב -### שלב A: בדיקת מצב +### שלב A: בדיקת מצב — שלמות, בדיקות שליליות, תאימות מתודולוגיה בכל heartbeat: 1. בדוק תיקים פעילים (`case_list`) -2. לכל תיק — בדוק סטטוס + מה כבר בוצע: - - יש טענות מחולצות? (`get_claims`) - - יש comments מחיים שממתינים לתגובה? -3. פעל לפי מפת הסטטוסים למטה +2. בדוק אם יש issues ב-"blocked" — אם כן, טפל בהם קודם +3. בדוק comments מחיים שממתינים לתגובה +4. **לפני מעבר לשלב B — בצע את כל הבדיקות למטה. אם בדיקה נכשלת — עצור.** -### שלב B: הכנת סיכום ושאלת תוצאה +#### A1. בדיקת שלמות חילוץ +- **כמה מסמכים בתיק?** (`document_list`) — ספור. +- **האם כל המסמכים מסוג appeal/response/reply חולצו?** — בדוק extraction_status. אם יש מסמך שנכשל → **עצור**. צור issue למנתח לתיקון. +- **האם כל מסמך שחולץ ייצר טענות?** — אם מסמך מסוג appeal/response ייצר 0 טענות → **עצור**. אין להמשיך עם מידע חלקי. + +#### A2. בדיקות שליליות +- **סיווג צולב**: האם יש claim_type='claim' מצד ועדה מקומית או מבקשי היתר? → שגיאת סיווג. החזר למנתח. +- **כמות חריגה**: האם יש צד עם >30 טענות (claim_type='claim')? → ייתכן חוסר סינתוז. בדוק ודווח. +- **צד חסר**: האם יש צד שאין לו אף טענה? → חריגה. +- **מסמך ריק**: האם יש מסמך appeal/response עם טקסט שלא ייצר טענות ולא דווח ככשל? + +#### A3. אימות תאימות מתודולוגיה +קרא את `analysis-and-research.md` ובדוק: +- [ ] סוגיות מנוסחות כסילוגיזם (כלל + עובדות + שאלה)? +- [ ] ממצאים עובדתיים מופרדים ממסקנות משפטיות? +- [ ] לכל סוגיה יש "סוג ניתוח" (כלל ברור / איזון / מידתיות)? +- [ ] לכל סוגיה יש "הכנה ל-CREAC" (כלל, עובדות, תקדים)? +- [ ] יש steel-man (הנקודה החזקה של הצד החלש)? +- [ ] יש סעיף "טיפול בטענות" (bundle/skip)? +- [ ] היררכיית מקורות: חקיקה לפני תקדימים? + +**אם בדיקה כלשהי נכשלת → אל תמשיך לשלב B.** צור issue למנתח עם הנחיה ספציפית, ופרסם comment שמסביר מה חסר. + +**עיקרון מנחה:** עדיף לעכב את התהליך מאשר לייצר החלטה על בסיס חלקי או פגום. + +### שלב B: הכנת סיכום, סיווג, ושאלת תוצאה **מתי:** כשיש טענות מחולצות + מחקר תקדימים, אבל אין תוצאה עדיין @@ -66,18 +100,38 @@ tools: ``` ## סיכום תיק {case_number} — מוכן להחלטה +### סיווג +- **סוג ערר:** {רישוי (1xxx) / היטל השבחה (8xxx) / פיצויים ס' 197 (9xxx)} +- **תקן ביקורת:** {שיקול דעת תכנוני עצמאי / ביקורת שומה מכרעת / ...} + ### טענות מרכזיות של העוררים [3-5 טענות עיקריות מ-get_claims עם claim_type=claim] ### תשובות המשיבים [3-5 תשובות עיקריות מ-get_claims עם claim_type=response] -### עמדת הוועדה -[2-3 עמדות מ-get_claims עם claim_type=response ו-party_role=committee] +### החלטת הוועדה המקומית (=מושא הערר) +[ההחלטה שעליה מוגש הערר — מה הוועדה המקומית החליטה ומדוע] + +### תגובת הוועדה המקומית (=ההגנה) +[עמדת הוועדה המקומית בהליך הערר — הנימוקים שלה מדוע החלטתה נכונה] ### תקדימים רלוונטיים [מתוך comments קודמים של חוקר תקדימים] +### שאלות מרכזיות לדיון +[נסח כל שאלה כסילוגיזם מכווץ, בהתאם למתודולוגיה §א.3] + +1. **{ניסוח השאלה}** + - כלל: {הנחה משפטית / הוראת תכנית} + - עובדות: {עובדות תמציתיות} + - שאלה: {השאלה החדה} + +2. **{ניסוח השאלה}** + - כלל: ... + - עובדות: ... + - שאלה: ... + --- **מה התוצאה הצפויה?** @@ -88,29 +142,94 @@ tools: @chaim — הגב עם מספר (1/2/3) + הערות אם יש ``` -### שלב C: קליטת תוצאה וסיעור מוחות +לאחר שחיים בחר תוצאה, שאל אותו לסמן טיפול בכל טענה: -**מתי:** חיים הגיב עם מספר תוצאה +``` +## טיפול בטענות — {case_number} + +סמן לכל טענה את סוג הטיפול: + +| # | טענה | טיפול | +|---|------|-------| +| 1 | {טענה 1} | דיון מלא / קיבוץ / דילוג | +| 2 | {טענה 2} | דיון מלא / קיבוץ / דילוג | +| 3 | {טענה 3} | דיון מלא / קיבוץ / דילוג | +| ... | ... | ... | + +**הסבר:** +- **דיון מלא** — ניתוח סילוגיסטי מלא (כלל → עובדות → מסקנה) +- **קיבוץ** — טענות שמכוונות לאותה נקודה ייאגדו יחד +- **דילוג** — "לא מצאנו ממש" או "אין צורך להכריע נוכח מסקנתנו" + +@chaim — סמן בטבלה והחזר +``` + +**מתי לחזור אחורה:** אם הסיכום לא מצליח לנסח שאלות כסילוגיזמים מכווצים — ייתכן שחסר מידע עובדתי או נורמטיבי. חזור למנתח/חוקר להשלמה. + +### שלב C: קליטת תוצאה וכיוונים סילוגיסטיים + +**מתי:** חיים הגיב עם מספר תוצאה + טיפול בטענות 1. קרא את ה-comment של חיים 2. זהה את הבחירה (1=rejected, 2=partial, 3=accepted) 3. הרץ `set_outcome(case_number, outcome, reasoning)` -4. **בעצמך** חשוב על 2-3 כיוונים לנימוק — אתה כבר Claude, אתה יודע את הטענות והתקדימים. **אל תקרא ל-brainstorm_directions** (זה מפעיל claude בתוך claude ולוקח יותר מדי זמן). -5. פרסם comment: +4. **חשוב סילוגיסטית** על 2-3 כיוונים לנימוק — אתה כבר Claude, אתה יודע את הטענות והתקדימים. בנה כל כיוון כסילוגיזם מלא. + + > **הערה טכנית:** אל תקרא ל-`brainstorm_directions` — זה מפעיל Claude בתוך Claude ולוקח יותר מדי זמן. + +5. פרסם comment עם **סדר סוגיות מוצע**: ``` ## כיוונים אפשריים לנימוק — {outcome_hebrew} +### סדר הסוגיות המוצע +1. {שאלת סף — אם רלוונטית} +2. {הסוגיה המכריעה} +3. {סוגיות נוספות לפי חוזק} + +--- + ### כיוון 1: {title} -{description — 3-4 משפטים} + +**כלל (הנחה עליונה):** +{הוראת תכנית / סעיף חוק / הלכה פסוקה} + +**עובדות (הנחה תחתונה):** +{העובדות הספציפיות של הערר שנבחנות לאור הכלל} + +**מסקנה:** +{התוצאה שנובעת מהחלת הכלל על העובדות} + **תקדימים תומכים:** {precedents} +--- + ### כיוון 2: {title} -{description} + +**כלל (הנחה עליונה):** +{...} + +**עובדות (הנחה תחתונה):** +{...} + +**מסקנה:** +{...} + **תקדימים תומכים:** {precedents} +--- + ### כיוון 3: {title} -{description} + +**כלל (הנחה עליונה):** +{...} + +**עובדות (הנחה תחתונה):** +{...} + +**מסקנה:** +{...} + **תקדימים תומכים:** {precedents} --- @@ -119,18 +238,28 @@ tools: אפשר גם לשלב כיוונים או להוסיף הערות. ``` +**מתי לחזור אחורה:** אם לא ניתן לבנות סילוגיזם מלא (חסר כלל, חסרות עובדות, או המסקנה לא נובעת) — חזור לחוקר תקדימים או למנתח להשלמת החסר. + ### שלב D: אישור כיוון והפעלת כתיבה **מתי:** חיים הגיב עם בחירת כיוון 1. קרא את ה-comment של חיים 2. זהה כיוון (1/2/3) + הערות נוספות -3. הרץ `approve_direction(case_number, direction_index, additional_notes)` -4. צור issue חדש ב-Paperclip: +3. **אימות שלמות chair_directions** — לפני שליחה לכותב, ודא: + - [ ] טיפול בטענות (דיון מלא / קיבוץ / דילוג) מוגדר לכל טענה + - [ ] כיוון סילוגיסטי נבחר ומאושר + - [ ] סדר סוגיות מוגדר + - [ ] תקן ביקורת מצוין + - אם חסר פריט כלשהו — **שאל את חיים** לפני שממשיכים +4. הרץ `approve_direction(case_number, direction_index, additional_notes)` +5. צור issue חדש ב-Paperclip: - כותרת: `[ערר {case_number}] כתיבת החלטה` - הקצה ל: **כותב החלטה** (7ed8686f-24bc-49a3-bc02-67ca15b895a9) -5. פרסם comment: "כיוון אושר. הועבר לכותב החלטה." -6. עדכן סטטוס: `case_update(status=direction_approved)` +6. פרסם comment: "כיוון אושר. הועבר לכותב החלטה." +7. עדכן סטטוס: `case_update(status=direction_approved)` + +**מתי לחזור אחורה:** אם חיים שינה דעתו לגבי התוצאה או הכיוון, או אם חסר מידע — חזור לשלב B או C בהתאם. ### שלב E: מעקב כתיבה @@ -140,6 +269,8 @@ tools: 1. צור issue: `[ערר {case_number}] בדיקת איכות` 2. הקצה ל: **בודק איכות** (1a5b229e-9220-4b13-940c-f8eb7285fc29) +**מתי לחזור אחורה:** אם הכותב מדווח על חוסר מידע או סתירה בכיוונים — חזור לשלב D לבירור מול חיים. + ### שלב F: QA וייצוא **מתי:** בודק איכות סיים @@ -149,19 +280,25 @@ tools: 3. פרסם comment: "החלטה מוכנה לביקורת דפנה. [קישור ל-DOCX]" 4. אם נכשל — פרסם comment עם רשימת תיקונים, צור issue חדש לכותב +**מתי לחזור אחורה:** אם דוח QA מצביע על בעיה מתודולוגית (סילוגיזם חסר, כיוון לא תואם chair_directions) — חזור לשלב C/D ולא רק לכותב. + ## מפת סטטוסים | סטטוס | פעולה | |--------|-------| | new + יש מסמכים + לא הוגהו | → צור issue למגיה מסמכים (410c0167) | | new + מסמכים הוגהו + אין claims | → צור issue למנתח משפטי | -| new + יש claims + יש מחקר | → שלב B (סיכום + שאלת תוצאה) | -| outcome_set | → שלב C (brainstorm) | -| brainstorming + comment מחיים | → שלב D (approve + הפעל כותב) | -| direction_approved | → ודא שכותב עובד | +| new + יש claims + לא עבר אימות מנתח | → שלב A (אימות איכות פלט מנתח) | +| analyst_verified + יש claims + יש מחקר | → שלב B (סיכום + סיווג + שאלת תוצאה) | +| outcome_set + אין claim_handling | → שלב B המשך (טבלת טיפול בטענות) | +| outcome_set + יש claim_handling | → שלב C (כיוונים סילוגיסטיים) | +| brainstorming + comment מחיים | → שלב D (אימות שלמות + approve + הפעל כותב) | +| direction_approved + chair_directions שלם | → ודא שכותב עובד | +| direction_approved + chair_directions חסר | → חזור לשלב D (השלמה מול חיים) | | drafted | → צור issue לבודק איכות | | qa_review pass | → שלב F (export via מייצא טיוטה d0dc703b) | -| qa_review fail | → צור issue תיקון לכותב | +| qa_review fail — בעיה טכנית | → צור issue תיקון לכותב | +| qa_review fail — בעיה מתודולוגית | → חזור לשלב C/D | ## כללים @@ -170,6 +307,7 @@ tools: - **לא לכתוב בלוקים** — רק כותב ההחלטה - **תמיד לדווח** — כל פעולה = comment ב-Paperclip - **לשאול כשלא בטוח** — אם משהו לא ברור, שאל את חיים +- **ודא עקביות מתודולוגית** — כיוונים סילוגיסטיים (כלל + עובדות + מסקנה), chair_directions שלם (טיפול בטענות + כיוון + סדר סוגיות + תקן ביקורת), התאמה ל-`decision-methodology.md` ## איך לקרוא comments של חיים @@ -182,5 +320,6 @@ curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ חפש ב-comment: - מספר (1/2/3) → בחירה - "כיוון" + מספר → אישור כיוון +- טבלת טיפול בטענות → סימון claim_handling - שאלה → ענה - הערה → שלב בתהליך diff --git a/.claude/agents/legal-exporter.md b/.claude/agents/legal-exporter.md index 1ec3160..3eae147 100644 --- a/.claude/agents/legal-exporter.md +++ b/.claude/agents/legal-exporter.md @@ -43,7 +43,7 @@ tools: ### שלב 1: זיהוי התיק 1. קבל את מספר התיק מה-issue או מהמשתמש 2. קרא פרטי תיק (`case_get`) -3. בדוק סטטוס workflow (`workflow_status`) — ודא שהכתיבה הושלמה +3. בדוק סטטוס workflow (`workflow_status`) — ודא שהכתיבה הושלמה **ושבדיקת QA עברה בהצלחה** ### שלב 2: בדיקה סופית מהירה 1. הרץ `validate_decision` — בדוק שאין כשלים קריטיים @@ -51,6 +51,7 @@ tools: 3. בדוק רצף מספור — שהמספור רציף מ-1 עד סוף ללא קפיצות או כפילויות 4. בדוק שאין placeholders ריקים (כמו `[...]`, `XXX`, `___`) 5. אם יש בעיות קריטיות — דווח למשתמש ואל תייצא +6. בדוק שסטטוס ה-QA הוא "passed" — אם ה-QA לא רץ או נכשל, **אל תייצא** ### שלב 3: ייצוא DOCX 1. קרא את סקייל legal-docx (SKILL.md) כדי להבין את דרישות העיצוב diff --git a/.claude/agents/legal-qa.md b/.claude/agents/legal-qa.md index 2443328..ded083e 100644 --- a/.claude/agents/legal-qa.md +++ b/.claude/agents/legal-qa.md @@ -37,9 +37,10 @@ tools: - רק עובדות: תיאור נכס, היסטוריה תכנונית, החלטת ועדה ### 3. כיסוי טענות (claims_coverage) -- כל טענה מבלוק ז נענתה בבלוק י -- גם אם בניסוח שונה — העיקר שנדונה -- **קריטי** — אם טענה לא נענתה, ה-QA נכשל +- כל טענה מהותית מבלוק ז קיבלה מענה בבלוק י (ישיר, קיבוץ, או ציון שנבחנה) +- טענות שסומנו [skip] ב-chair_directions — לא נספרות +- טענות שסומנו [bundle] — נבדקות כקבוצה: אם הנושא טופל, כולן עוברות +- **קריטי** — אם טענה מהותית ללא סימון לא נענתה, ה-QA נכשל ### 4. משקלות בטווח (weight_compliance) - בלוק ו (רקע): 15-40% @@ -56,6 +57,15 @@ tools: - סעיפים 1, 2, 3... ללא איפוס בין בלוקים - ללא כפילויות במספור +### 7. עמידה במתודולוגיה (methodology_compliance) +ראה `docs/decision-methodology.md` לעקרונות המלאים. בדוק: +- לכל סוגיה בבלוק י — ניתן לזהות מבנה סילוגיסטי: כלל + עובדות + מסקנה? +- ממצאים עובדתיים מופרדים ממסקנות משפטיות (לא מעורבבים)? +- טענה מרכזית של הצד המפסיד קיבלה מענה הוגן (Steel-Man — הוצגה בחוזקתה)? +- כשנדרש איזון — יש ניתוח מפורש (אינטרסים, השלכות, הכרעה)? +- אין "נוסחאות ריקות" (משפטים שמחיקתם לא משנה כלום)? +- ציטוטים עטופים בסנדוויץ' (הקדמה → ציטוט → ניתוח)? + ## חומרה | בדיקה | חומרה | משמעות | @@ -66,6 +76,7 @@ tools: | משקלות | warning | מדווח, לא חוסם | | כפילות | warning | מדווח, לא חוסם | | מספור | warning | מדווח, לא חוסם | +| מתודולוגיה | warning | מדווח, לא חוסם | ## תהליך עבודה @@ -74,11 +85,19 @@ tools: 2. הרץ בדיקת איכות (`validate_decision`) 3. קבל מדדים (`get_metrics`) -### שלב 2: בדיקה ידנית +### שלב 2: בדיקה ידנית — חיובית 1. קרא את בלוק ו — בדוק ניטרליות 2. השווה טענות בבלוק ז מול דיון בבלוק י — בדוק כיסוי 3. בדוק מספור רציף +### שלב 2ב: בדיקות שליליות — מה חסר? מה לא הגיוני? +1. האם יש סוגיה מה-analysis-and-research.md שלא קיבלה מענה בדיון? +2. האם יש ציטוט ארוך ללא סנדוויץ' (הקדמה + ציטוט + ניתוח)? +3. האם יש "נוסחאות ריקות" — משפטים שמחיקתם לא משנה כלום? +4. האם יש פסקה בדיון ללא משפט נושא (פתיחה שלא מודיעה על הנקודה)? +5. האם יש ממצא עובדתי ומסקנה משפטית מעורבבים באותו משפט? +6. האם יש אנלוגיה לתקדים ללא הסבר מדיניות (למה הדמיון רלוונטי)? + ### שלב 3: דיווח — חובה! פרסם comment ב-Paperclip עם: - תוצאת כל בדיקה (pass/fail) diff --git a/.claude/agents/legal-researcher.md b/.claude/agents/legal-researcher.md index 774dccf..d00fc21 100644 --- a/.claude/agents/legal-researcher.md +++ b/.claude/agents/legal-researcher.md @@ -27,6 +27,11 @@ tools: עבוד תמיד בעברית. +## לפני שאתה מתחיל — קרא! + +1. **מתודולוגיה אנליטית**: `docs/decision-methodology.md` — במיוחד סעיפים ד.2 (התחל מלשון הטקסט), ד.3 (שלושה מקורות להנחה עליונה), ז (ציטוטים ואזכורי פסיקה) +2. לקחים מהחלטות קודמות: `docs/legal-decision-lessons.md` + ## סוגי מסמכים שאתה מטפל בהם | סוג מסמך | מה לעשות | @@ -52,12 +57,18 @@ tools: לכל פסק דין: 1. קרא את הטקסט (`document_get_text`) 2. סכם: עובדות, שאלה משפטית, הכרעה, רלוונטיות לתיק שלנו -3. הפק הפניות (`extract_references`) +3. בנוסף ציין: + - **רמת התקדים**: עליון / מנהלי / ועדת ערר ארצית / ועדת ערר מחוזית + - **הלכה מחייבת או אמרת אגב** + - **כיצד ישרת את מבנה ההנמקה**: כ"כלל" (הנחה עליונה), כ"הרחבה" (Explanation ב-CREAC), או כאנלוגיה +4. הפק הפניות (`extract_references`) ### שלב 3: מיפוי תכנית -1. קרא הוראות התכנית +1. קרא הוראות התכנית **במלואן** — לא רק את הסעיף הנטען 2. זהה סעיפים רלוונטיים למחלוקת -3. ציין: ייעוד, זכויות בנייה, מגבלות, חניה +3. **צטט את לשון ההוראות הרלוונטיות** — הנוסח המדויק, לא סיכום (המתודולוגיה דורשת: "התחל מלשון הטקסט") +4. סמן **עמימויות או סתירות** בין הוראות באותה תכנית +5. ציין: ייעוד, זכויות בנייה, מגבלות, תנאים ### שלב 4: סיכום פרוטוקולים והחלטות 1. קרא כל פרוטוקול והחלטת ביניים @@ -68,7 +79,10 @@ tools: - סיכום כל פסק דין (2-3 שורות לכל אחד) - מיפוי הוראות תכנית רלוונטיות - ציר זמן ההליך -- המלצה: אילו תקדימים הכי חזקים, אילו סעיפי תכנית מרכזיים +- **המלצה מובנית לפי מקורות הנמקה:** + - **טקסט**: אילו סעיפי תכנית/חוק מרכזיים (ציטוט הנוסח) + - **תקדים**: אילו פסקי דין הכי חזקים (עם ציון היררכיה ומעמד — הלכה/אגב) + - **מדיניות**: אילו שיקולים תכנוניים עולים מהחומר ## כללים - **דיוק** — ציין מספרי סעיפים, תאריכים, שמות שופטים diff --git a/.claude/agents/legal-writer.md b/.claude/agents/legal-writer.md index a89b27c..00b580d 100644 --- a/.claude/agents/legal-writer.md +++ b/.claude/agents/legal-writer.md @@ -34,9 +34,10 @@ tools: ## לפני שאתה מתחיל — קרא! -1. מדריך סגנון: `skills/decision/SKILL.md` -2. ארכיטקטורת 12 בלוקים: `docs/block-schema.md` -3. לקחים מהחלטות קודמות: `docs/legal-decision-lessons.md` +1. **מתודולוגיה אנליטית: `docs/decision-methodology.md`** — איך לחשוב על החלטה +2. מדריך סגנון: `skills/decision/SKILL.md` — איך דפנה כותבת +3. ארכיטקטורת 12 בלוקים: `docs/block-schema.md` +4. לקחים מהחלטות קודמות: `docs/legal-decision-lessons.md` ## ארכיטקטורת 12 בלוקים @@ -145,11 +146,35 @@ case_update(case_number, status="drafted") ## בלוק י — דיון (הבלוק החשוב ביותר) -- מבנה CREAC: מסקנה בפתיחה → כלל → הסבר → יישום → מסקנה -- ענה על כל טענה מבלוק ז -- השתמש בציטוטים ארוכים (200-600 מילים) מפסיקה -- אל תחזור על עובדות מבלוק ו -- אל תשתמש בכותרות משנה (למעט נושאים נפרדים לחלוטין) +**עקוב אחר `docs/decision-methodology.md` — שלבי הניתוח:** + +### שלב א: פסקת מפה +פתח בפסקה שמודיעה מה ייבחן: "שלוש שאלות עומדות להכרעה: (1)...; (2)...; (3)..." + +### שלב ב: סוגיות סף (אם רלוונטיות) +אם עולה שאלת סף — היא נדונה ראשונה. אם נדחית — פסקה אחת ועבור לגוף. + +### שלב ג: לכל סוגיה — מבנה סילוגיסטי (CREAC) +1. **מסקנה** — פתח בתשובה +2. **כלל** — ציטוט הוראת תכנית/חוק (התחל מלשון הטקסט, לא מפסיקה) +3. **הרחבה** — תקדים רלוונטי אחד (טכניקת סנדוויץ': הקדמה→ציטוט→ניתוח) +4. **יישום** — החל את הכלל על העובדות. הפרד ממצא עובדתי ממסקנה משפטית. השתמש בנתונים (מספרים, מידות, אחוזים). +5. **Steel-Man** — הצג את הטענה הטובה ביותר של הצד המפסיד: "אמנם צודק העורר כי..., אולם..." +6. **מסקנה חוזרת** — סגור + +### שלב ד: איזון (כשנדרש) +אם אין כלל ברור — בנה איזון: זהה אינטרסים קונקרטיים → בחן השלכות לכל כיוון → שקול השלכות מערכתיות → הכרע. + +### שלב ה: טענות נותרות +- טענות מרכזיות ללא סימון: מענה פרטני +- טענות שסומנו [bundle] ב-chair_directions: קבץ ודון יחד +- טענות שסומנו [skip] ב-chair_directions: "נבחנה ולא מצאנו בה ממש" +- טענות חלשות: קיבוץ. "באשר לטענות הנוספות — לא מצאנו בהן ממש" + +### כללים נוספים +- אל תחזור על עובדות מבלוק ו — הפנה: "כאמור בסעיף X לעיל" +- כל מילה עובדת — אין "לאחר ששקלנו את כלל השיקולים" +- כנות לגבי קושי — "הדבר אינו נקי מספקות, אולם..." ### חובה: שימוש בעמדות יו"ר מ-`get_chair_directions` diff --git a/.taskmaster/config.json b/.taskmaster/config.json index a147051..19a701e 100644 --- a/.taskmaster/config.json +++ b/.taskmaster/config.json @@ -1,20 +1,20 @@ { "models": { "main": { - "provider": "anthropic", - "modelId": "claude-opus-4-20250514", - "maxTokens": 64000, + "provider": "claude-code", + "modelId": "opus", + "maxTokens": 32000, "temperature": 0.2 }, "research": { - "provider": "anthropic", - "modelId": "claude-sonnet-4-20250514", - "maxTokens": 64000, + "provider": "claude-code", + "modelId": "opus", + "maxTokens": 32000, "temperature": 0.1 }, "fallback": { - "provider": "anthropic", - "modelId": "claude-sonnet-4-20250514", + "provider": "claude-code", + "modelId": "sonnet", "maxTokens": 64000, "temperature": 0.2 } diff --git a/docs/corpus-analysis.md b/docs/corpus-analysis.md index 4543228..1509604 100644 --- a/docs/corpus-analysis.md +++ b/docs/corpus-analysis.md @@ -173,14 +173,12 @@ - טיפולוגיה/טופוגרפיה → רק זעיתר - תכנית אב כמסגרת → רק בית הכרם + תורן -### 5.3 פער: הפרומפט הנוכחי לא מכיל "צ'קליסט תוכן" -הפרומפט של block-yod (שורות 198-234 ב-block_writer.py) אומר: -- ✅ CREAC methodology -- ✅ ענה על כל טענה -- ✅ צטט פסיקה -- ❌ **אין**: "בתיק רישוי, כסה את הנושאים התכנוניים הרלוונטיים" -- ❌ **אין**: צ'קליסט תוכן לפי סוג ערר -- ❌ **אין**: "הקשר תכנוני רחב" כמרכיב חובה +### 5.3 ~~פער: הפרומפט הנוכחי לא מכיל "צ'קליסט תוכן"~~ — **נסגר (2026-04-12)** +נוספו: +- ✅ צ'קליסטים תוכניים לפי סוג ערר (`lessons.py: CONTENT_CHECKLISTS`) — מוזרקים לפרומפט +- ✅ מתודולוגיה אנליטית (`docs/decision-methodology.md`) — מלמדת איך לחשוב, לא רק מה לכסות +- ✅ טיפול גמיש בטענות (bundle/skip דרך chair_directions) +- ✅ בדיקת QA חדשה (methodology compliance) ### 5.4 פער: הבחנה לא מספיקה בין תת-סוגי רישוי תיקי רישוי שונים מאוד זה מזה: diff --git a/docs/legal-decision-lessons.md b/docs/legal-decision-lessons.md index 3cfc8cc..2af94a6 100644 --- a/docs/legal-decision-lessons.md +++ b/docs/legal-decision-lessons.md @@ -161,3 +161,94 @@ Our skill was "over-indexed" on one case type (הכט = rejected appeal). The co - Created `create-decision-structure.cjs` script for generating structure DOCX - Key innovation from Arieli: "ההליכים בפני ועדת הערר" as separate section (Block ח) - "Judge Test": every block written as if administrative court judge reads cold + +--- + +## Lessons from Systematic Corpus Analysis (24 decisions, April 2026) + +### Source +- All 24 proofread decisions in `/data/training/proofread/` +- Full analysis: [`docs/corpus-analysis.md`](corpus-analysis.md) +- Date: April 2026 + +### 12. System Learned Style but Not Substantive Content +- **Problem:** Dafna reviewed Kiryat Yearim draft and noted missing planning discussion in block-yod +- **Root cause:** The block-yod prompt taught CREAC methodology and "answer all claims" but never said "in licensing cases, include comprehensive planning discussion" +- **Fix:** Content checklists added to `lessons.py` (`CONTENT_CHECKLISTS`), injected into block-yod prompt via `{content_checklist}` +- **Applied to:** `lessons.py`, `block_writer.py` + +### 13. Corpus Composition — All Licensing, No Betterment Levy +- All 24 training decisions are licensing/construction (1xxx) +- Zero betterment levy (8xxx) decisions in corpus +- Not a current priority gap — focusing on licensing first + +### 14. Planning Discussion Patterns in Licensing Decisions +- **Always present** when the appeal reaches substantive planning questions +- **Never present** when the appeal is purely jurisdictional or property-based +- **Structure**: broad planning context → direct plan provision citations (200-600 words) → application to specific case → planning conclusion +- **Deepest planning**: פרומר (pure plan interpretation), לבנון (height/building appendix), בית הכרם (multi-plan TAMA 38) +- **No planning**: טלי-אביב (property only), גבאי (jurisdiction only) + +### 15. Five Appeal Subtypes Identified (Not Just Three) +Licensing appeals are not homogeneous — the discussion structure varies significantly: +1. **Substantive licensing** — full planning discussion + legal analysis (majority of cases) +2. **Threshold/jurisdiction** — legal analysis only, no planning +3. **Property-focused** — תימוכין קנייניים, minimal planning +4. **TAMA 38** — balancing public interest + planning + neighbor impact +5. **Deviant use (שימוש חורג)** — deep plan interpretation across multiple plans + +### 16. Chair Feedback System Established +- DB table `chair_feedback` records Dafna's comments on drafts +- Categories: missing_content, wrong_tone, wrong_structure, factual_error, style, other +- MCP tools + UI page for recording and reviewing feedback +- First entry: Kiryat Yearim — missing planning discussion (2026-04-12) + +--- + +## Lessons from External Expertise Research (April 2026) + +### Source +- Federal Judicial Center, *Judicial Writing Manual* (1991, 2nd ed. 2020) +- Bryan Garner, *Legal Writing in Plain English* (2001) +- Scalia & Garner, *Making Your Case: The Art of Persuading Judges* (2008) +- Richard Posner, *How Judges Think* (2008) +- Full texts stored in: `docs/sources/` + +### 17. Methodology Document Created — Separating "How to Think" from "How to Write" + +**Problem:** The system knew Dafna's STYLE (SKILL.md) and WHAT TOPICS to cover (content checklists), but had no formal methodology for HOW TO REASON through a decision — the analytical stages, when to balance, how to structure arguments, how to handle counterarguments. + +**Fix:** Created `docs/decision-methodology.md` — a standalone analytical methodology document based on synthesis of all four external sources. 3,400 words, 12 sections, 10 guiding principles. Covers: pre-analysis, threshold questions, issue ordering, syllogistic structure (CREAC), balancing/proportionality, claims handling (steel-man, bundling), quotation technique (sandwich), factual findings vs. legal conclusions, disposition, writing techniques, analogy/precedent, editing checklist. + +**Key principle:** Methodology is UNIVERSAL — it teaches how to think about any quasi-judicial decision. It does not contain case-specific content (parking, building lines, etc.). Case-specific content stays in the content checklists. + +**Applied to:** +- `docs/decision-methodology.md` — new document +- `lessons.py` — new function `get_methodology_summary()` injected into block-yod prompt +- `block_writer.py` — new `{methodology_guidance}` placeholder in block-yod prompt +- `.claude/agents/legal-writer.md` — restructured block-yod workflow to follow methodology stages +- `.claude/agents/legal-qa.md` — new check #7 (methodology compliance) + +### 18. "Answer All Claims" Made Flexible + +**Problem:** The block-yod prompt hardcoded "answer every claim individually" and the QA check enforced it. But Dafna sometimes bundles weak claims, skips irrelevant ones, and focuses on what matters. + +**Fix:** +- Block-yod prompt changed from "חובה לענות על כל אחת" to flexible handling: address substantive claims; bundle [bundle]; skip [skip] +- Chair can mark claims in `chair_directions` as bundle or skip +- QA check #3 updated to respect these markings +- Methodology teaches WHEN to address individually vs. bundle vs. skip (methodology §ו) + +### 19. Source Library Established + +Downloaded and converted to text 5 authoritative sources for the methodology: +- `docs/sources/fjc-judicial-writing-manual-1991.txt` (13,567 words) +- `docs/sources/fjc-judicial-writing-manual-2nd-ed-2020.txt` (15,912 words) +- `docs/sources/garner-legal-writing-plain-english.txt` (97,475 words) +- `docs/sources/posner-how-judges-think.txt` (156,789 words) +- `docs/sources/scalia-garner-making-your-case.txt` (54,683 words) +Total: ~340,000 words of source material. + +Intermediate extraction documents also saved: +- `docs/fjc-principles-extraction.md` — 38 principles from FJC +- `docs/garner-methodology-extraction.md` — ~50 principles from Garner/Scalia diff --git a/mcp-server/src/legal_mcp/server.py b/mcp-server/src/legal_mcp/server.py index 2f6b732..bbeb297 100644 --- a/mcp-server/src/legal_mcp/server.py +++ b/mcp-server/src/legal_mcp/server.py @@ -204,6 +204,16 @@ async def get_claims( return await documents.get_claims(case_number, party_role) +@mcp.tool() +async def document_update_status( + case_number: str, + doc_title: str, + status: str, +) -> str: + """עדכון סטטוס עיבוד מסמך. status: pending/extracted/proofread/error.""" + return await documents.document_update_status(case_number, doc_title, status) + + # References @mcp.tool() async def extract_references( @@ -261,6 +271,22 @@ async def draft_section( return await drafting.draft_section(case_number, section, instructions) +@mcp.tool() +async def get_research_findings(case_number: str) -> str: + """שליפת ממצאי מחקר — סיכומי פסיקה, מיפוי תכניות, ציר זמן, והמלצות. + קורא מ-research-findings.md שנוצר ע"י חוקר התקדימים. + """ + return await drafting.get_research_findings(case_number) + + +@mcp.tool() +async def get_full_analysis(case_number: str) -> str: + """שליפת הניתוח המשפטי המלא — טענות, תשובות, חוזקות/חולשות, שאלות מחקר, + חקיקה, תקדימים ועמדות יו"ר. הכלי המרכזי לכותב לפני כתיבת בלוק י. + """ + return await drafting.get_full_analysis(case_number) + + @mcp.tool() async def get_chair_directions(case_number: str) -> str: """שליפת עמדות יו"ר הוועדה (דפנה) על סוגיות הערר כ-direction_doc לכותב. @@ -296,6 +322,15 @@ async def save_block_content( return await drafting.save_block_content(case_number, block_id, content) +@mcp.tool() +async def get_decision_blocks( + case_number: str, + block_id: str = "", +) -> str: + """שליפת בלוקים שנכתבו — תוכן, מילים, משקלות. ריק = כל הבלוקים.""" + return await drafting.get_decision_blocks(case_number, block_id) + + @mcp.tool() async def validate_decision(case_number: str) -> str: """בדיקת QA — 6 בדיקות איכות על ההחלטה. אם בדיקה קריטית נכשלת — ייצוא חסום.""" diff --git a/mcp-server/src/legal_mcp/services/block_writer.py b/mcp-server/src/legal_mcp/services/block_writer.py index 889555e..3759781 100644 --- a/mcp-server/src/legal_mcp/services/block_writer.py +++ b/mcp-server/src/legal_mcp/services/block_writer.py @@ -525,14 +525,31 @@ async def _build_precedents_context(case_id: UUID, block_id: str) -> str: text = r["key_quote"] or r["summary"] or "" if text: parts.append( - f"[פסיקה: {r['case_number']} {r['case_name']} ({r.get('court', '')})] " + f"[פ��יקה: {r['case_number']} {r['case_name']} ({r.get('court', '')})] " f"score={r['score']:.3f}\n{text[:400]}" ) + # Search 3: case_precedents (user-attached quotes by Daphna) + # These are hand-picked citations — highest priority. + attached = await db.list_case_precedents(case_id) + if attached: + parts.insert(0, "## תקדימים שצורפו ידנית ע\"י יו\"ר הוועדה\n") + for i, prec in enumerate(attached): + section = prec.get("section_id") or "כללי" + citation = prec.get("citation", "") + quote = prec.get("quote", "") + note = prec.get("chair_note", "") + entry = f"[תקדים מצורף #{i+1} — סוגיה: {section}] {citation}" + if quote: + entry += f"\nציטוט: {quote[:600]}" + if note: + entry += f"\nהערת יו\"ר: {note}" + parts.insert(i + 1, entry) + except Exception as e: logger.warning("Failed to fetch precedents: %s", e) - return "\n\n".join(parts) if parts else "(אין תקדימים)" + return "\n\n".join(parts) if parts else "(אין תקד��מים)" async def _build_style_context() -> str: diff --git a/mcp-server/src/legal_mcp/services/research_md.py b/mcp-server/src/legal_mcp/services/research_md.py index 18f8e07..9f289e2 100644 --- a/mcp-server/src/legal_mcp/services/research_md.py +++ b/mcp-server/src/legal_mcp/services/research_md.py @@ -26,6 +26,13 @@ CHAIR_POSITION_PLACEHOLDERS = ( "[טרם מולא]", ) +# Any text starting with these prefixes is also a placeholder +# (the analyst sometimes adds explanatory text after the bracket) +CHAIR_POSITION_PLACEHOLDER_PREFIXES = ( + "[ימולא", + "ימולא ע", +) + CHAIR_POSITION_LABEL = "עמדת ועדת הערר" # Matches "## N. title" or "## title" for main sections @@ -47,6 +54,9 @@ CASE_NUMBER_RE = re.compile(r"#\s*ניתוח.*?ערר\s+([\d/\-]+)", re.MULTILIN DATE_RE = re.compile(r"^תאריך:\s*(.+?)\s*$", re.MULTILINE) +RESEARCH_FINDINGS_FILENAME = "research-findings.md" + + def _is_placeholder(text: str) -> bool: """Check if a field value is one of the placeholder strings (empty).""" stripped = text.strip() @@ -55,6 +65,9 @@ def _is_placeholder(text: str) -> bool: for ph in CHAIR_POSITION_PLACEHOLDERS: if ph in stripped: return True + for prefix in CHAIR_POSITION_PLACEHOLDER_PREFIXES: + if stripped.startswith(prefix): + return True return False @@ -434,3 +447,199 @@ def extract_chair_directions(file_path: Path) -> dict[str, Any]: "threshold_claims": threshold, "issues": issues, } + + +# ── Full analysis extraction (for legal-writer) ────────────────── + + +# Map Hebrew field labels → stable English keys for JSON output +_FIELD_KEY_MAP = { + "טענה": "claims", + "טענה (claim)": "claims", + "טענות": "claims", + "תשובה": "responses", + "תשובה (response)": "responses", + "תשובות": "responses", + "תגובה": "replies", + "תגובה (reply)": "replies", + "תגובות": "replies", + # Analyst sometimes appends party name to the label + # e.g. "תגובה (reply — קובר)" — catch the pattern dynamically below + "ניתוח אסטרטגי": "strategic_analysis", + "חוזקות": "strengths", + "חולשות": "weaknesses", + "הזדמנויות": "opportunities", + "שאלות משפטיות": "legal_questions", + "חיפוש תקדימים": "precedent_search", + "חקיקה רלוונטית": "relevant_legislation", + "תקדימים מהקורפוס הפנימי": "internal_precedents", +} + + +def _fields_to_dict(fields: list[dict]) -> dict[str, str]: + """Convert ordered field list to a dict with stable English keys. + + Unknown labels are kept as-is (Hebrew) so no data is lost. + Handles dynamic labels like "תגובה (reply — קובר)" by matching prefix. + """ + result: dict[str, str] = {} + for f in fields: + label = f["label"] + key = _FIELD_KEY_MAP.get(label) + if key is None: + # Try prefix matching for dynamic labels (e.g. "תגובה (reply — name)") + if label.startswith("תגובה"): + key = "replies" + elif label.startswith("טענה"): + key = "claims" + elif label.startswith("תשובה"): + key = "responses" + else: + key = label + result[key] = f["content"] + return result + + +def extract_full_analysis(file_path: Path) -> dict[str, Any]: + """Extract the complete strategic analysis from analysis-and-research.md. + + Unlike extract_chair_directions (which returns only chair positions), + this returns ALL fields per issue: claims, responses, replies, + strengths/weaknesses/opportunities, legal questions, legislation, + and internal precedents — everything the legal-writer needs to + produce block-yod (discussion). + + Returns the same envelope as extract_chair_directions (status, counts) + plus full field data in each item. + """ + if not file_path.exists(): + return { + "file_exists": False, + "status": "missing", + "error": "analysis-and-research.md not found", + "procedural_background": "", + "agreed_facts": "", + "disputed_facts": "", + "conclusions": "", + "threshold_claims": [], + "issues": [], + "total_items": 0, + "filled_count": 0, + "empty_count": 0, + } + + parsed = parse(file_path) + + def enrich_item(item: dict) -> dict: + """Return full item with all fields as a flat dict.""" + enriched = { + "id": item["id"], + "number": item["number"], + "title": item["title"], + "direction": item.get("chair_position", "") or "", + } + # Add all extracted fields with stable keys + enriched.update(_fields_to_dict(item.get("fields", []))) + return enriched + + threshold = [enrich_item(t) for t in parsed.get("threshold_claims", [])] + issues = [enrich_item(i) for i in parsed.get("issues", [])] + + all_items = threshold + issues + total = len(all_items) + filled = sum(1 for x in all_items if x["direction"].strip()) + empty = total - filled + + if total == 0: + status = "missing" + elif filled == 0: + status = "empty" + elif filled == total: + status = "complete" + else: + status = "partial" + + return { + "file_exists": True, + "file_path": str(file_path), + "case_number": parsed.get("header", {}).get("case_number", ""), + "modified_at": parsed.get("header", {}).get("modified_at", ""), + "status": status, + "total_items": total, + "filled_count": filled, + "empty_count": empty, + "procedural_background": parsed.get("procedural_background", ""), + "agreed_facts": parsed.get("agreed_facts", ""), + "disputed_facts": parsed.get("disputed_facts", ""), + "conclusions": parsed.get("conclusions", ""), + "threshold_claims": threshold, + "issues": issues, + } + + +# ── Research findings extraction ────────────────────────────────── + + +def extract_research_findings(file_path: Path) -> dict[str, Any]: + """Extract structured research findings from research-findings.md. + + The file is produced by the legal-researcher agent and contains: + precedent summaries, plan mappings, timeline, and recommendations. + Returns a structured dict or a status-only dict if file is missing. + """ + if not file_path.exists(): + return { + "file_exists": False, + "status": "missing", + "error": "research-findings.md not found", + } + + content = file_path.read_text(encoding="utf-8") + stat = file_path.stat() + mtime_iso = datetime.fromtimestamp(stat.st_mtime).isoformat() + + sections = _split_main_sections(content) + + result: dict[str, Any] = { + "file_exists": True, + "file_path": str(file_path), + "modified_at": mtime_iso, + "file_size": stat.st_size, + "precedent_summaries": [], + "plan_mappings": [], + "timeline": "", + "recommendations": "", + "other_sections": [], + } + + for _number, title, body in sections: + title_norm = title.strip() + if "סיכום פסיקה" in title_norm or "פסיקה" in title_norm: + subs = _split_subsections(body) + for sub_title, sub_body in subs: + fields = _extract_fields(sub_body) + result["precedent_summaries"].append({ + "title": sub_title, + "fields": {f["label"]: f["content"] for f in fields}, + "raw": sub_body if not fields else "", + }) + elif "מיפוי תכנית" in title_norm or "תכנית" in title_norm: + subs = _split_subsections(body) + for sub_title, sub_body in subs: + fields = _extract_fields(sub_body) + result["plan_mappings"].append({ + "title": sub_title, + "fields": {f["label"]: f["content"] for f in fields}, + "raw": sub_body if not fields else "", + }) + elif "ציר זמן" in title_norm: + result["timeline"] = body + elif "המלצות" in title_norm: + result["recommendations"] = body + else: + result["other_sections"].append({ + "title": title_norm, + "body": body, + }) + + return result diff --git a/mcp-server/src/legal_mcp/tools/documents.py b/mcp-server/src/legal_mcp/tools/documents.py index 258d792..f493802 100644 --- a/mcp-server/src/legal_mcp/tools/documents.py +++ b/mcp-server/src/legal_mcp/tools/documents.py @@ -383,3 +383,49 @@ async def get_claims(case_number: str, party_role: str = "") -> str: }) return json.dumps(formatted, default=str, ensure_ascii=False, indent=2) + + +async def document_update_status( + case_number: str, + doc_title: str, + status: str, +) -> str: + """עדכון סטטוס עיבוד מסמך (extraction_status). + + ערכים אפשריים: pending, extracted, proofread, error. + + Args: + case_number: מספר תיק הערר + doc_title: שם/כותרת המסמך (או חלק ממנו) + status: הסטטוס החדש + """ + valid_statuses = ("pending", "extracted", "proofread", "error") + if status not in valid_statuses: + return f"סטטוס לא חוקי: {status}. ערכים אפשריים: {', '.join(valid_statuses)}" + + case = await db.get_case_by_number(case_number) + if not case: + return f"תיק {case_number} לא נמצא." + + case_id = UUID(case["id"]) + docs = await db.get_documents(case_id) + + # Find matching document by title (partial match) + matched = None + for d in docs: + if doc_title.lower() in d.get("title", "").lower(): + matched = d + break + + if not matched: + titles = [d.get("title", "?") for d in docs] + return f"מסמך '{doc_title}' לא נמצא בתיק {case_number}. מסמכים קיימים: {titles}" + + doc_id = UUID(matched["id"]) + await db.update_document(doc_id, extraction_status=status) + + return json.dumps({ + "updated": True, + "document": matched["title"], + "new_status": status, + }, ensure_ascii=False, indent=2) diff --git a/mcp-server/src/legal_mcp/tools/drafting.py b/mcp-server/src/legal_mcp/tools/drafting.py index 2433550..c3b845b 100644 --- a/mcp-server/src/legal_mcp/tools/drafting.py +++ b/mcp-server/src/legal_mcp/tools/drafting.py @@ -281,6 +281,39 @@ async def draft_section( return json.dumps(context, ensure_ascii=False, indent=2) +async def get_research_findings(case_number: str) -> str: + """שליפת ממצאי מחקר — סיכומי פסיקה, מיפוי תכניות, ציר זמן, והמלצות. + + קורא מ-research-findings.md שנוצר ע"י סוכן חוקר התקדימים (legal-researcher). + מחזיר JSON מובנה עם הממצאים, או status=missing אם הקובץ לא קיים עדיין. + + Args: + case_number: מספר תיק הערר + """ + case_dir = config.find_case_dir(case_number) + file_path = case_dir / "documents" / "research" / research_md.RESEARCH_FINDINGS_FILENAME + result = research_md.extract_research_findings(file_path) + return json.dumps(result, ensure_ascii=False, indent=2) + + +async def get_full_analysis(case_number: str) -> str: + """שליפת הניתוח המשפטי המלא מ-analysis-and-research.md — כולל טענות, תשובות, + תגובות, ניתוח אסטרטגי (חוזקות/חולשות/הזדמנויות), שאלות מחקר, חקיקה רלוונטית, + תקדימים פנימיים, ועמדות יו"ר הוועדה. + + זה הכלי המרכזי שכותב ההחלטה צריך לקרוא **לפני** כתיבת בלוק י (דיון). + מחזיר JSON מובנה עם כל השדות שהניתוח המשפטי הפיק, ולא רק עמדות כמו + get_chair_directions. + + Args: + case_number: מספר תיק הערר + """ + case_dir = config.find_case_dir(case_number) + file_path = case_dir / "documents" / "research" / "analysis-and-research.md" + result = research_md.extract_full_analysis(file_path) + return json.dumps(result, ensure_ascii=False, indent=2) + + async def get_chair_directions(case_number: str) -> str: """שליפת עמדות יו"ר הוועדה (דפנה) על סוגיות הערר, לצורך יצירת direction_doc לכותב. קורא מ-analysis-and-research.md (שנוצר ע"י legal-analyst ומולא ע"י @@ -454,6 +487,71 @@ async def save_block_content(case_number: str, block_id: str, content: str) -> s return str(e) +async def get_decision_blocks(case_number: str, block_id: str = "") -> str: + """שליפת בלוקים שנכתבו בהחלטה — תוכן, ספירת מילים, משקלות. + + אם block_id ריק — מחזיר את כל הבלוקים. אם מצוין — רק בלוק ספציפי. + שימושי לבודק איכות (QA) שצריך לקרוא בלוקים בודדים. + + Args: + case_number: מספר תיק הערר + block_id: מזהה בלוק (ריק = כולם). למשל: block-yod, block-vav + """ + case = await db.get_case_by_number(case_number) + if not case: + return f"תיק {case_number} לא נמצא." + + case_id = UUID(case["id"]) + decision = await db.get_decision_by_case(case_id) + if not decision: + return f"אין החלטה בתיק {case_number}." + + decision_id = UUID(decision["id"]) + pool = await db.get_pool() + async with pool.acquire() as conn: + if block_id: + rows = await conn.fetch( + "SELECT block_id, block_index, title, content, word_count, " + "weight_percent, status FROM decision_blocks " + "WHERE decision_id = $1 AND block_id = $2 " + "ORDER BY block_index", + decision_id, block_id, + ) + else: + rows = await conn.fetch( + "SELECT block_id, block_index, title, content, word_count, " + "weight_percent, status FROM decision_blocks " + "WHERE decision_id = $1 ORDER BY block_index", + decision_id, + ) + + if not rows: + if block_id: + return f"בלוק {block_id} לא נמצא בהחלטה." + return "אין בלוקים בהחלטה." + + blocks = [] + total_words = 0 + for r in rows: + total_words += r["word_count"] or 0 + blocks.append({ + "block_id": r["block_id"], + "index": r["block_index"], + "title": r["title"], + "content": r["content"], + "word_count": r["word_count"], + "weight_percent": float(r["weight_percent"] or 0), + "status": r["status"], + }) + + return json.dumps({ + "case_number": case_number, + "total_blocks": len(blocks), + "total_words": total_words, + "blocks": blocks, + }, default=str, ensure_ascii=False, indent=2) + + async def analyze_style() -> str: """הרצת ניתוח סגנון על קורפוס ההחלטות של דפנה. מחלץ דפוסי כתיבה ושומר אותם.""" from legal_mcp.services.style_analyzer import analyze_corpus diff --git a/web/paperclip_client.py b/web/paperclip_client.py index 8def13e..02efba4 100644 --- a/web/paperclip_client.py +++ b/web/paperclip_client.py @@ -76,6 +76,14 @@ async def create_project( VALUES ($1, $2::uuid, $3, $4, 'backlog', $5)""", project_id, company_id, project_name, description[:500] if description else "", color, ) + # Create workspace pointing to the legal-ai repo root + workspace_id = str(uuid.uuid4()) + await conn.execute( + """INSERT INTO project_workspaces (id, company_id, project_id, name, cwd) + VALUES ($1, $2::uuid, $3::uuid, 'legal-ai', '/home/chaim/legal-ai')""", + workspace_id, company_id, project_id, + ) + # Create initial issue linked to the project issue_id, identifier = await _create_issue( conn, company_id, project_id, case_number, title, prefix,