Files
legal-ai/docs/spec/X7-paperclip-client-params.md
Chaim 37c56ff22a docs(spec): cycle-2 — 8 application-surface domains (X6–X10) + ui-audit + GAP-24..62/FU-9..15
Extends the system spec beyond the core pipeline to the 8 surfaces outside it:
- X6 UI↔API contract + design rules (INV-UI1..6)
- X7 Paperclip client & connection params (INV-INT4..8)
- X8 field-population & extraction provenance (INV-FP1..5)
- X9 MCP tool contract — 71 tools (INV-TOOL1..6)
- X10 deploy/env/secrets (INV-ENV1..5)
- ui-audit.md — page-by-page UI audit (13 pages)
- 02-data-model: derived-entity invariants (INV-DM4..6)
- X4-agents: tool-grant map + INV-AG3
- gap-audit: GAP-24..62 → FU-9..15; cycle-1 (FU-1..8b) marked done
- constitution §7 + README index (X1..X10)

Planning/spec artifacts only — no application code. All engineering invariants
backed by ≥3 sources; every finding carries verified file:line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 16:21:27 +00:00

13 KiB
Raw Blame History

X7 — לקוח-Paperclip ופרמטרי-חיבור (Paperclip Client & Connection Parameters)

קובץ-תחום זה כפוף ל-חוקת המערכת ומשלים את X3: בעוד X3 מתאר את זרימות-האינטגרציה (wakeup, ניתוב comments, webhook), קובץ זה הוא ה-deep-dive על שכבת-הלקוח והפרמטריםאיך legal-ai מדבר עם Paperclip בקוד (אילו לקוחות, אילו מסלולים), ועל כל הפרמטרים המחברים (מזהי-חברה/סוכן, env, מפתחות, plugin_state, גזירת company_id).

invariant פרויקטלי-תפעולי. ה-invariants כאן הם עובדות על איך מערכת זו בנויה — אין להן סמכות חיצונית; מקור-הסמכות = ה-runbooks והקוד (root CLAUDE.md, legal-ai/CLAUDE.md, web/paperclip_api.py, web/paperclip_client.py). כל invariant נקשר ל-G גלובלי שהוא משרת — כאן בעיקר G2 (מסלול קנוני יחיד) ו-G9 (עקיבוּת/audit), וכלל-ההנדסה "סימטריה" (חוקה §6).


1. מצב קיים — שני לקוחות מקבילים

ל-legal-ai יש שני לקוחות Paperclip שונים שחיים בו-זמנית, וזהו מקור-השורש לרוב הפערים כאן:

לקוח קובץ אופי מה מנהל
"current" (API) web/paperclip_api.py HTTP דרך pc_request + board API key webhooks יוצאים, wakeup חלקי
"legacy" (DB-ישיר) web/paperclip_client.py חיבור psql ישיר ל-DB של Paperclip + API projects, issues, comments, wakeup, queries

legal-ai/CLAUDE.md מתעד ש-paperclip_client.py הוא "legacy — השתמש ב-paperclip_api.py", אך בפועל ה-legacy עדיין מבצע את רוב העבודה הכבדה (יצירת תיקים/issues, comments, wakeup-ים), וחלקו דרך INSERT/SELECT ישיר ל-DB של Paperclip — מסלול-מקביל לעוקף את ה-API.

זוהי בדיוק התבנית ש-G2 אוסר: שני מסלולי-קוד מקבילים ליכולת אחת (גישה ל-Paperclip), שמתפצלים ועלולים לסטות.


2. הפרמטרים המחברים (Connection Parameters)

2א. משתני-סביבה

Var קורא ברירת-מחדל סוד?
PAPERCLIP_API_URL paperclip_api.py http://localhost:3100 לא
PAPERCLIP_BOARD_API_KEY paperclip_api.py / paperclip_client.py "" כן (board key long-lived, לא JWT)
PAPERCLIP_DB_URL paperclip_client.py:21, app.py:3789 postgresql://paperclip:paperclip@127.0.0.1:54329/paperclip כן — creds בתוך ברירת-המחדל
PAPERCLIP_COMPANY_ID app.py:3976 42a7acd0-... (CMP, hardcoded) לא
legalApiBaseUrl plugin (instance config) http://localhost:8085 לא

ראה גם X10-deploy-env-secrets.md — חוזה-ה-env המלא וטיפול-הסודות.

2ב. מזהים קשיחים בקוד (hardcoded) — סתירה ל-X3

paperclip_client.py:36-62 מכיל מזהי-חברה וסוכן קשיחים:

  • COMPANIES["licensing"] = "42a7acd0-..." (CMP), COMPANIES["betterment"] = "8639e837-..." (CMPA)
  • CEO/curator/analyst UUIDs לכל חברה (CMP CEO 752cebdd-..., וכו').
  • ה-plugin (worker.ts) מכיל CEO IDs קשיחים משלו.

זו סתירה ישירה ל-X3 §1א הקובע "מזהה-ה-CEO נגזר מ-$PAPERCLIP_COMPANY_ID, לעולם לא UUID hardcoded". הסתירה מתועדת כממצא (gap-audit GAP-26, וכן GAP-56 ב-X10).

2ג. plugin_state keys (חוזה הקישור Paperclip↔legal-ai)

scope_kind state_key ערך משמעות
issue legal-case-number מספר-תיק קישור issue→תיק
issue precedent-case-law-id case_law_id קישור issue→פסיקה לחילוץ
instance webhook-idem-{requestId} timestamp guard idempotency 5 דק' (inbound)

2ד. גזירת company_id — שתי דרכים שונות

  • app.py: נגזר מ-prefix מספר-התיק (1→licensing, 8/9→betterment) (X3 §1ג).
  • paperclip_client.py: מ-_FALLBACK_APPEAL_TYPE_TO_COMPANY (מיפוי tag→company) + lookup ב-DB.

שתי דרכי-גזירה לאותו ערך = drift פוטנציאלי (gap-audit GAP-27).


3. צד נכנס (Inbound) — הפלאגין

plugin-legal-ai/src/worker.ts (לא בריפו זה) קורא ל-legal-ai דרך legalApiBaseUrl. שלושה סוגי-משטח, שכולם חוזה-API שאינו מתועד היום ב-X6:

  • 16 כלי legal_* — עוטפים endpoints של /api/cases/..., /api/search, וכו'.
  • onWebhook — מקבל את ה-webhook היוצא (ראה X3 §1ג ו-INV-INT8 להלן).
  • 3 cron jobssync-case-status (כל 15 דק'), stale-case-reminder (יומי), weekly-feedback-analysis (שבועי).

4. Invariants של התחום

INV-INT4: לקוח-Paperclip קנוני יחיד — אין לקוח-מקביל ואין גישת-DB ישירה

כלל: כל גישה ל-Paperclip עוברת דרך לקוח-API קנוני יחיד (pc_request/pc.sh). אסור מסלול-מקביל — לא לקוח שני, ולא INSERT/SELECT/UPDATE ישיר ל-DB של Paperclip. נתונים נקראים/נכתבים דרך ה-API הרשמי בלבד; ה-DB של Paperclip הוא מקור-האמת של Paperclip, ו-legal-ai אינו מסלול-כתיבה מקביל אליו. מופע של G2 וכלל "סימטריה" (חוקה §6). מקור-סמכות: legal-ai/CLAUDE.md ("paperclip_client.py legacy — השתמש ב-paperclip_api.py"; "קריאות API — תמיד דרך helper"); X3 INV-INT3. (פרויקטלי-תפעולי — משרת G2.) אכיפה: איחוד שני הלקוחות ללקוח-API אחד; הסרת PAPERCLIP_DB_URL כמסלול-כתיבה. כיום אין אכיפה — שני הלקוחות דו-קיימים (יעד FU-9). הפרה ידועה: paperclip_client.pycreate_project/post_comment-fallback עושים INSERT ישיר ל-projects/issues/comments/plugin_state (gap-audit GAP-24, GAP-25).

INV-INT5: מזהי-חברה/סוכן מ-config — לא hardcoded בקוד

כלל: מזהי-החברה (CMP/CMPA) ומזהי-הסוכנים (CEO/curator/analyst) נגזרים מ-config (env/טבלת-מיפוי), לא קבועים בקוד. הוספת חברה/החלפת instance אינה דורשת שינוי-קוד. מופע של G2 (SSoT למיפוי) — מקור-אמת יחיד למיפוי. מקור-סמכות: X3 §1א ("לעולם לא UUID hardcoded"); X2-multi-company.md. (פרויקטלי-תפעולי — משרת G2.) אכיפה: טבלת-מיפוי/env יחידה; code-review. כיום אין אכיפה — UUIDs קשיחים. הפרה ידועה: paperclip_client.py:36-62 + app.py:3976 + plugin worker.ts — IDs קשיחים. סותר את X3 §1א (gap-audit GAP-26).

INV-INT6: גזירת company_id קנונית יחידה

כלל: ל-company_id יש מסלול-גזירה אחד מתוך מספר-התיק/סוג-הערר, במקום יחיד. אסור שתי לוגיקות-גזירה מקבילות (prefix מול fallback-map) שעלולות לסטות. מופע של G2. מקור-סמכות: X3 §1ג; X2-multi-company.md. (פרויקטלי-תפעולי.) אכיפה: פונקציית-גזירה יחידה משותפת ל-app.py ול-client.py (יעד FU-9). כיום אין. הפרה ידועה: prefix ב-app.py מול _FALLBACK_APPEAL_TYPE_TO_COMPANY ב-paperclip_client.py (gap-audit GAP-27).

INV-INT7: webhook יוצא — at-least-once + idempotency + ללא בליעה שקטה

כלל: ה-webhook היוצא (legal-ai→plugin) מספק at-least-once עם מפתח-idempotency יציב (event id), כך שמסירה-כפולה בטוחה בצד-המקבל; וכישלון-מסירה נרשם ומדווח (telemetry/health), לא נבלע בשקט. זהו invariant הנדסי (סמנטיקת-מסירה כללית), הקשור ל-G9 (עקיבוּת) ולכלל "אין בליעה שקטה" (חוקה §6). מקורות: Stripe — Webhooks / at-least-once delivery & idempotency (https://docs.stripe.com/webhooks) · Hookdeck — At-Least-Once vs Exactly-Once Webhook Delivery (https://hookdeck.com/webhooks/guides/webhook-delivery-guarantees) · Martin Kleppmann, DDIA (O'Reilly 2017, idempotence & exactly-once semantics) | סטטוס: verified אכיפה: event-id יציב + UNIQUE-dedup בצד-המקבל; ה-emitter רושם כישלון ל-telemetry (יעד). כיום: inbound יש guard 5 דק' (X3 §1ג); outbound אין idempotency, וה-emitter בולע שגיאות ב-logger.warning בלבד. הפרה ידועה: emit_*_webhook ב-paperclip_api.py — fire-and-forget, try/except שמתעד warning ולעולם לא raise, ללא event-id/dedup (gap-audit GAP-28).

INV-INT8: חוזה-אירועי-webhook מתוקען ומגורס

כלל: ל-webhook חוזה-אירוע מפורש ומגורסeventType מתוך קבוצה סגורה, סכמת-payload מתועדת לכל סוג, וגרסה. אין eventType חופשי ואין "ברירת-מחדל שקטה". מופע של G2/G9. מקור-סמכות: X3 §1ג (3 סוגי-האירוע: status_change, missing_precedent_created, export_complete); קוד ה-emitter (paperclip_api.py:87+). (פרויקטלי-תפעולי — משרת G2/G9.) אכיפה: enum + סכמה משותפים emitter↔handler. כיום: eventType נופל ל-status_change כברירת-מחדל אם חסר/לא-מוכר (gap-audit GAP-29).


5. מצב קיים מול יעד — פער אכיפה

האינטגרציה נשענת על נוהל + שני לקוחות, לא על מסלול-קוד קנוני אחד:

  • לקוח (INV-INT4): יעד — לקוח-API יחיד; הסרת מסלול-ה-DB הישיר.
  • מזהים (INV-INT5/INT6): יעד — טבלת-מיפוי/env יחידה; פונקציית-גזירה אחת.
  • webhook (INV-INT7/INT8): יעד — event-id + dedup + enum-אירוע מגורס + רישום-כישלון.

כל אלה מקובצים ל-FU-9 (gap-audit.md).


6. הפניות-אחיות