`block_distance_to_final` החזיר `anti_pattern_total` בלבד. ריצת-כיול שמדווחת
"anti=4" אינה יכולה לומר מה לתקן — באבחון ה-A/B של 2026-07-28 נאלצנו להסיק
את הדפוס האשם מהקשר במקום למדוד אותו.
- `anti_by_pattern` (שם-דפוס → מספר-פגיעות) נוסף לתא-המדידה, מ-
`count_anti_patterns` הקיים — אין ספירה מקבילה.
- `_mean_by_pattern` ממצע על **כל** הריצות: דפוס שלא נורה בריצה נספר כ-0
ולא מושמט, אחרת הממוצע היה מוטה כלפי מעלה.
- הדוח מקבל טבלת "פילוח אנטי-דפוסים (איזה כלל הופר)" per block×effort×model.
invariants: INV-G8 (eval-harness) · G2 (מרונדר מ-count_anti_patterns/
ANTI_PATTERNS הקנוניים — מקור אחד).
אימות: self-test ALL PASS · 470 passed · בדיקת-שפיות ישירה —
טקסט עם 1 כותרת + 2 תבליטים + 1 פיצול-מיני מפולח נכון ל-
{markdown_headers:1, bullet_lists:2, inline_numbered_fragments:1}.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
מוחל על כל המודלים בריצה (אחרת השוואת-מודלים הופכת בשקט להשוואת-פרומפטים),
ונרשם ב-grid_summary + בכותרת הדוח כדי שריצת-וריאנט לא תושווה בטעות לבסיס.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`calibrate_effort.py` כייל `effort` בלבד, על מודל נעוץ (GENERATION_MODEL).
כדי להשוות מודל-ייצור (opus-4-8 מול opus-5) מול הסופיים החתומים של דפנה
נדרש ממד שני — ללא מסלול-מדידה מקביל.
- `block_writer.write_block(model_override=…)` — אותו חוזה כמו
`effort_override` הקיים (נוצר בדיוק ל-#208). מקבל את המזהה הבסיסי בלבד;
אסקלציית ההקשר-1M (#216) מוחלת מעליו, כך ש-override לא מאבד בשקט את
חלון ה-1M.
- `--models` ל-harness (ריק = המודל הנעוץ ⇒ ריצת ברירת-המחדל זהה לקודם).
- הדוח מקבל טבלת השוואת-מודלים (block × effort × model) ומסמן ⭐ לפי
אותו דירוג style-clean (#213): anti_total → ratioΔ → distance.
- כל תא מתעד את `model_used` שה-CLI דיווח בפועל; אי-התאמה מסומנת כאזהרת
fallback-שקט במקום להיזקף בטעות למודל המבוקש.
invariants: INV-G8 (eval-harness — מדידה, לא הרגשה) · G2 (אין מסלול מקביל:
משתמש ב-style_distance/learning_loop הקיימים ובשדה result הקיים
`model_used`) · §6 (אין בליעה שקטה — כשל-תא מדווח ומדולג).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
מיישם את ממצא ה-A/B (fable-5/opus-4-8 על תיק 1043-02-26): פרומפט
מגוזם-ומוכוון-שיפוט מפיק היסק משפטי חד יותר מתבנית נוקשה. משכתב את
תבנית מסמך-ההכוונה של ה-CEO לכותב בלבד — משמר את החוזה המלא (5 הרכיבים,
chair_directions בתגית, אילוצי-הסגנון) ומזריק את רמזי-השיפוט שהוכחו:
אדנים עצמאיים, מוקשי-עקביות-פנימית, מענה לצד המפסיד, הובלה בסוגיה
המכריעה. שאר מכונת-התזמור התפעולית (שערים, סטטוסים, API, MCP-race)
לא נגעה. net -27/+20.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
שדרוג מבנה-פרומפט ממוקד (Anthropic prompt-engineering), רק היכן שיש ROI אמיתי —
הפרומפטים כבר בנויים היטב, אז לא בוצעה עטיפת-XML גורפת:
- legal-qa: דוח-בדיקה במבנה קבוע (טבלת pass/fail/חומרה + החלטת-ייצוא). markdown
ולא XML — הדוח מוצג ליו"ר כהערת Paperclip.
- legal-ceo ↔ legal-writer (זוגי): ה-CEO עוטף עמדות-יו"ר מילוליות ב-<chair_directions>
בהעברה לכותב; הכותב מונחה להתייחס אליהן כמחייבות (כמו chair_ruling מהכלי).
- legal-analyst: הבהרה ששתי הופעות "7א" הן אותו סעיף (הסרת תבניות חופפות).
- legal-writer: דוגמת-הפלט עטופה ב-<example_output> (מבדיל דוגמה מהוראה).
תגיות רק במקום שהמודל קורא (פרומפט/הקשר-מועבר), לא ב-output מוצג-למשתמש.
Deploy: קבצי-סוכנים נקראים מעץ-העבודה על ה-host; אחרי merge צריך git pull ב-~/legal-ai.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
טאב הסוכנים בתיק — 5 תיקונים לבקשת היו"ר, מאושרים דרך שער-העיצוב
(Claude Design X17, כרטיס 18i4):
1. היררכיה — הרצה שנפתחה תחת השורש (למשל CMP-220 תחת CMP-189) מסומנת
בתג "↳ <parent>" גם בציר-הזמן וגם בבורר-היעד (בהזחה). הנתון parent_id
כבר הגיע מה-API — תצוגה בלבד.
2. סמן-כהושלם / בטל ידני — כל משימה פתוחה מקבלת פעולות בכותרת. פותר
"אין אפשרות לסמן ידנית" (superseded runs נתקעו ב-in_review בלי מנגנון
סגירה). endpoint חדש: POST /api/cases/{n}/agents/issue-status → דרך
ה-Port (pc_set_issue_status), סגירה loop-safe ישירה ל-DB, בלי wakeup.
3. בורר-היעד נגלל — max-height + overflow פנימי, כך שהתיבה לא בורחת
מגבולות העמוד. פותר "לא רואים את התחתית / לא מגיעים לפתיחת הליך".
4. "פתח הרצה חדשה" עלה לראש הרשימה, מעל "משימות פעילות", ומודגש.
5. קו-הפרדה בין משימות בציר-הזמן עבה יותר (border-b-2 rule).
G12: כל מגע-פלטפורמה עובר דרך agent_platform_port. אימות: lint + tsc +
next build עוברים.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
**התסמין:** היו"ר כותב הוראה על תיק והסוכן "מתעלם". ההרצה מדווחת succeeded/exit 0
ואפילו עולה כסף — ולכן זה שרד חודשים בלי שמישהו שם לב.
**שתי סיבות-שורש בלתי-תלויות, שתיהן מאומתות:**
1. ה-wakeup הישיר על ה-issue של התיק מבוטל תמיד: תיק שממתין ליו"ר הוא `in_review`
ומשויך *לאדם*, ו-Paperclip עונה `issue_assignee_changed` → "אעיר את הבעלים החדש"
→ הבעלים אדם → אף סוכן לא מתעורר.
2. רשת-הביטחון בפלאגין מוסרת את ההוראה כ-`payload.prompt`, וה-runner קורא רק
`payload.issueId` — הפרומפט נבלע. 18/18 מדידה ב-DB: 0% מסירה, אי-פעם.
**התיקון:** המסלול הטבעי (הערה בטאב-הסוכנים) מפרסם את ההערה בשרשור כרגיל **וגם**
פותח הרצת-CEO עם ההוראה, דרך `pc_open_ceo_run` — הפרימיטיב היחיד שאומת כמגיע
לסוכן (הוכח היום על 1043-02-26: 3,212→7,697 מילים). אותו תיקון ל-interaction-response,
שסובל מאותה תקלה (החצי השני של #228).
**G2 — הסרת מסלול מקביל:** ה-wakeup הנדון-לביטול הוסר מ-`post_comment`; הוא היה
המסלול השני, זה ששותק. כעת מסלול-מסירה קנוני אחד. `mark_comment_routed` מסמן את
ההערה כמנותבת כדי שה-sweep לא יירה הרצת-סרק על מה שכבר טופל.
**G12 — שער-הפלטפורמה:** המגע החדש (`mark_comment_routed`) נחשף דרך
`agent_platform_port.py` בלבד; leak-guard עובר.
התיקון יושב ב-backend (REST) ולא בפלאגין, ולכן אינו חשוף לבאג ה-invocation-scope
שמפיל את ה-sweep. תועד ב-docs/paperclip-quirks.md §7 עם המלכודות שנתקלנו בהן.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
בעיה חוזרת: הקלדת הוראה בתיבת "כתוב הוראה לסוכנים..." נבלעה בשקט. השרת בחר
את היעד ב-`active[-1]` תוך סינון `done` בלבד — לא `cancelled`. כשה-child האחרון
בוטל (למשל 1027-04-26: "חישוב טיעונים" cancelled שנוצר אחרי ה-issue הראשי),
ההערה נותבה אליו; Paperclip מדלג על wakeup ל-issue מבוטל → ההוראה לא הגיעה לאיש.
תיקון (G1 — נרמול במקור, לא תיקון-בקריאה):
- `pick_default_comment_target` חדש ב-paperclip_client — מעדיף את ה-issue הראשי
החי של ה-CEO (top-level+open), נופל ל-open כלשהו, ואז top-level, ואף פעם לא
מעדיף child-סגור. `get_case_issues` מחזיר כעת `parent_id`.
- app.py מנתב דרכו; מחזיר `issue_status` כדי שה-UI יוכל לסמן יעד-סגור.
- נחשף דרך ה-Port (G12) כ-`pc_pick_default_comment_target`.
- 6 בדיקות ל-picker (כולל תרחיש 1027-04-26 המדויק).
Invariants: G1 (נרמול-במקור), G2 (אין מסלול-ניתוב מקביל), G12 (מגע-Paperclip דרך ה-Port).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
משלים את follow-up #1: מנחה סוכן שמתעורר-מחדש על תיק לקרוא את כלי-ה-plugin
legal_predecessor_context(case_number) לפני שיגלה-מחדש הקשר מאפס — כך ממשיך
מנקודת-העצירה ומונע blind heartbeat. הכלי חי ב-plugin (plugin-legal-ai #5+#6,
toolCount=9); ה-endpoint by-case חי (legal-ai #410).
תוקף: HEARTBEAT נקרא ע"י claude_local מ-~/legal-ai — דורש host git pull.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
עד כה claims.party_name נשאר ריק והצבירה/הכתיבה גזרו את הפרדת-המשיבים
מ-source_document בזמן-ריצה (שדה-מת). עכשיו הוא מאוכלס במקור ונקרא ע"י הצרכנים.
- claims_extractor.extract_and_store_claims: מחתים party_name=source_document
ל-SPLIT_PARTIES (respondent/permit_applicant); single-voice נשאר ''. אותו
כלל SPLIT_PARTIES של הצבירה (G2, import לא-מעגלי).
- SCHEMA_V52: backfill idempotent ל-claims קיימים (רק split + party_name ריק).
- argument_aggregator + block_writer._build_claims_context: קוראים
claims.party_name, עם fallback ל-source_document ל-claims מדור-קודם. התנהגות
זהה (party_name==source_document) — אפס-רגרסיה, אך השדה כעת מקור-האמת (G1).
py_compile + טעינת-שרשרת-ייבוא נקיים. סוגר את הפתוח האחרון של #224.
Invariants: G1 (אכלוס במקור, לא גזירה-בקריאה), G2 (כלל-פיצול יחיד).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
השלמת #224 לבלוק י' (דיון והכרעה): הקשר-הטענות כבר מופרד לפי כתב-תשובה
(PR#401, אותו _build_claims_context), אך ה-prompt של block-yod לא הנחה
להתייחס לכל משיב לגופו. נוסף כלל "צדדים מרובים": התייחס לעמדת כל משיב/כתב-תשובה
בנפרד (משיבות 2-3 מול משיבים 4-6), אל תמזג ל"טענות המשיבים" גורפות, וכשעמדות
מנוגדות — ציין והכרע בנימוק.
שינוי-prompt בלבד; משפיע על ניסוח בלוק י' בהחלטות עם כמה משיבים. py_compile ירוק.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
בלוק ז' של טיוטת-הביניים מיזג את כל המשיבים לסעיף אחד גנרי (או השמיטם) —
כי (א) _build_claims_context קיבץ רק לפי party_role, ו-(ב) ה-prompt נעל
"מבנה קבוע 3 חלקים: עוררים / ועדה מקומית / מבקשי-היתר" — בלי "משיבים" בכלל.
לכן בתיק 1043-02-26 המשיבים 2-3 ו-4-6 לא הופיעו כצדדים מובחנים, אף שהנתונים
(129 טענות-משיבים מפוצלות לפי כתב-תשובה) תקינים ב-claims. הפרדת-המשיבים של
#224 יושמה בצבירה+פאנל בלבד (PR#392) — לא בכותב-הבלוקים.
- `_build_claims_context` מקבץ לפי (party_role, brief) כאשר brief=source_document
ל-SPLIT_PARTIES (respondent/permit_applicant) — **אותו כלל בדיוק כמו הצבירה**
(import SPLIT_PARTIES מ-argument_aggregator, G2). התוצאה: סעיף נפרד לכל
כתב-תשובה ("טענות המשיבים — כתב תשובה משיבות 2 3", "— משיבים 4 6"...).
- prompt בלוק ז': מבנה-צדדים דינמי לפי הכותרות בקונטקסט; הוראה מפורשת לא למזג
משיבים שונים ולשקף עמדות-מנוגדות; יעד-אורך גמיש לכמה כתבי-תשובה.
משפיע גם על בלוק י' (אותו _build_claims_context) — הקשר מופרד. py_compile ירוק.
עבודה על MCP → דורש host reload + הרצה-מחדש כדי לראות בפועל.
Invariants: G2 (נגזרת-הפרדה יחידה משותפת עם הצבירה), G1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
עד כה ה-"מייצר…" כיסה רק את שנייה-ה-POST; אחרי זה לא היה חיווי אם ההפקה
רצה/הצליחה/נכשלה — ריצה שבוטלה נראתה כמו הצלחה. עכשיו צ'יפ-סטטוס ליד כל
כפתור-הפקה: בתור · רץ+שעון-חי · הושלם+מתי · נכשל+סיבה+"נסה שוב".
**נשמר-שרת (דרישת חיים):** הסטטוס נגזר בשרת מ-issue-הילד של ה-CEO +
ה-heartbeat_run האחרון + קיום-הקובץ — לא מזיכרון-הדף. שעון-הריצה מעוגן
ל-started_at של השרת, כך שעזיבה+חזרה לדף מציגות את המצב והזמן הנכונים,
בלי צורך להישאר בדף.
Backend:
- paperclip_client.get_generation_run_status — קורא issue-ילד + latest run
מ-Paperclip DB (read-only, מאחורי שער-הפלטפורמה G12); dedup לפי כותרת.
- GET /api/cases/{n}/generate/{action}/status — ממפה ל-idle/queued/running/
done/failed + output_ready + started_at/finished_at (UTC). run שהסתיים בלי
קובץ = failed (שלא ייראה כהצלחה).
Frontend:
- useGenerateStatus (poll 5s רק כשפעיל; עוצר בטרמינלי) + GenerateStatusChip
(שעון-ריצה חי מ-started_at; רכיב חסר-מצב). כפתורי-ההפקה מרעננים את
שאילתת-הסטטוס ב-onSuccess. שורה 3 (החלטה מלאה, סינכרוני) — בלי צ'יפ.
עיצוב: מוקאפ 29-generate-status-indicator אושר ע"י חיים דרך שער Claude Design (X17).
tsc + eslint + py_compile ירוקים. (api:types לריענון post-deploy.)
Invariants: G12 (Paperclip רק בשער), G1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
באג: כפתורי "הפקת מסמכים" (טיוטת-ביניים / סיכום-מנהלים) העירו את ה-CEO
*על ה-issue הראשי*. כשהתיק ב-in_review משויך-לחיים (המצב הרגיל של תיק
ממתין-ליו"ר), Paperclip ביטל את הריצה מיד (issue_assignee_changed) — הכלום
הופק, וה-endpoint החזיר 202 בכל מקרה, כך שהכשל היה שקוף.
תיקון: wake_ceo_for_action יוצר issue-ילד משויך-ל-CEO תחת ה-issue הראשי
ומעיר על הילד — בדיוק כדפוס wake_analyst_for_argument_aggregation שעובד.
ה-CEO בעל הילד → הריצה לא מבוטלת. ההוראה מוכלת בתיאור-הילד (הרץ את הכלי,
comment, סגור done) ולא תלויה בפענוח payload.action. legal-ceo.md מעודכן
שההערה מגיעה על issue-ילד ייעודי שיש לסגור בסיום.
מגע-Paperclip רק בשער-הפלטפורמה (G12). זהו החצי הראשון (ב) של #227; חצי
שני (ג) = סימון-סטטוס ב-UI. py_compile ירוק.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>