Files
legal-ai/docs/spec/X2-multi-company.md
2026-05-30 16:47:19 +00:00

13 KiB
Raw Blame History

X2 — מודל רב-החברתי וכללי ה-Sync (Multi-Company & Sync)

קובץ-תחום זה כפוף ל-חוקת המערכת והוא ה-deep-dive על המבנה הרב-חברתי של עוזר משפטי — שתי החברות (CMP/CMPA), 14 הסוכנים, ואיך שינוי-הגדרות מפושט מ-Master ל-Mirror. הוא אוכף את G2 (מקור-אמת יחיד — אין מסלולים מקבילים מתפצלים) בהקשר של תצורת-סוכנים: שתי החברות הן שתי העתקות של אותה מערכת, ואסור להן להתפצל (drift).

invariant פרויקטלי-תפעולי. ה-invariants כאן הם עובדות על איך המערכת הזו מנוהלת רב-חברתית — לא תאוריה הנדסית כללית ולא תוכן משפטי. אין סמכות חיצונית ל"איך מסנכרנים CMP↔CMPA"; לכן הם נושאים שדה מקור-סמכות = הראנבוקים והקוד של הפרויקט עצמו (CLAUDE.md, HEARTBEAT.md, scripts/sync_agents_across_companies.py) — לא ≥3 מקורות חיצוניים וללא סטטוס verified/UNVERIFIED. אבל כל invariant נקשר לעיקרון הגלובלי שהוא משרת: כלל אי-ה-drift הוא מופע של G2.


1. שתי החברות: Master מול Mirror

Paperclip מחייב agents.company_id NOT NULL — אין סוכנים משותפים. כדי לשרת את שני סוגי העררים, המערכת מורצת כשתי חברות נפרדות, כל אחת עם מערך-סוכנים מלא משלה:

ממד CMP — Master CMPA — Mirror
תפקיד מקור-האמת לתצורת-סוכנים העתקה מסונכרנת מ-Master
COMPANY_ID 42a7acd0-30c5-4cbd-ac97-7424f65df294 8639e837-4c9d-47fa-a76b-95788d651896
סוגי תיקים רישוי ובנייה היטל השבחה + פיצויים ס'197
טווח-מספרים 1xxx 8xxx, 9xxx
CEO Agent ID 752cebdd-6748-4a04-aacd-c7ab0294ef33 cdbfa8bc-3d61-41a4-a2e7-677ec7d34562

(המקור: HEARTBEAT.md §1, שורות 3844; מזהי-החברות מקודדים גם ב-sync_agents_across_companies.py:62-63.)

14 סוכנים = 7 × 2. כל חברה מחזיקה את אותם 7 תפקידי-סוכן (CEO, writer, analyst, researcher, qa, proofreader, exporter — ראה X4-agents.md). מאחר ש-company_id הוא NOT NULL, כל תפקיד מיוצג בשתי רשומות-סוכן נפרדות — אחת ל-CMP, אחת ל-CMPA. אין רשומה משותפת.

Master = CMP, Mirror = CMPA. התצורה נכתבת ומתוחזקת בחברת ה-Master (CMP, 1xxx), והסנכרון הוא חד-כיווני CMP → CMPA (sync...py:1-7,361-362).


2. ניתוב לפי חברה — סינון ב-company_id

הזרימה התפעולית נאכפת לפי $PAPERCLIP_COMPANY_ID של הסוכן הפועל (HEARTBEAT.md §1):

  • 42a7acd0… → הסוכן מטפל רק בתיקי 1xxx; 8639e837…רק בתיקי 8xxx/9xxx (שורות 4344).
  • אסור ליצור פרויקט/issue/תוכן לתיק מחוץ לטווח-החברה (שורה 45); issue שמכוון לתיק מחוץ לטווח → סירוב מנומס ב-comment + העֵרת ה-CEO של החברה הנכונה (שורה 46).
  • CEO שונה לכל חברה — בחירת ה-CEO ל-wakeup נגזרת מ-$PAPERCLIP_COMPANY_ID, לעולם לא UUID hardcoded (HEARTBEAT.md §4ג, שורות 143150).
  • גבול-חברה נאכף בצד-Paperclip: wakeup לחברה אחרת נדחה — Agent key cannot access another company (HEARTBEAT.md §4ג, שורה 157).

3. כלל ה-Sync — אחרי כל שינוי-הגדרות ב-Master

טריגר: כל שינוי ב-adapter_config, runtime_config, budget_monthly_cents, או skills של סוכן ב-Master (UI / SQL / API). מקור: סעיף "Cross-company agent sync" ב-legal-ai/CLAUDE.md וב-root CLAUDE.md.

הפעולה החובה — קודם בדיקה, אז החלה:

PAPERCLIP_BOARD_API_KEY=$(…infisical…) \
  python ~/legal-ai/scripts/sync_agents_across_companies.py --verify   # drift report
PAPERCLIP_BOARD_API_KEY=$() \
  python ~/legal-ai/scripts/sync_agents_across_companies.py --apply     # backup + apply

מה הסקריפט עושה (מאומת מול הקוד):

  • חד-כיווני CMP → CMPA, סינכרון של שדות-תצורה מוגדרים: top-level (budget_monthly_cents, metadata, icon, title, role), מפתחות adapter_config נבחרים (model, effort, timeoutSec, maxTurnsPerRun, נתיבי-instructions, cwd…), ו-runtime_config כ-full-replace (sync...py:66-75,124-160). שדות פר-חברה (id, company_id, adapter_type, agent_api_keys, status, spent_monthly_cents, permissions) אינם מסונכרנים (sync...py:24-29).
  • מבוסס-API, לא DB ישיר. ה-PATCH דרך PATCH /api/agents/{id} וה-skills דרך POST /api/agents/{id}/skills/sync עם Authorization: Bearer (sync...py:204-237).
  • מסנן skills מקומיים שלא קיימים ב-Mirror. desiredSkills מושוות כ-subset; skills מקומיים של CMP (למשל local/eba6210d5a/legal-decision) שלא קיימים ב-CMPA נשמטים עם אזהרה (sync...py:138-154,194-195).
  • יוצר revisions. סנכרון skills עובר דרך endpoint ייעודי שמייצר skill-sync revision (sync...py:277-284).
  • idempotent + אל-כשל. --verify/--dry-run כברירת-מחדל, גיבוי pg_dump לפני --apply, pre-flight על קבצי-instructions, ו-re-verify אוטומטי אחרי ההחלה (sync...py:9,163-173,408-465).
  • מדלג על סוכן עם adapter_type שונה בין החברות. אם ל-Master ול-Mirror adapter_type שונה → SKIPPING, ללא סנכרון (sync...py:387-389). זו המלכודת ב-INV-MC1 (להלן).

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

INV-MC1: תצורת-סוכן ב-Master מפושטת ל-Mirror — אין drift בין החברות

כלל: כל שינוי ב-adapter_config / runtime_config / budget_monthly_cents / skills של סוכן בחברת ה-Master (CMP) חייב להיות מפושט ל-Mirror (CMPA) דרך סקריפט ה-Sync המבוסס-API (--verify ואז --apply). שתי החברות לא מתפצלות — הן שתי העתקות מסונכרנות של אותה תצורה (מופע של G2 — מקור-אמת יחיד, אין מסלולים מקבילים מתפצלים; וכלל-ההנדסה "סימטריה", חוקה §6). מקור-סמכות: סעיף "Cross-company agent sync" ב-legal-ai/CLAUDE.md + ב-root CLAUDE.md + scripts/sync_agents_across_companies.py + HEARTBEAT.md §1, §4ג. (invariant פרויקטלי-תפעולי — ללא פרוטוקול ≥3-המקורות; משרת את העיקרון הגלובלי G2.) אכיפה: סקריפט ה-Sync (idempotent, מבוסס-API, גיבוי+re-verify) — מורץ ידנית אחרי כל שינוי-תצורה ב-Master. אין אכיפה אוטומטית (ראה §5). הפרה ידועה: הסקריפט מדלג על סוכן ש-adapter_type שונה בין CMP ל-CMPA (sync...py:387-389). כשמעבירים סוכן ל-deepseek_local ב-Master, ה-Mirror נשאר על ה-adapter הישן והסנכרון מדלג עליו — חובה להחיל את שינוי ה-adapter_type ידנית בשתי החברות לפני הרצת ה-Sync (CLAUDE.md "External adapters — deepseek_local"), אחרת נוצר drift שקט באותו סוכן.

INV-MC2: אין סוכן משותף — רשומה נפרדת לכל חברה

כלל: סוכן לעולם אינו רשומה משותפת בין החברות. כל אחד מ-7 התפקידים מיוצג בשתי רשומות-סוכן נפרדות (CMP + CMPA), שכן Paperclip מחייב agents.company_id NOT NULL. הסנכרון מעתיק ערכי-תצורה בין שתי רשומות — לא ממזג אותן לרשומה אחת (תואם G2: מקור-אמת יחיד לתצורה, גם כשהיא משוכפלת על פני רשומות). מקור-סמכות: סעיף "Cross-company agent sync" ב-legal-ai/CLAUDE.md (14 agents = 7 × 2; agents.company_id NOT NULL) + sync...py:4-7,83-103 (שולף מערכי-סוכן נפרדים לכל company_id) + HEARTBEAT.md §1. (invariant פרויקטלי-תפעולי.) אכיפה: אילוץ company_id NOT NULL בצד-Paperclip; הסקריפט מתאים סוכנים בין החברות לפי name ולעולם לא יוצר רשומה משותפת (sync...py:372,383-385 — "we never auto-create"). הפרה ידועה:


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

ה-Sync הוא ידני ולא-נאכף. הסקריפט עצמו בנוי "אל-כשל" (dry-run כברירת-מחדל, גיבוי, re-verify), אך שום מנגנון לא מכריח הרצה אחרי שינוי-תצורה ב-Master:

  • drift אם שוכחים. שינוי adapter_config/runtime_config/budget/skills ב-CMP בלי הרצת --apply משאיר את CMPA מאחור — שתי החברות מתפצלות בשקט, בניגוד ל-INV-MC1. יעד: טריגר/ בדיקת-בריאות תקופתית שמריצה --verify ומדווחת drift (היום ההרצה תלויה בזיכרון המפעיל).
  • מלכודת adapter_type-skip. סוכן עם adapter_type שונה בין החברות נשמט מהסנכרון (sync...py:387-389) — ה---verify ידווח SKIPPING, אך אם המפעיל לא יחיל את שינוי ה-adapter ידנית בשתי החברות, הסוכן יישאר drifted. יעד: אזהרת-SKIPPING שמתבלטת ב-report + צ'קליסט-ידני (כבר מתועד ב-CLAUDE.md).

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