feat(learning): FU-2 UI — התלבטות-הפאנל במסך-אישור היו"ר (#133)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 6s
מציג את התלבטות 3-השופטים (הצבעה+נימוק לכל לינאז' + ה-verdict) בתוך כרטיס-האישור הקיים של דפנה ב-/precedents → "ממתין לאישור", כדי שהכרעתה — תווית-הזהב שהלולאה לומדת ממנה — תהיה מיודעת ב*למה* הפאנל נחלק. אושר ב-Claude Design (כרטיס 18-halacha-deliberation). Backend (opt-in, ברירת-מחדל off — קוראים קיימים לא מושפעים): - db.list_halachot(include_panel_round=True) → _annotate_panel_rounds מצרף את הסבב האחרון מ-halacha_panel_rounds (DISTINCT ON, latest). - GET /api/halachot?include_panel_round=true. Frontend: - Halacha.panel_round (טיפוס ידני; ה-endpoint מחזיר dict). - תור-הסקירה (useHalachotPending) מבקש include_panel_round בשני הדליים (clean=keep, needsFix=nli/entailed). - רכיב PanelDeliberation: טבלת 3-שופטים (✓נתמך/✗הכלל-חורג + נימוק), תג-ורדיקט "פיצול 2:1", ושורת "שורש המחלוקת" (קפדני↔תמצית) רק בפיצול-entailment. מוזרק אחרי רשת הכלל/ציטוט. שער יחיד — אין עמוד/שער חדש (INV-IA/G10); display-only, לא נוגע ב-review_status. ולידציה: py_compile + tsc --noEmit + eslint נקיים; בדיקה פונקציונלית: panel_round מצורף ל-6 שיש להן סבב, 1994 בלי. חלק מ-#133 (FU-2). דורש deploy + (אופ') npm run api:types אחרי. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4466,6 +4466,7 @@ async def list_halachot(
|
||||
order_by_priority: bool = False,
|
||||
cluster: bool = False,
|
||||
include_equivalents: bool = False,
|
||||
include_panel_round: bool = False,
|
||||
) -> list[dict]:
|
||||
"""List halachot with optional triage controls (#84).
|
||||
|
||||
@@ -4549,9 +4550,47 @@ async def list_halachot(
|
||||
await _annotate_clusters(pool, out)
|
||||
if include_equivalents and out:
|
||||
await _annotate_equivalents(pool, out)
|
||||
if include_panel_round and out:
|
||||
await _annotate_panel_rounds(pool, out)
|
||||
return out
|
||||
|
||||
|
||||
async def _annotate_panel_rounds(pool, out: list[dict]) -> None:
|
||||
"""Attach the LATEST 3-judge panel round to each row (#133/FU-2), display-only.
|
||||
|
||||
Surfaces the panel's deliberation — each lineage's vote + rationale and the
|
||||
derived verdict — inside the chair's review card, so her decision (the gold
|
||||
label the loop learns from) is informed by *why* the judges split. Reads the
|
||||
capture table halacha_panel_rounds; never affects review_status (INV-G10)."""
|
||||
ids = [d["id"] for d in out]
|
||||
rows = await pool.fetch(
|
||||
"SELECT DISTINCT ON (halacha_id) halacha_id, question, verdict, "
|
||||
"applied_action, round_ts, claude_vote, claude_reason, deepseek_vote, "
|
||||
"deepseek_reason, gemini_vote, gemini_reason "
|
||||
"FROM halacha_panel_rounds WHERE halacha_id = ANY($1::uuid[]) "
|
||||
"ORDER BY halacha_id, round_ts DESC",
|
||||
ids,
|
||||
)
|
||||
by_id = {
|
||||
str(r["halacha_id"]): {
|
||||
"question": r["question"],
|
||||
"verdict": r["verdict"],
|
||||
"applied_action": r["applied_action"],
|
||||
"round_ts": r["round_ts"].isoformat() if r["round_ts"] else None,
|
||||
"judges": [
|
||||
{"model": "claude", "vote": r["claude_vote"], "reason": r["claude_reason"]},
|
||||
{"model": "deepseek", "vote": r["deepseek_vote"], "reason": r["deepseek_reason"]},
|
||||
{"model": "gemini", "vote": r["gemini_vote"], "reason": r["gemini_reason"]},
|
||||
],
|
||||
}
|
||||
for r in rows
|
||||
}
|
||||
for d in out:
|
||||
pr = by_id.get(str(d["id"]))
|
||||
if pr:
|
||||
d["panel_round"] = pr
|
||||
|
||||
|
||||
async def _annotate_clusters(pool, out: list[dict]) -> None:
|
||||
"""Add cluster_id + cluster_size to each row (#84.2), display-only.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user