feat(mcp): FU-14 GAP-51 — איחוד אוצר-המילים של תוצאת-תיק (set_outcome SSoT)
הכרעת-יו"ר: קנוני = 3 תוצאות אמיתיות (rejection/partial_acceptance/full_acceptance); betterment_levy יוצא מהיותו "תוצאה" ועובר ל-override לפי practice_area. + עקרון "אנגלית-ב-DB, עברית-ב-UI": מפת-תוויות SSoT אחת. lessons.py: - VALID_OUTCOMES = 3 (הוסר betterment_levy). - OUTCOME_LABELS_HE (SSoT לתצוגה) + LEGACY_OUTCOME_MAP + canonical_outcome(). - PRACTICE_AREA_OVERRIDES["betterment_levy"] מרכז את כל ה-guidance שהיה מפתוח כ-outcome (golden_ratios/opening/summary/discussion/template). - get_lessons_for_outcome(outcome, practice_area) + format_ratios_comment(..., practice_area) מחילים override + מנרמלים legacy. block_writer.py: STRUCTURE_GUIDANCE קנוני + תווית מ-OUTCOME_LABELS_HE + override betterment. workflow.set_outcome: קנוני 3 + מיפוי-legacy סלחני; תווית מ-SSoT. drafting.py: טבלת יחסי-זהב + get_decision_template מודעי-practice_area (override). web-ui case.ts: הסרת betterment_levy מ-expectedOutcomes (הוא practice_area). server.py: docstrings קנוניים. מיגרציה: migrate_gap51_outcomes.py — 9 שורות נורמלו (rejected→rejection וכו'), גיבוי ב-data/audit/. הקוד canonicalize בקריאה ⇒ backward-compatible גם בלי מיגרציה. אומת: py_compile (5 קבצים) + בדיקות-יחידה offline (override/legacy/labels) + אימות-DB. עודכנו X9 §3 + gap-audit (GAP-51 ✅). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,11 @@ from legal_mcp.services.lessons import (
|
||||
GOLDEN_RATIOS,
|
||||
OPENING_STRATEGIES,
|
||||
PARAGRAPH_LENGTHS,
|
||||
PRACTICE_AREA_OVERRIDES,
|
||||
SUMMARY_STRATEGIES,
|
||||
TRANSITION_PHRASES,
|
||||
VALID_OUTCOMES,
|
||||
canonical_outcome,
|
||||
format_ratios_comment,
|
||||
get_lessons_for_outcome,
|
||||
)
|
||||
@@ -156,6 +158,15 @@ async def get_style_guide() -> str:
|
||||
f"| {r['discussion'][0]}-{r['discussion'][1]}% "
|
||||
f"| {r['summary'][0]}-{r['summary'][1]}% |\n"
|
||||
)
|
||||
# GAP-51: betterment_levy is a practice_area override (applied on top of the outcome), not an outcome.
|
||||
_bl = PRACTICE_AREA_OVERRIDES["betterment_levy"]["golden_ratios"]
|
||||
result += (
|
||||
f"| {outcome_labels['betterment_levy']} (override לפי תחום) "
|
||||
f"| {_bl['background'][0]}-{_bl['background'][1]}% "
|
||||
f"| {_bl['claims'][0]}-{_bl['claims'][1]}% "
|
||||
f"| {_bl['discussion'][0]}-{_bl['discussion'][1]}% "
|
||||
f"| {_bl['summary'][0]}-{_bl['summary'][1]}% |\n"
|
||||
)
|
||||
result += "\n"
|
||||
|
||||
# Opening and summary strategies
|
||||
@@ -167,6 +178,13 @@ async def get_style_guide() -> str:
|
||||
result += f"- **פתיחה:** {opening['description']} ({opening['paragraphs'][0]}-{opening['paragraphs'][1]} פסקאות)\n"
|
||||
result += f"- **סיכום ({summary['heading']}):** {summary['description']}\n\n"
|
||||
|
||||
# GAP-51: betterment_levy override (practice_area, applied over the outcome)
|
||||
_bo = PRACTICE_AREA_OVERRIDES["betterment_levy"]
|
||||
_op, _sm = _bo["opening_strategy"], _bo["summary_strategy"]
|
||||
result += f"### {outcome_labels['betterment_levy']} (override לפי תחום)\n"
|
||||
result += f"- **פתיחה:** {_op['description']} ({_op['paragraphs'][0]}-{_op['paragraphs'][1]} פסקאות)\n"
|
||||
result += f"- **סיכום ({_sm['heading']}):** {_sm['description']}\n\n"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -319,7 +337,9 @@ async def get_decision_template(case_number: str) -> str:
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
expected_outcome = case.get("expected_outcome", "")
|
||||
# GAP-51: canonicalize outcome + apply betterment_levy practice_area override.
|
||||
expected_outcome = canonical_outcome(case.get("expected_outcome", ""))
|
||||
practice_area = case.get("practice_area", "")
|
||||
|
||||
format_args = dict(
|
||||
case_number=case["case_number"],
|
||||
@@ -332,19 +352,24 @@ async def get_decision_template(case_number: str) -> str:
|
||||
|
||||
# Use outcome-specific template if available
|
||||
if expected_outcome and expected_outcome in DECISION_TEMPLATES:
|
||||
# Add ratio comments
|
||||
format_args["ratios_background"] = format_ratios_comment(expected_outcome, "background")
|
||||
format_args["ratios_claims"] = format_ratios_comment(expected_outcome, "claims")
|
||||
format_args["ratios_discussion"] = format_ratios_comment(expected_outcome, "discussion")
|
||||
format_args["ratios_summary"] = format_ratios_comment(expected_outcome, "summary")
|
||||
# Add ratio comments (practice_area-aware)
|
||||
format_args["ratios_background"] = format_ratios_comment(expected_outcome, "background", practice_area)
|
||||
format_args["ratios_claims"] = format_ratios_comment(expected_outcome, "claims", practice_area)
|
||||
format_args["ratios_discussion"] = format_ratios_comment(expected_outcome, "discussion", practice_area)
|
||||
format_args["ratios_summary"] = format_ratios_comment(expected_outcome, "summary", practice_area)
|
||||
|
||||
template = DECISION_TEMPLATES[expected_outcome].format(**format_args)
|
||||
# betterment_levy practice_area supplies its own template; else use the outcome's.
|
||||
override = PRACTICE_AREA_OVERRIDES.get(practice_area, {})
|
||||
template_src = override.get("decision_template") or DECISION_TEMPLATES[expected_outcome]
|
||||
template = template_src.format(**format_args)
|
||||
|
||||
# Add guidance header
|
||||
opening = OPENING_STRATEGIES[expected_outcome]
|
||||
summary = SUMMARY_STRATEGIES[expected_outcome]
|
||||
# Add guidance header (override-aware via get_lessons_for_outcome)
|
||||
lessons_o = get_lessons_for_outcome(expected_outcome, practice_area)
|
||||
opening = lessons_o["opening_strategy"]
|
||||
summary = lessons_o["summary_strategy"]
|
||||
header = (
|
||||
f"<!-- תבנית מותאמת ל: {expected_outcome} -->\n"
|
||||
f"<!-- תבנית מותאמת ל: {expected_outcome}"
|
||||
f"{' / ' + practice_area if practice_area in PRACTICE_AREA_OVERRIDES else ''} -->\n"
|
||||
f"<!-- פתיחת דיון: {opening['description']} -->\n"
|
||||
f"<!-- סיכום: {summary['description']} -->\n\n"
|
||||
)
|
||||
|
||||
@@ -7,6 +7,11 @@ import logging
|
||||
from uuid import UUID
|
||||
|
||||
from legal_mcp.services import db
|
||||
from legal_mcp.services.lessons import (
|
||||
OUTCOME_LABELS_HE,
|
||||
VALID_OUTCOMES,
|
||||
canonical_outcome,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -151,7 +156,8 @@ async def set_outcome(
|
||||
|
||||
Args:
|
||||
case_number: מספר תיק הערר
|
||||
outcome: תוצאה — rejected (דחייה), accepted (קבלה), partial (קבלה חלקית)
|
||||
outcome: תוצאה — rejection (דחייה) / partial_acceptance (קבלה חלקית) /
|
||||
full_acceptance (קבלה מלאה). ערכי-legacy (rejected/accepted/partial) ממופים אוטומטית.
|
||||
reasoning: נימוק (אופציונלי). אם ריק — מפעיל סיעור מוחות.
|
||||
"""
|
||||
from legal_mcp.services import brainstorm
|
||||
@@ -160,9 +166,10 @@ async def set_outcome(
|
||||
if not case:
|
||||
return f"תיק {case_number} לא נמצא."
|
||||
|
||||
valid_outcomes = ("rejected", "accepted", "partial")
|
||||
if outcome not in valid_outcomes:
|
||||
return f"תוצאה לא תקינה. אפשרויות: {', '.join(valid_outcomes)}"
|
||||
# GAP-51: accept legacy vocabulary (rejected/accepted/partial), store canonical.
|
||||
outcome = canonical_outcome(outcome)
|
||||
if outcome not in VALID_OUTCOMES:
|
||||
return f"תוצאה לא תקינה. אפשרויות: {', '.join(VALID_OUTCOMES)}"
|
||||
|
||||
case_id = UUID(case["id"])
|
||||
|
||||
@@ -187,7 +194,7 @@ async def set_outcome(
|
||||
# Update case status
|
||||
await db.update_case(case_id, status="in_progress", expected_outcome=outcome)
|
||||
|
||||
outcome_hebrew = {"rejected": "דחייה", "accepted": "קבלה", "partial": "קבלה חלקית"}.get(outcome, outcome)
|
||||
outcome_hebrew = OUTCOME_LABELS_HE.get(outcome, outcome)
|
||||
|
||||
result = {
|
||||
"decision_id": decision["id"],
|
||||
|
||||
Reference in New Issue
Block a user