feat(mcp): FU-14 GAP-52 — idempotency על case_create/precedent_attach/document_upload #63

Merged
chaim merged 1 commits from fix/fu14-gap52-idempotency into main 2026-06-06 14:53:14 +00:00
Owner

מה ולמה

GAP-52 מתוך FU-14 (פרוסה 2). INV-TOOL3 — כלי-מוטציה idempotent על מפתח דטרמיניסטי: קריאה חוזרת לא יוצרת כפילות.

Invariants — הצהרה

  • GAP-52 / INV-TOOL3 (G3) — idempotency על מפתח דטרמיניסטי בכלי-מוטציה.

מה תוקן

כלי מפתח דטרמיניסטי התנהגות חדשה
case_create case_number (כבר UNIQUE ב-schema) מחזיר את התיק הקיים במקום unique-violation
precedent_attach (case_id, section_id, citation, quote) צירוף חוזר של אותו ציטוט→אותו סעיף מחזיר קיים
document_upload (case_id, SHA-256 של הקובץ) העלאה חוזרת של אותו קובץ מחזירה קיים + מדלגת על copy+OCR+embed

לכל פלט idempotent נוסף idempotent_existing: true להבחנה.

בחירת מימוש — בדיקת-מפתח ברמת-אפליקציה (לא UNIQUE-constraint)

הספ אומר "upsert/ON CONFLICT", אבל הוספת UNIQUE-constraint על טבלה עם נתונים-כפולים legacy קיימים הייתה שוברת את ה-startup (יצירת ה-index נכשלת). לכן נבחרה בדיקת-מפתח (SELECT-לפני-INSERT) — מקיימת את אותו invariant בלי מיגרציה הרסנית ובלי סיכון startup. document_upload קיבל עמודה תוספתית documents.content_hash (DEFAULT '', ללא unique; hash ריק=legacy → לעולם לא מותאם ככפילות).

צ'קליסט — פרוטוקול כתיבת-קוד

  • קראתי 00-constitution.md + X9 (INV-TOOL3) לפני הכתיבה
  • אין מסלול מקביל (G2); נשען על db helpers קיימים + helper יחיד חדש
  • אין בליעה שקטה — מחזיר קיים מפורשות עם marker, לא בולע
  • בדקתי מול gap-audit — GAP-52, עדכנתי סטטוס
  • data-migration? רק עמודה תוספתית content_hash DEFAULT '' (ADD COLUMN IF NOT EXISTS) — ללא backfill, ללא unique. אפס סיכון על נתונים קיימים
  • py_compile עבר על 4 קבצי הקוד

אימות

- py_compile: cases.py, precedents.py, documents.py, db.py → ✅
- idempotency markers במקום בכל 3 הכלים
- get_document_by_hash: מתעלם מ-hash ריק (legacy)

⚠️ activation: שינויי mcp-server/ נכנסים לתוקף ב-restart של ה-MCP server המקומי + ה-ALTER TABLE רץ ב-startup. לא ארסטרט בזמן החילוץ הפעיל. השינוי נוגע ב-cases/documents/case_precedents — לא בטבלאות החילוץ (case_law/halachot), אז אין התנגשות.

🤖 Generated with Claude Code

## מה ולמה GAP-52 מתוך FU-14 (פרוסה 2). **INV-TOOL3** — כלי-מוטציה idempotent על מפתח דטרמיניסטי: קריאה חוזרת לא יוצרת כפילות. ## Invariants — הצהרה - **GAP-52 / INV-TOOL3 (G3)** — idempotency על מפתח דטרמיניסטי בכלי-מוטציה. ## מה תוקן | כלי | מפתח דטרמיניסטי | התנהגות חדשה | |-----|------------------|---------------| | `case_create` | `case_number` (כבר UNIQUE ב-schema) | מחזיר את התיק הקיים במקום unique-violation | | `precedent_attach` | `(case_id, section_id, citation, quote)` | צירוף חוזר של אותו ציטוט→אותו סעיף מחזיר קיים | | `document_upload` | `(case_id, SHA-256 של הקובץ)` | העלאה חוזרת של אותו קובץ מחזירה קיים + **מדלגת על copy+OCR+embed** | לכל פלט idempotent נוסף `idempotent_existing: true` להבחנה. ## בחירת מימוש — בדיקת-מפתח ברמת-אפליקציה (לא UNIQUE-constraint) הספ אומר "upsert/ON CONFLICT", אבל הוספת UNIQUE-constraint על טבלה עם **נתונים-כפולים legacy קיימים** הייתה **שוברת את ה-startup** (יצירת ה-index נכשלת). לכן נבחרה בדיקת-מפתח (SELECT-לפני-INSERT) — מקיימת את אותו invariant בלי מיגרציה הרסנית ובלי סיכון startup. `document_upload` קיבל עמודה תוספתית `documents.content_hash` (`DEFAULT ''`, ללא unique; hash ריק=legacy → לעולם לא מותאם ככפילות). ## צ'קליסט — פרוטוקול כתיבת-קוד - [x] קראתי `00-constitution.md` + `X9` (INV-TOOL3) לפני הכתיבה - [x] אין מסלול מקביל (G2); נשען על db helpers קיימים + helper יחיד חדש - [x] אין בליעה שקטה — מחזיר קיים מפורשות עם marker, לא בולע - [x] בדקתי מול gap-audit — GAP-52, עדכנתי סטטוס - [x] **data-migration?** רק עמודה תוספתית `content_hash DEFAULT ''` (ADD COLUMN IF NOT EXISTS) — ללא backfill, ללא unique. אפס סיכון על נתונים קיימים - [x] py_compile עבר על 4 קבצי הקוד ## אימות ``` - py_compile: cases.py, precedents.py, documents.py, db.py → ✅ - idempotency markers במקום בכל 3 הכלים - get_document_by_hash: מתעלם מ-hash ריק (legacy) ``` > ⚠️ activation: שינויי `mcp-server/` נכנסים לתוקף ב-restart של ה-MCP server המקומי + ה-ALTER TABLE רץ ב-startup. **לא ארסטרט** בזמן החילוץ הפעיל. השינוי נוגע ב-cases/documents/case_precedents — **לא** בטבלאות החילוץ (case_law/halachot), אז אין התנגשות. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
chaim added 1 commit 2026-06-06 14:53:09 +00:00
INV-TOOL3 (idempotency על מפתח דטרמיניסטי). כל שלושת הכלים מחזירים את הרשומה
הקיימת במקום ליצור כפילות:

- case_create — מפתח case_number (כבר UNIQUE ב-schema): מחזיר את התיק הקיים
  במקום unique-violation.
- precedent_attach — מפתח (case_id, section_id, citation, quote): צירוף חוזר
  של אותו ציטוט לאותו סעיף מחזיר את הקיים.
- document_upload — מפתח (case_id, SHA-256 של בייטי הקובץ): העלאה חוזרת של אותו
  קובץ מחזירה את המסמך הקיים ו**מדלגת על copy+OCR+embed** (החלק היקר). נוספה
  עמודת documents.content_hash (תוספתי, DEFAULT '') + get_document_by_hash.

נבחרה בדיקת-מפתח ברמת-אפליקציה (SELECT-לפני-INSERT) ולא UNIQUE-constraint —
כדי לא לשבור startup אם קיימים נתונים-כפולים legacy. אין מיגרציה הרסנית.

עודכנו docs/spec/X9 (INV-TOOL3 ) ו-gap-audit (GAP-52 , פרוסה 2).
py_compile עבר על 4 קבצי הקוד. אימות runtime (restart MCP server) נדחה עד
שהחילוץ הפעיל יסתיים.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
chaim merged commit 0d0f5aa8e9 into main 2026-06-06 14:53:14 +00:00
chaim deleted branch fix/fu14-gap52-idempotency 2026-06-06 14:53:14 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/legal-ai#63