feat(mcp): FU-14 GAP-48 פרוסה 1 — envelope אחיד (SSoT) + משפחת-חיפוש
INV-TOOL1: כלי-ה-MCP החזירו 3 מוסכמות סותרות (raw payload / {error} /
{status,message} אד-הוק) + 5 עותקי _ok/_err משוכפלים. נוצר tools/envelope.py
כמקור-אמת יחיד: ok/empty/err → {status,data,message}, כש-status מבחין
מפורשות הצלחה/ריק/שגיאה.
פרוסה 1 ממירה את משפחת-החיפוש (search_decisions, search_case_documents,
find_similar_cases, search_internal_decisions). web/app.py מפרק את המעטפת
דרך envelope_unwrap כדי לשמר את חוזה-ה-UI↔API (X6) ללא-שינוי — תשובת ה-HTTP
זהה (list על hits, {"message"} על ריק/שגיאה). טסט test_search_domain_scope
עודכן לחוזה החדש (5/5 עוברים).
החלטה: הדרגתי לפי-משפחה ולא big-bang. מפת-צרכנים: server.py pass-through,
web-ui מבודד (/api/*), רק 17 כלים נצרכים ישירות מ-app.py → סיכון מינימלי
לסוכנים החיים. ~73 כלים נותרו לפרוסות הבאות.
Invariants: מקדם INV-TOOL1 (envelope עקבי) + G2 (SSoT, ביטול כפילות _ok/_err).
לא נוגע ב-G1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -84,7 +84,8 @@ def test_explicit_practice_area_used(patched: dict) -> None:
|
||||
# explicit value must not trigger a case lookup
|
||||
assert patched["cases"] == {}
|
||||
# ran -> JSON result, not an error string
|
||||
assert json.loads(out)[0]["content"] == "hit"
|
||||
assert json.loads(out)["status"] == "ok"
|
||||
assert json.loads(out)["data"][0]["content"] == "hit"
|
||||
|
||||
|
||||
def test_case_practice_area_used(patched: dict) -> None:
|
||||
@@ -98,7 +99,8 @@ def test_case_practice_area_used(patched: dict) -> None:
|
||||
)
|
||||
assert len(patched["hybrid"]) == 1
|
||||
assert patched["hybrid"][0]["practice_area"] == "betterment_levy"
|
||||
assert json.loads(out)[0]["content"] == "hit"
|
||||
assert json.loads(out)["status"] == "ok"
|
||||
assert json.loads(out)["data"][0]["content"] == "hit"
|
||||
|
||||
|
||||
def test_case_empty_practice_area_derived_from_prefix(patched: dict) -> None:
|
||||
@@ -113,7 +115,8 @@ def test_case_empty_practice_area_derived_from_prefix(patched: dict) -> None:
|
||||
)
|
||||
assert len(patched["hybrid"]) == 1
|
||||
assert patched["hybrid"][0]["practice_area"] == "betterment_levy"
|
||||
assert json.loads(out)[0]["content"] == "hit"
|
||||
assert json.loads(out)["status"] == "ok"
|
||||
assert json.loads(out)["data"][0]["content"] == "hit"
|
||||
|
||||
|
||||
def test_case_undeterminable_is_blocked(patched: dict) -> None:
|
||||
@@ -128,11 +131,10 @@ def test_case_undeterminable_is_blocked(patched: dict) -> None:
|
||||
)
|
||||
# hybrid search must NOT have been called
|
||||
assert patched["hybrid"] == []
|
||||
# returns a Hebrew error string, not JSON
|
||||
assert out.startswith("שגיאה")
|
||||
assert "7777/25" in out
|
||||
with pytest.raises(json.JSONDecodeError):
|
||||
json.loads(out)
|
||||
# GAP-48: returns the {status,data,message} envelope with status="error"
|
||||
parsed = json.loads(out)
|
||||
assert parsed["status"] == "error"
|
||||
assert "7777/25" in parsed["message"]
|
||||
|
||||
|
||||
def test_no_case_no_practice_area_proceeds(patched: dict) -> None:
|
||||
@@ -141,4 +143,5 @@ def test_no_case_no_practice_area_proceeds(patched: dict) -> None:
|
||||
assert len(patched["hybrid"]) == 1
|
||||
assert patched["hybrid"][0]["practice_area"] is None
|
||||
assert patched["cases"] == {}
|
||||
assert json.loads(out)[0]["content"] == "hit"
|
||||
assert json.loads(out)["status"] == "ok"
|
||||
assert json.loads(out)["data"][0]["content"] == "hit"
|
||||
|
||||
Reference in New Issue
Block a user