Files
legal-ai/docs/spec/X3-integration-deploy.md

16 KiB
Raw Blame History

X3 — אינטגרציה ו-Deploy (Integration & Deploy)

קובץ-תחום זה כפוף ל-חוקת המערכת והוא ה-deep-dive על שני ממדי-התפעול של עוזר משפטי: (א) האינטגרציה עם Paperclip — איך המערכת מעירה סוכנים, איך תגובות-משתמש מנותבות, ואיך שינוי-סטטוס תיק מתפרסם חזרה; (ב) מודל ה-Deploy — שני מודלי-הרצה הדו-קיימים על שרת Nautilus (Coolify-Docker מול pm2-מקומי) ומחזור-השינוי של legal-ai.

invariant פרויקטלי-תפעולי. ה-invariants כאן הם עובדות על איך המערכת הזו משתלבת ונפרסת — לא תאוריה הנדסית כללית ולא תוכן משפטי. אין סמכות חיצונית ל"איך מעירים סוכן Paperclip" או "איך פורסים את legal-ai"; לכן הם נושאים שדה מקור-סמכות = הראנבוקים והקוד של הפרויקט עצמו (root CLAUDE.md, legal-ai/CLAUDE.md, HEARTBEAT.md, זיכרון reference_paperclip_wakeup, ו-web/paperclip_api.py) — לא ≥3 מקורות חיצוניים וללא סטטוס verified/UNVERIFIED. אבל כל invariant נקשר לעיקרון הגלובלי שהוא משרת: כלל ה-wakeup-דרך-API-בלבד הוא מופע של G2 (מסלול קנוני יחיד; ה-DB-insert המקביל אסור כי הוא מתפצל מהמסלול שיוצר heartbeat_run).


1. אינטגרציית Paperclip

עוזר משפטי משתלב עם Paperclip בשלושה כיוונים: wakeup (legal-ai/אוטומציה → סוכן), ניתוב comments (משתמש → CEO → סוכן), ו-webhook יוצא (legal-ai → פלאגין).

1א. Wakeup — תמיד דרך API, לעולם לא דרך DB

הנתיב הקנוני היחיד להערת סוכן הוא POST /api/agents/{agent-id}/wakeup עם payload המכיל issueId (root CLAUDE.md "Wakeup API"; legal-ai/CLAUDE.md "Wakeup API"; HEARTBEAT.md §4ד, שורות 152158):

~/legal-ai/scripts/pc.sh POST "/api/agents/$CEO_ID/wakeup" \
  '{"source":"automation","triggerDetail":"system","reason":"...",
    "payload":{"issueId":"...","mutation":"comment","commentId":"..."}}'
  • POST .../wakeup, לא /wake — שם-הנתיב מדויק (legal-ai/CLAUDE.md).
  • חובה payload.issueId — בלעדיו הסוכן מתעורר בלי הקשר (בלי תיק, בלי issue, בלי cwd נכון) (HEARTBEAT.md שורה 156).
  • אסור INSERT INTO agent_wakeup_requests ישיר — insert ל-DB יוצר רשומת-בקשה בלבד בלי heartbeat_run, והסוכן לא יתעורר לעולם (HEARTBEAT.md שורה 158; זיכרון reference_paperclip_wakeup). זהו בדיוק "מסלול מקביל מתפצל" שאסור לפי G2.
  • CEO לכל חברה — מזהה-ה-CEO ל-wakeup נגזר מ-$PAPERCLIP_COMPANY_ID, לעולם לא UUID hardcoded; wakeup לחברה אחרת נדחה (Agent key cannot access another company) (HEARTBEAT.md §4ג; ראה X2-multi-company.md §2).

1ב. ניתוב comments — דרך ה-CEO

תגובת-משתמש על issue ב-Paperclip אינה מנותבת ישירות לסוכן-המטרה. הזרימה (root CLAUDE.md "Comment routing"; legal-ai/CLAUDE.md):

user comment  →  plugin-legal-ai  →  ctx.agents.invoke() מעיר CEO
              →  CEO קורא comment, מחליט ניתוב, יוצר issue לסוכן המתאים
  • ה-CEO הוא נקודת-הניתוב היחידה — סוכן-משנה לא מקבל עבודה ישירות מ-comment.
  • כל סוכן חייב לקרוא comments אחרונים לפני שהוא מתחיל עבודה (HEARTBEAT שלבים 2b2c).

1ג. Webhook יוצא — עדכון סטטוס תיק לפלאגין

כשסטטוס תיק משתנה דרך PUT /api/cases/{case_number}, הבקאנד שולח webhook אסינכרוני לפלאגין כ-BackgroundTask, fire-and-forget:

PUT /api/cases/{n}  →  [BackgroundTask] emit_case_status_webhook()
  →  POST /api/plugins/marcusgroup.legal-ai/webhooks/case-status
  →  plugin-legal-ai/onWebhook()  →  comment בעברית + CEO wakeup (כש-qa_failed)

מאומת מול הקוד:

  • ה-call-site: web/app.py:2045-2061 — ה-webhook מתוזמן רק כש-old_status != new_status, ו-company_id נגזר מ-prefix מספר-התיק (1→licensing, 8/9→betterment).
  • המימוש: web/paperclip_api.py:87-117emit_case_status_webhook קורא ל-pc_request("POST", "/api/plugins/.../webhooks/case-status", ...) עם timeout=5.0, בלוק try/except שמתעד logger.warning ולעולם לא raise (לא חוסם את הקורא).
  • אותו דפוס משרת אירועים נוספים: emit_missing_precedent_webhook (paperclip_api.py:120-165) ו-emit_export_complete_webhook (paperclip_api.py:168+).

1ד. כל קריאת-API דרך helper — לא curl/httpx ישיר

קריאות ל-Paperclip עוברות תמיד דרך helper, לא דרך לקוח גולמי:

  • bash (סוכנים): ~/legal-ai/scripts/pc.sh <METHOD> <PATH> [BODY] — מוסיף אוטומטית Authorization: Bearer, X-Paperclip-Run-Id, Content-Type, ו-base URL (HEARTBEAT.md §0, שורות 1532; scripts/pc.sh:8-9,39-40).
  • Python (FastAPI): from web.paperclip_api import pc_request — בונה headers דרך _build_headers (paperclip_api.py:47-84), משתמש ב-board API key.
  • למה: ה-skill הרשמי דורש X-Paperclip-Run-Id בכל קריאה משנה issue (audit trail); ה-helper מבטיח עקביות + תאימות ל-board API keys long-lived שלא נושאות JWT claims (legal-ai/CLAUDE.md "קריאות API — תמיד דרך helper").

2. מודל ה-Deploy — שני מודלים דו-קיימים

על שרת Nautilus דרים שני מודלי-הרצה. ערבוב ביניהם הוא הטעות הנפוצה ביותר (root CLAUDE.md "Deploy architecture"; legal-ai/CLAUDE.md "ארכיטקטורת Deploy").

ממד legal-ai (web + web-ui) Paperclip + legal-chat-service
מודל Coolify-managed (Docker) PM2-managed (Node/Python מקומי)
מחזור-שינוי commit → push → Gitea Actions build → Coolify redeploy (~24 דק') עריכה → pm2 restart
Coolify UUID gyjo0mtw2c42ej3xxvbz8zio
build_pack dockerimage (לא dockerfile)
פורטים Next.js :3000 (חשוף) + FastAPI :8000 (פנימי) Paperclip localhost:3100; legal-chat-service 127.0.0.1:8770 (loopback)
הרצה מקומית אין — אין venv של Python על ה-host; אסור uvicorn/next dev לפרוד יש; מתחזק דרך pm2

שינוי קוד ב-web/ או web-ui/ לא נכנס לתוקף עד שמריצים את כל הצעדים, בסדר:

  1. git commit + git push origin main ל-Gitea.
  2. Gitea Actions בונה image ודוחף ל-registry (gitea.nautilus.marcusgroup.org/...).
  3. ה-workflow מפעיל Coolify redeploy דרך API (UUID gyjo0mtw2c42ej3xxvbz8zio).
  4. ~24 דקות end-to-end. בדיקה: curl -s https://legal-ai.nautilus.marcusgroup.org/api/health.
  • אסור לנסות uvicorn/next dev לפרוד — הקונטיינר מספק את שני התהליכים; אין סביבת Python על ה-host (root CLAUDE.md; legal-ai/CLAUDE.md).
  • endpoint חדש ≠ זמין ל-UI. הוספת endpoint ב-web/app.py היא תנאי הכרחי אך לא מספיק לצריכה מה-frontend — חובה npm run api:types בתוך web-ui/ כדי לחדש את ה-OpenAPI types (root CLAUDE.md, שורה 89; legal-ai/CLAUDE.md).

legal-chat-service (127.0.0.1:8770, pm2) הוא גשר host-side שעוטף את claude CLI ב-streaming לטאב הצ'אט ב-/training. הקונטיינר מגיע אליו דרך host.docker.internal:8770 — ולכן ה-Service Definition של legal-ai ב-Coolify חייב לכלול extra_hosts: host.docker.internal:host-gateway, אחרת ה-proxy יקבל ConnectError (root CLAUDE.md; legal-ai/CLAUDE.md "legal-chat-service"). הנחת-היסוד של "קריאות LLM רק ממקומי" נשמרת — ראה זיכרון feedback_claude_session_local_only.


3. Invariants של התחום (פרויקטלי-תפעולי)

INV-INT1: wakeup דרך API בלבד — DB-insert אסור

כלל: הערת סוכן Paperclip חייבת לעבור דרך POST /api/agents/{agent-id}/wakeup עם payload.issueId. אסור INSERT INTO agent_wakeup_requests ישיר — insert ל-DB אינו יוצר heartbeat_run, ולכן הסוכן לא יתעורר לעולם. זהו המסלול הקנוני היחיד; ה-DB-insert הוא מסלול-מקביל-מתפצל אסור (מופע של G2 — מקור-אמת/מסלול קנוני יחיד; וכלל-ההנדסה "סימטריה", חוקה §6). מקור-סמכות: "Wakeup API" ב-root CLAUDE.md + ב-legal-ai/CLAUDE.md + זיכרון reference_paperclip_wakeup + HEARTBEAT.md §4ד, שורות 152158. (invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות; משרת את העיקרון הגלובלי G2.) אכיפה: קריאות-wakeup דרך pc.sh/pc_request בלבד; payload.issueId חובה; בדיקה ש-heartbeat_run נוצר. אין אכיפה סכמתית שתחסום insert ישיר ל-agent_wakeup_requests — המניעה היא נוהל (ראה §4). הפרה ידועה: insert ישיר ל-agent_wakeup_requests (fallback ישן) → רשומה בלי heartbeat_run, הסוכן נשאר רדום (זיכרון reference_paperclip_wakeup).

כלל: שינוי קוד ב-web/ או web-ui/ לא נכנס לתוקף עד git commit + git push origin main

  • build ב-Gitea Actions + Coolify redeploy (build_pack dockerimage, UUID gyjo0mtw2c42ej3xxvbz8zio). אין הרצת uvicorn/next dev מקומית לפרוד. endpoint חדש ב-web/app.py דורש גם npm run api:types ב-web-ui/ כדי להיחשף ל-UI. מקור-סמכות: "Deploy architecture" ב-root CLAUDE.md (UUID, dockerimage, no local uvicorn, api:types) + "ארכיטקטורת Deploy" ב-legal-ai/CLAUDE.md + זיכרון reference_deployment. (invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות.) אכיפה: pipeline Gitea Actions → Coolify (אוטומטי בדחיפה ל-main); בדיקה ידנית curl .../api/health אחרי deploy. אין מסלול-פריסה חלופי. הפרה ידועה: בדיקת שינוי מול הרצה מקומית שלא קיימת — הקוד בפרוד נשאר ישן עד deploy; וכן drift אפשרי Infisical↔Coolify env (env לא מתעדכן אוטומטית מ-Infisical, ראה זיכרון feedback_infisical_coolify_drift).

INV-INT3: כל קריאת-Paperclip דרך helper — לא curl/httpx ישיר

כלל: קריאות ל-Paperclip API עוברות תמיד דרך helper — pc.sh (bash/סוכנים) או pc_request (Python/FastAPI) — ולעולם לא curl/httpx גולמי. ה-helper מזריק Authorization, X-Paperclip-Run-Id (audit), ו-Content-Type באופן עקבי, ותומך ב-board API keys long-lived (מופע של G2 — מסלול-גישה קנוני יחיד ל-Paperclip; ושל G9 — audit-trail עקבי). מקור-סמכות: "קריאות API — תמיד דרך helper" ב-legal-ai/CLAUDE.md + HEARTBEAT.md §0, שורות 1532 + scripts/pc.sh:8-9,39-40 + web/paperclip_api.py:47-84. (invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות.) אכיפה: נוהל + code-review; pc.sh ו-pc_request הם נקודות-הכניסה היחידות. אין אכיפה אוטומטית שתחסום httpx.AsyncClient ישיר ל-Paperclip בקוד חדש. הפרה ידועה:


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

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

  • wakeup (INV-INT1): אין constraint סכמתי שחוסם insert ישיר ל-agent_wakeup_requests; המניעה היא ידע-נוהל (HEARTBEAT). יעד: wrapper/בדיקת-בריאות שמסמן בקשות-wakeup ללא heartbeat_run תואם.
  • helper (INV-INT3): אין linter/בדיקה שתתפוס httpx/curl ישיר ל-Paperclip בקוד חדש. יעד: כלל-lint שמכריח שימוש ב-pc_request/pc.sh.

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