feat(mcp): FU-14 GAP-48 פרוסה 2 — envelope אחיד ל-11 משפחות-כלים

המשך מיגרציית INV-TOOL1 מעבר למשפחת-החיפוש (#71). הומרו ל-{status,data,message}:
precedent_library, citations, internal_decisions, missing_precedents,
training_enrichment, precedents, legal_arguments, cases, documents, workflow
(~55 כלים). בוטלו 5 עותקי _ok/_err משוכפלים (alias ל-tools/envelope.py — SSoT, G2).

עיקרון: envelope-status = הצלחת-הקריאה-לכלי; תוצאה-עסקית (idempotent_existing,
noop, completed...) נשמרת בתוך data. err רק לכשל אמיתי (not-found/invalid/exception).

תאימות-API: צרכני web/app.py של cases/workflow/precedents חוּוטו דרך
envelope_unwrap + בדיקת status=="error"→4xx — תשובת ה-HTTP זהה, web-ui לא מושפע.
(documents/legal_arguments/citations/... אינם נצרכים מ-app.py — agent-only.)

בדיקות: 182/182 עוברים (test_corpus_constraints עודכן לחוזה החדש).
נותר: משפחת drafting (מסלול הפקת-ההחלטה) בפרוסה נפרדת עם שער טסט-ייצוא.

Invariants: מקדם INV-TOOL1 + G2 (SSoT, ביטול כפילות). מתועד ב-X9 + gap-audit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 17:41:39 +00:00
parent 24f9ceb164
commit 79b9c37301
14 changed files with 168 additions and 240 deletions

View File

@@ -2,10 +2,10 @@
from __future__ import annotations
import json
from uuid import UUID
from legal_mcp.services import argument_aggregator, db
from legal_mcp.tools.envelope import empty, err, ok # GAP-48: SSoT envelope
async def aggregate_claims_to_arguments(
@@ -20,17 +20,14 @@ async def aggregate_claims_to_arguments(
"""
case = await db.get_case_by_number(case_number)
if not case:
return json.dumps(
{"status": "error", "message": f"תיק {case_number} לא נמצא."},
ensure_ascii=False, indent=2,
)
return err(f"תיק {case_number} לא נמצא.")
case_id = UUID(case["id"])
result = await argument_aggregator.aggregate_claims_to_arguments(
case_id, force=force,
)
result["case_number"] = case_number
return json.dumps(result, ensure_ascii=False, indent=2, default=str)
return ok(result)
async def get_legal_arguments(
@@ -46,21 +43,16 @@ async def get_legal_arguments(
"""
case = await db.get_case_by_number(case_number)
if not case:
return json.dumps(
{"status": "error", "message": f"תיק {case_number} לא נמצא."},
ensure_ascii=False, indent=2,
)
return err(f"תיק {case_number} לא נמצא.")
case_id = UUID(case["id"])
args = await argument_aggregator.get_legal_arguments(case_id, party=party)
if not args:
return json.dumps({
"status": "empty",
"case_number": case_number,
"message": "לא נמצאו טיעונים מאוגדים. הרץ aggregate_claims_to_arguments תחילה.",
"arguments": [],
}, ensure_ascii=False, indent=2)
return empty(
"לא נמצאו טיעונים מאוגדים. הרץ aggregate_claims_to_arguments תחילה.",
data={"case_number": case_number, "arguments": []},
)
# Group by party for nicer display.
party_he = {
@@ -75,9 +67,8 @@ async def get_legal_arguments(
label = party_he.get(a["party"], a["party"])
by_party.setdefault(label, []).append(a)
return json.dumps({
"status": "ok",
return ok({
"case_number": case_number,
"total": len(args),
"by_party": by_party,
}, ensure_ascii=False, indent=2, default=str)
})