fix(learning): chair_name במקור — סופי-ועדה תמיד נכנס לקורפוס-הפסיקה (TaskMaster #134)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
הבאג: שלב-הלמידה (ingest_final_version → ingest_internal_decision) מוסיף כל סופי כתקדים ציטוטי ב-case_law (source_kind=internal_committee), אך נכשל בשקט (non-fatal warning) כש-cases.chair_name ריק — בגלל constraint case_law_internal_chair_check. כך סופיים של 1194/1200/8070 לא נכנסו לקורפוס-הפסיקה. שורש: (1) chair_name לא נקבע בפתיחת תיק; (2) מסלול-ה-MCP העביר chair גולמי בעוד מסלול-ה-UI (web/) כבר פתר אותו דטרמיניסטית — **מסלולים מקבילים מתפצלים (הפרת INV-G2)**; (3) הכשל נבלע (נגד §6). תיקון-שורש (3 שכבות): 1. **SoT יחיד (INV-G2):** `config.committee_chair_for_case` — המקום היחיד שגם web/app.py וגם tools/workflow.py + db.create_case גוזרים ממנו chair (לפי תחילית מספר-התיק; override ל-env). web/ אחוד אליו (הוסרה הכפילות). 2. **נרמול-במקור (INV-G1):** `db.create_case` קובע chair_name תמיד לא-ריק; `cases.case_create` חושף param. `ingest_final_version` גוזר chair מה-SoT במקום הערך הגולמי → ה-constraint לא נופל. 3. **נראות (§6/feedback_silent_swallow):** כשל-העתק מוחזר ב-result (`internal_corpus_error`) ו-`final_learning_pipeline` מדפיס אזהרה — לא נבלע. backfill ל-11 תיקים עם chair ריק. `audit_corpus_integrity`: נוספו CHECK_D (תיקים מוכרעים ללא chair) + CHECK_E (סופי-final חסר מקורפוס-הפסיקה) — שניהם 0 כעת. invariants: מקיים INV-G1 (נרמול בכתיבה), INV-G2 (מסלול-יחיד, אוחד web↔MCP), §6 (אין בליעה שקטה). בדיקות: py_compile + 14 pytest (chair_seed_gate, audit_provenance) + integration של create_case (default+override) + הרצת ה-audit החי (A–E=0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
18
web/app.py
18
web/app.py
@@ -3492,20 +3492,10 @@ async def api_mark_final(case_number: str, filename: str):
|
||||
}
|
||||
|
||||
|
||||
# Our committee's standing chair — internal_committee rows REQUIRE a chair_name
|
||||
# (DB constraint case_law_internal_chair_check). Both CMP (1xxx) and CMPA (8/9xxx)
|
||||
# are currently chaired by Dafna Tamir; map by prefix so adding a chair later is local.
|
||||
COMMITTEE_CHAIR_BY_PREFIX = {"1": "דפנה תמיר", "8": "דפנה תמיר", "9": "דפנה תמיר"}
|
||||
COMMITTEE_CHAIR_DEFAULT = "דפנה תמיר"
|
||||
|
||||
|
||||
def _committee_chair_for_case(case: dict, case_number: str) -> str:
|
||||
"""Resolve the chair for one of OUR decisions deterministically (no LLM): the case's
|
||||
own chair_name, else the committee default by case-number prefix."""
|
||||
existing = (case.get("chair_name") or "").strip()
|
||||
if existing:
|
||||
return existing
|
||||
return COMMITTEE_CHAIR_BY_PREFIX.get(case_number[:1], COMMITTEE_CHAIR_DEFAULT)
|
||||
# Committee chair resolution lives in ONE place — config.committee_chair_for_case
|
||||
# (INV-G2: the FastAPI upload path and the MCP learning path must not drift into
|
||||
# parallel chair logic). Thin alias keeps call-sites here readable.
|
||||
_committee_chair_for_case = config.committee_chair_for_case
|
||||
|
||||
|
||||
def _party_name(parties) -> str:
|
||||
|
||||
Reference in New Issue
Block a user