feat(mcp): FU-14 GAP-45 — extraction_status (חשיפת תור-החילוץ הסמוי) #64
@@ -63,8 +63,8 @@ Kleppmann *DDIA* (idempotence) · IETF — *Idempotency-Key header* draft (https
|
||||
**כלל:** לכל כלי-חילוץ שכותב ל-DB יש **כלי-קריאה (get) מקביל**, והפלט **נשמר durably** (לא מוחזר-ונאבד).
|
||||
מופע של [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) (מקור-אמת נגיש). **פרויקטלי-תפעולי.**
|
||||
**מקור-סמכות:** דפוס `extract_claims`↔`get_claims`, `aggregate`↔`get_legal_arguments` ב-[server.py](../../mcp-server/src/legal_mcp/server.py).
|
||||
**אכיפה:** לכל extract — get מקביל. **GAP-44 ✅ נסגר (2026-06-06):** נוסף `get_appraiser_facts` (קורא `list_appraiser_facts`+`detect_appraiser_conflicts`, ללא חילוץ-מחדש). נותר: תור-חילוץ סמוי (GAP-45).
|
||||
**הפרה ידועה:** תור-חילוץ סמוי ([gap-audit GAP-45](gap-audit.md)).
|
||||
**אכיפה:** לכל extract — get מקביל. **GAP-44 ✅ + GAP-45 ✅ נסגרו (2026-06-06):** נוסף `get_appraiser_facts` (קורא `list_appraiser_facts`+`detect_appraiser_conflicts`, ללא חילוץ-מחדש); נוסף `extraction_status` שחושף את עומק תור-החילוץ (metadata/halacha) + גיל הבקשה הוותיקה — read-only.
|
||||
**הפרה ידועה:** —
|
||||
|
||||
### INV-TOOL5: limit-caps על כל כלי-רשימה/חיפוש
|
||||
**כלל:** לכל כלי שמחזיר רשימה יש **תקרת-limit נאכפת** (הגנה מפני עומס/DoS); pagination היכן שרלוונטי. **הנדסי.**
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
- **מכסה:** GAP-44,45,47..54 · **invariants:** INV-TOOL1–TOOL5 · **effort:** L · **תלויות:** FU-1
|
||||
- **סוג:** code — envelope אחיד, מיזוג חיפוש/בלוקים, idempotency, limit-caps, get-symmetry, set_outcome SSoT
|
||||
- **סטטוס חלקי (פרוסה 1, 2026-06-06):** ✅ **GAP-44** — נוסף `get_appraiser_facts` (ה-get המקביל ל-extract, INV-TOOL4); ✅ **GAP-53** — נוסף `_clamp_limit` (תקרה 200, INV-TOOL5) על ~13 כלי list/search + הוספת limit ל-`list_chair_feedback` (שהיה ללא תקרה).
|
||||
- **סטטוס חלקי (פרוסה 2, 2026-06-06):** ✅ **GAP-52** (INV-TOOL3 idempotency) — `case_create`/`precedent_attach`/`document_upload` מחזירים קיים במקום כפילות (בדיקת-מפתח ברמת-אפליקציה; document_upload לפי SHA-256 → מדלג OCR/embed כפול). נותר: GAP-45 (status-tool), GAP-51 (set_outcome enum SSoT — דורש הכרעת-domain), GAP-48 (envelope), GAP-49/50 (מיזוג+rename — שובר).
|
||||
- **סטטוס חלקי (פרוסה 2, 2026-06-06):** ✅ **GAP-52** (INV-TOOL3 idempotency) — `case_create`/`precedent_attach`/`document_upload` מחזירים קיים במקום כפילות (בדיקת-מפתח ברמת-אפליקציה; document_upload לפי SHA-256 → מדלג OCR/embed כפול); ✅ **GAP-45** (INV-TOOL4 visibility) — נוסף `extraction_status` שחושף עומק תור-החילוץ (metadata/halacha) + גיל הבקשה הוותיקה. נותר: GAP-51 (set_outcome enum SSoT — דורש הכרעת-domain), GAP-48 (envelope), GAP-49/50 (מיזוג+rename — שובר).
|
||||
|
||||
### FU-15 — deploy/env/secrets
|
||||
- **מכסה:** GAP-55..62 · **invariants:** INV-ENV1–ENV5 · **effort:** M · **תלויות:** —
|
||||
|
||||
@@ -290,10 +290,16 @@ async def style_corpus_pending_enrichment(limit: int = 50) -> str:
|
||||
return await train_tools.list_corpus_pending_enrichment(_clamp_limit(limit))
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def extraction_status() -> str:
|
||||
"""סטטוס תור-החילוץ — כמה פסיקות ממתינות לחילוץ metadata/halacha + גיל הבקשה הוותיקה. read-only (חושף את התור ש-precedent_process_pending מרוקן)."""
|
||||
return await plib.extraction_status()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def precedent_process_pending(kind: str = "metadata", limit: int = 20) -> str:
|
||||
"""ריקון תור בקשות חילוץ שנשלחו מ-UI. kind: 'metadata' או 'halacha'. מריץ extractor מקומית עם CLI על כל פריט בתור, ומנקה את הסימון אחרי הצלחה."""
|
||||
return await plib.precedent_process_pending(kind, limit)
|
||||
return await plib.precedent_process_pending(kind, _clamp_limit(limit))
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
|
||||
@@ -4332,6 +4332,31 @@ async def list_pending_extraction_requests(
|
||||
return out
|
||||
|
||||
|
||||
async def extraction_queue_status() -> dict:
|
||||
"""Pending-extraction queue depth per kind (INV-TOOL4 visibility / GAP-45).
|
||||
|
||||
Surfaces the otherwise-hidden queue that ``process_pending_extractions``
|
||||
drains: how many case_law rows still carry a metadata/halacha extraction
|
||||
request, and the age of the oldest one. Read-only — does not drain.
|
||||
"""
|
||||
pool = await get_pool()
|
||||
async with pool.acquire() as conn:
|
||||
meta = await conn.fetchrow(
|
||||
"SELECT COUNT(*) AS n, MIN(metadata_extraction_requested_at) AS oldest "
|
||||
"FROM case_law WHERE metadata_extraction_requested_at IS NOT NULL"
|
||||
)
|
||||
hal = await conn.fetchrow(
|
||||
"SELECT COUNT(*) AS n, MIN(halacha_extraction_requested_at) AS oldest "
|
||||
"FROM case_law WHERE halacha_extraction_requested_at IS NOT NULL"
|
||||
)
|
||||
|
||||
def _fmt(r: dict) -> dict:
|
||||
oldest = r["oldest"]
|
||||
return {"pending": r["n"], "oldest_request": oldest.isoformat() if oldest else None}
|
||||
|
||||
return {"metadata": _fmt(meta), "halacha": _fmt(hal)}
|
||||
|
||||
|
||||
async def clear_extraction_request(
|
||||
case_law_id: UUID, kind: str = "metadata",
|
||||
) -> None:
|
||||
|
||||
@@ -233,6 +233,19 @@ async def precedent_reindex(case_law_id: str) -> str:
|
||||
return _ok(result)
|
||||
|
||||
|
||||
async def extraction_status() -> str:
|
||||
"""סטטוס תור-החילוץ — כמה פסיקות ממתינות לחילוץ metadata/halacha (INV-TOOL4 / GAP-45).
|
||||
|
||||
חושף את התור ש-precedent_process_pending מרוקן: עומק-תור + גיל הבקשה
|
||||
הוותיקה ביותר לכל סוג. read-only — אינו מרוקן את התור.
|
||||
"""
|
||||
try:
|
||||
status = await db.extraction_queue_status()
|
||||
except Exception as e:
|
||||
return _err(str(e))
|
||||
return _ok(status)
|
||||
|
||||
|
||||
async def precedent_process_pending(kind: str = "metadata", limit: int = 20) -> str:
|
||||
"""ריקון תור בקשות חילוץ שנערמו ע"י כפתורי ה-UI. kind: 'metadata' או 'halacha'.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user