feat(hearing): פאנל "מה קרה בדיון" — חשיפת ניתוח-הפרוטוקול ב-UI (#226)
תוצר analyze_protocol (איך הטענות הכתובות התפתחו בדיון בעל-פה) היה שכבת ידע-תיק ללא endpoint ותצוגה — נצרך רק downstream ע"י הכותב בבלוק-י. פאנל חדש בתחתית טאב "טיעונים ועמדות", מתחת לכרטיס-הטיעונים (INV-IA1: לא טאב-מקביל). Backend: - SCHEMA_V51: cases.hearing_attendees JSONB — בית קנוני לנוכחות-בדיון (panel_members/appellants_present/respondents_present). נבדל סמנטית מ-decisions.panel_members (מותב חותם-ההחלטה) → אין מסלול-מקביל (G2). - protocol_analyzer._extract_header: מאכלס hearing_attendees; refresh על re-run (מתקן #223), לא דורס עם חילוץ ריק. אישור-משתמש: "אפשרות א" (persist+הצגה). - GET /api/cases/{n}/protocol-analysis — header (תאריך+נוכחים קנוניים) + verdicts מקובצים לפי צד. קריאה-בלבד, container-safe, ריק≠שגיאה. - POST /api/cases/{n}/analyze-protocol — מעיר את המנתח (analyze_protocol מריץ claude מקומי, נעדר בקונטיינר) כתבנית aggregate-arguments. - מגע-Paperclip רק דרך agent_platform_port (G12): wake_analyst_for_protocol_analysis. Frontend: - lib/api/protocol-analysis.ts (hooks read+trigger) + HearingChangesPanel (סרגל-כותרת, פסי-סינון התחזק/נטען-לראשונה, כרטיסים לפי צד, ציטוט-ראיה). - חיווט לטאב arguments מתחת ל-LegalArgumentsPanel. עיצוב: מוקאפ 27-case-hearing-changes-panel אושר ע"י חיים דרך שער Claude Design (X17). Invariants: מקיים G2 (בית קנוני יחיד, לא מסלול-מקביל), G1 (נרמול-במקור), G12 (שער-הפלטפורמה), INV-IA1 (לא טאב-מקביל), INV-AH (evidence_quote כבר נאכף במקור). אימות: py_compile + tsc --noEmit + eslint ירוקים; runtime של ה-endpoint = post-deploy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -259,13 +259,25 @@ async def _extract_header(protocol_text: str, case_id: UUID) -> dict:
|
||||
except ValueError:
|
||||
logger.info("protocol header: unparseable hearing_date %r", hearing_date)
|
||||
|
||||
# Attendees → cases.hearing_attendees (#226). Unlike hearing_date there is no
|
||||
# chair-entry path to protect, and re-running to point at the correct ערר
|
||||
# protocol (#223) must refresh who appeared — so write whenever the operative
|
||||
# protocol yielded any names, but never clobber good data with an empty
|
||||
# extraction (all-empty feed → leave the prior snapshot intact).
|
||||
attendees = {
|
||||
"panel_members": feed.get("panel_members") or [],
|
||||
"appellants_present": feed.get("appellants_present") or [],
|
||||
"respondents_present": feed.get("respondents_present") or [],
|
||||
}
|
||||
if any(attendees.values()):
|
||||
await db.update_case(case_id, hearing_attendees=attendees)
|
||||
applied["hearing_attendees"] = True
|
||||
|
||||
return {
|
||||
"status": "ok",
|
||||
"feed": {
|
||||
"hearing_date": hearing_date,
|
||||
"panel_members": feed.get("panel_members") or [],
|
||||
"appellants_present": feed.get("appellants_present") or [],
|
||||
"respondents_present": feed.get("respondents_present") or [],
|
||||
**attendees,
|
||||
},
|
||||
"applied_to_case": applied,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user