16 KiB
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, MEMORY 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ד, שורות 152–158):
~/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 שלבים 2b–2c).
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-117 —
emit_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, שורות 15–32; 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 (~2–4 דק') | עריכה → 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 |
2א. מחזור-השינוי של legal-ai (Coolify dockerimage)
שינוי קוד ב-web/ או web-ui/ לא נכנס לתוקף עד שמריצים את כל הצעדים, בסדר:
git commit+git push origin mainל-Gitea.- Gitea Actions בונה image ודוחף ל-registry (
gitea.nautilus.marcusgroup.org/...). - ה-workflow מפעיל Coolify redeploy דרך API (UUID
gyjo0mtw2c42ej3xxvbz8zio). - ~2–4 דקות 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).
2ב. legal-chat-service ו-host.docker.internal
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 רק ממקומי" נשמרת — ראה
reference: 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ד, שורות 152–158. (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).
INV-INT2: שינוי-קוד legal-ai נכנס לתוקף רק דרך commit→push→Coolify deploy
כלל: שינוי קוד ב-web/ או web-ui/ לא נכנס לתוקף עד git commit + git push origin main
- build ב-Gitea Actions + Coolify redeploy (build_pack
dockerimage, UUIDgyjo0mtw2c42ej3xxvbz8zio). אין הרצת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, ראה reference: 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, שורות 15–32 +
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. הפניות-אחיות
- 00-constitution.md — G2 (מסלול קנוני יחיד) + G9 (audit-trail) + כלל-ההנדסה "סימטריה" (§6).
- X2-multi-company.md — wakeup-per-company + ניתוב לפי
company_idמשלים את §1 כאן. - X4-agents.md — מפת הסוכנים שה-CEO מנתב אליהם comments.
- root CLAUDE.md + legal-ai/CLAUDE.md — "Wakeup API", "Comment routing", "Deploy architecture", "קריאות API — תמיד דרך helper".
- .claude/agents/HEARTBEAT.md — §0 (pc.sh), §4ג–§4ד (wake CEO + payload).
- web/paperclip_api.py —
pc_request,emit_case_status_webhook. - scripts/pc.sh — helper ה-bash.