docs(spec): X2-multi-company sync rules
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
157
docs/spec/X2-multi-company.md
Normal file
157
docs/spec/X2-multi-company.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# X2 — מודל רב-החברתי וכללי ה-Sync (Multi-Company & Sync)
|
||||
|
||||
קובץ-תחום זה כפוף ל-[חוקת המערכת](00-constitution.md) והוא ה-deep-dive על **המבנה הרב-חברתי**
|
||||
של עוזר משפטי — שתי החברות (CMP/CMPA), 14 הסוכנים, ואיך שינוי-הגדרות מפושט מ-Master ל-Mirror.
|
||||
הוא אוכף את [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) (מקור-אמת
|
||||
יחיד — אין מסלולים מקבילים מתפצלים) בהקשר של תצורת-סוכנים: שתי החברות הן שתי העתקות של אותה
|
||||
מערכת, ואסור להן להתפצל (drift).
|
||||
|
||||
> **invariant פרויקטלי-תפעולי.** ה-invariants כאן הם **עובדות על איך המערכת *הזו* מנוהלת**
|
||||
> רב-חברתית — לא תאוריה הנדסית כללית ולא תוכן משפטי. אין סמכות חיצונית ל"איך מסנכרנים
|
||||
> CMP↔CMPA"; לכן הם נושאים שדה `מקור-סמכות` = הראנבוקים והקוד של הפרויקט עצמו ([CLAUDE.md](../../CLAUDE.md),
|
||||
> [HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md), [scripts/sync_agents_across_companies.py](../../scripts/sync_agents_across_companies.py))
|
||||
> — **לא** ≥3 מקורות חיצוניים ו**ללא** סטטוס verified/UNVERIFIED. אבל כל invariant **נקשר
|
||||
> לעיקרון הגלובלי שהוא משרת**: כלל אי-ה-drift הוא מופע של [G2](00-constitution.md#inv-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](../../.claude/agents/HEARTBEAT.md), שורות 38–44; מזהי-החברות מקודדים גם
|
||||
ב-[sync_agents_across_companies.py:62-63](../../scripts/sync_agents_across_companies.py).)
|
||||
|
||||
**14 סוכנים = 7 × 2.** כל חברה מחזיקה את אותם 7 תפקידי-סוכן (CEO, writer, analyst, researcher,
|
||||
qa, proofreader, exporter — ראה [X4-agents.md](X4-agents.md)). מאחר ש-`company_id` הוא `NOT NULL`,
|
||||
כל תפקיד מיוצג בשתי **רשומות-סוכן נפרדות** — אחת ל-CMP, אחת ל-CMPA. אין רשומה משותפת.
|
||||
|
||||
**Master = CMP, Mirror = CMPA.** התצורה נכתבת ומתוחזקת בחברת ה-Master (CMP, 1xxx), והסנכרון
|
||||
הוא **חד-כיווני** CMP → CMPA ([sync...py:1-7,361-362](../../scripts/sync_agents_across_companies.py)).
|
||||
|
||||
---
|
||||
|
||||
## 2. ניתוב לפי חברה — סינון ב-`company_id`
|
||||
|
||||
הזרימה התפעולית נאכפת לפי `$PAPERCLIP_COMPANY_ID` של הסוכן הפועל ([HEARTBEAT.md §1](../../.claude/agents/HEARTBEAT.md)):
|
||||
|
||||
- `42a7acd0…` → הסוכן מטפל **רק** בתיקי 1xxx; `8639e837…` → **רק** בתיקי 8xxx/9xxx (שורות 43–44).
|
||||
- **אסור** ליצור פרויקט/issue/תוכן לתיק מחוץ לטווח-החברה (שורה 45); issue שמכוון לתיק מחוץ
|
||||
לטווח → סירוב מנומס ב-comment + העֵרת ה-CEO של החברה הנכונה (שורה 46).
|
||||
- **CEO שונה לכל חברה** — בחירת ה-CEO ל-wakeup נגזרת מ-`$PAPERCLIP_COMPANY_ID`, **לעולם לא**
|
||||
UUID hardcoded ([HEARTBEAT.md §4ג](../../.claude/agents/HEARTBEAT.md), שורות 143–150).
|
||||
- **גבול-חברה נאכף בצד-Paperclip:** wakeup לחברה אחרת נדחה — `Agent key cannot access another
|
||||
company` ([HEARTBEAT.md §4ג](../../.claude/agents/HEARTBEAT.md), שורה 157).
|
||||
|
||||
---
|
||||
|
||||
## 3. כלל ה-Sync — אחרי כל שינוי-הגדרות ב-Master
|
||||
|
||||
> **טריגר:** כל שינוי ב-`adapter_config`, `runtime_config`, `budget_monthly_cents`, או skills
|
||||
> של סוכן ב-Master (UI / SQL / API). מקור: סעיף "Cross-company agent sync" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md)
|
||||
> וב-[root CLAUDE.md](../../../CLAUDE.md).
|
||||
|
||||
הפעולה החובה — קודם בדיקה, אז החלה:
|
||||
|
||||
```bash
|
||||
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](../../scripts/sync_agents_across_companies.py)). שדות פר-חברה
|
||||
(`id`, `company_id`, `adapter_type`, `agent_api_keys`, `status`, `spent_monthly_cents`,
|
||||
`permissions`) **אינם** מסונכרנים ([sync...py:24-29](../../scripts/sync_agents_across_companies.py)).
|
||||
- **מבוסס-API, לא DB ישיר.** ה-PATCH דרך `PATCH /api/agents/{id}` וה-skills דרך
|
||||
`POST /api/agents/{id}/skills/sync` עם `Authorization: Bearer` ([sync...py:204-237](../../scripts/sync_agents_across_companies.py)).
|
||||
- **מסנן skills מקומיים שלא קיימים ב-Mirror.** `desiredSkills` מושוות כ-subset; skills מקומיים
|
||||
של CMP (למשל `local/eba6210d5a/legal-decision`) שלא קיימים ב-CMPA נשמטים עם אזהרה
|
||||
([sync...py:138-154,194-195](../../scripts/sync_agents_across_companies.py)).
|
||||
- **יוצר revisions.** סנכרון skills עובר דרך endpoint ייעודי שמייצר `skill-sync` revision
|
||||
([sync...py:277-284](../../scripts/sync_agents_across_companies.py)).
|
||||
- **idempotent + אל-כשל.** `--verify`/`--dry-run` כברירת-מחדל, גיבוי `pg_dump` לפני `--apply`,
|
||||
pre-flight על קבצי-instructions, ו-re-verify אוטומטי אחרי ההחלה ([sync...py:9,163-173,408-465](../../scripts/sync_agents_across_companies.py)).
|
||||
- **מדלג על סוכן עם `adapter_type` שונה בין החברות.** אם ל-Master ול-Mirror `adapter_type`
|
||||
שונה → `SKIPPING`, ללא סנכרון ([sync...py:387-389](../../scripts/sync_agents_across_companies.py)).
|
||||
זו המלכודת ב-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](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים) — מקור-אמת
|
||||
יחיד, אין מסלולים מקבילים מתפצלים; וכלל-ההנדסה "סימטריה", [חוקה §6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)).
|
||||
**מקור-סמכות:** סעיף "Cross-company agent sync" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) +
|
||||
ב-[root CLAUDE.md](../../../CLAUDE.md) +
|
||||
[scripts/sync_agents_across_companies.py](../../scripts/sync_agents_across_companies.py) +
|
||||
[HEARTBEAT.md §1, §4ג](../../.claude/agents/HEARTBEAT.md). (invariant פרויקטלי-תפעולי — ללא
|
||||
פרוטוקול ≥3-המקורות; משרת את העיקרון הגלובלי G2.)
|
||||
**אכיפה:** סקריפט ה-Sync (idempotent, מבוסס-API, גיבוי+re-verify) — מורץ **ידנית** אחרי כל
|
||||
שינוי-תצורה ב-Master. **אין אכיפה אוטומטית** (ראה §5).
|
||||
**הפרה ידועה:** הסקריפט **מדלג** על סוכן ש-`adapter_type` שונה בין CMP ל-CMPA
|
||||
([sync...py:387-389](../../scripts/sync_agents_across_companies.py)). כשמעבירים סוכן ל-`deepseek_local`
|
||||
ב-Master, ה-Mirror נשאר על ה-adapter הישן והסנכרון מדלג עליו — **חובה להחיל את שינוי ה-`adapter_type`
|
||||
ידנית בשתי החברות לפני הרצת ה-Sync** ([CLAUDE.md "External adapters — deepseek_local"](../../CLAUDE.md)),
|
||||
אחרת נוצר drift שקט באותו סוכן.
|
||||
|
||||
### INV-MC2: אין סוכן משותף — רשומה נפרדת לכל חברה
|
||||
**כלל:** סוכן **לעולם אינו רשומה משותפת** בין החברות. כל אחד מ-7 התפקידים מיוצג בשתי
|
||||
רשומות-סוכן נפרדות (CMP + CMPA), שכן Paperclip מחייב `agents.company_id NOT NULL`. הסנכרון
|
||||
מעתיק *ערכי-תצורה* בין שתי רשומות — לא ממזג אותן לרשומה אחת (תואם [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים):
|
||||
מקור-אמת יחיד לתצורה, גם כשהיא משוכפלת על פני רשומות).
|
||||
**מקור-סמכות:** סעיף "Cross-company agent sync" ב-[legal-ai/CLAUDE.md](../../CLAUDE.md) (14 agents = 7 × 2;
|
||||
`agents.company_id NOT NULL`) + [sync...py:4-7,83-103](../../scripts/sync_agents_across_companies.py)
|
||||
(שולף מערכי-סוכן נפרדים לכל `company_id`) + [HEARTBEAT.md §1](../../.claude/agents/HEARTBEAT.md).
|
||||
(invariant פרויקטלי-תפעולי.)
|
||||
**אכיפה:** אילוץ `company_id NOT NULL` בצד-Paperclip; הסקריפט מתאים סוכנים בין החברות לפי
|
||||
`name` ולעולם לא יוצר רשומה משותפת ([sync...py:372,383-385](../../scripts/sync_agents_across_companies.py)
|
||||
— "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](../../scripts/sync_agents_across_companies.py)) — ה-`--verify` ידווח
|
||||
`SKIPPING`, אך אם המפעיל לא יחיל את שינוי ה-adapter ידנית בשתי החברות, הסוכן יישאר drifted.
|
||||
**יעד:** אזהרת-SKIPPING שמתבלטת ב-report + צ'קליסט-ידני (כבר מתועד ב-[CLAUDE.md](../../CLAUDE.md)).
|
||||
|
||||
---
|
||||
|
||||
## 6. הפניות-אחיות
|
||||
|
||||
- [00-constitution.md](00-constitution.md) — [G2](00-constitution.md#inv-g2-מקור-אמת-יחיד--אין-מסלולים-מקבילים-מתפצלים)
|
||||
(מקור-אמת יחיד, אין מסלולים מקבילים מתפצלים) + כלל-ההנדסה "סימטריה" ([§6](00-constitution.md#6-כללי-הנדסה-מונעים-הישנות)).
|
||||
- [X4-agents.md](X4-agents.md) — מפת 7 תפקידי-הסוכן שמשוכפלים על פני שתי החברות.
|
||||
- [X3-integration-deploy.md](X3-integration-deploy.md) — Paperclip (wakeup, ניתוב comments) ו-deploy;
|
||||
ה-wakeup-per-company משלים את הניתוב כאן.
|
||||
- [scripts/sync_agents_across_companies.py](../../scripts/sync_agents_across_companies.py) — מימוש ה-Sync.
|
||||
- [legal-ai/CLAUDE.md](../../CLAUDE.md) + [root CLAUDE.md](../../../CLAUDE.md) — סעיף
|
||||
"Cross-company agent sync" + "External adapters — deepseek_local" (מלכודת ה-adapter_type).
|
||||
- [.claude/agents/HEARTBEAT.md](../../.claude/agents/HEARTBEAT.md) — §1 (סינון-חברה) + §4ג (wake CEO לפי חברה).
|
||||
Reference in New Issue
Block a user