Files
legal-ai/docs/superpowers/specs/2026-05-31-fu8a-process-to-code-guards-design.md
Chaim e8431a2adf docs(spec): FU-8a process→code guards design (GAP-21/22) + split GAP-23 to #69
GAP-21: sync_agents --verify exits non-zero on drift; adapter_type mismatch
counted as drift (loud), not silent skip — makes it an enforceable gate (INV-MC1).
GAP-22: fitness-function pytest guarding against raw Paperclip HTTP + direct
agent_wakeup_requests INSERT (INV-INT1/INT3). Repo pre-scanned: 0 existing
violations → clean forward-fence. Verified vs 3+ sources (architectural fitness
functions; drift-verify non-zero exit). GAP-23 (spec→agents) split to #69.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:48:15 +00:00

79 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FU-8a — מחסומי-תהליך → מחסומי-קוד (Process Barriers → Code Guards) — עיצוב
**סטטוס:** מאושר-לעיצוב · **תאריך:** 2026-05-31 · **ענף:** TBD
**מכסה:** GAP-21, GAP-22 · **מספק:** INV-MC1, INV-INT1, INV-INT3 · **משימה:** TaskMaster #66
**תלוי ב:** — · **סוג:** pure-code · **מחוץ-להיקף:** GAP-23 (חיווט ספ→סוכנים) → #69 / FU-8b.
---
## 1. הבעיה
שני מחסומים שהיום נשענים על **נוהל אנושי** ולא על **קוד**, ולכן ניתנים להפרה שקטה:
- **GAP-21 (INV-MC1):** סנכרון-סוכנים חוצה-חברות (`sync_agents_across_companies.py`) ידני ולא-נאכף.
ב-`--verify` (script:397) הוא **יוצא 0 גם כשיש drift**, ו-adapter_type-mismatch (script:388)
מודפס כ-"SKIPPING" ו**נבלע** — אין סיגנל-כשל שניתן לתלות בו gate.
- **GAP-22 (INV-INT1/INT3):** אין מחסום-קוד נגד עקיפת ה-helpers המאושרים של Paperclip — קריאת
`httpx`/`requests` גולמית ל-API של Paperclip (במקום `web/paperclip_api.pc_request`) או `INSERT`
ישיר ל-`agent_wakeup_requests` (במקום ה-wakeup API). היום זה כלל-נוהל ב-CLAUDE.md/HEARTBEAT בלבד.
## 2. ההכרעה (מאומתת ≥3 מקורות)
**Architectural fitness functions** — הופכים החלטת-ארכיטקטורה מ"הסכמה חברתית" ל**כלל נאכף שנתפס
ברגע ההפרה**, כ-assertion דמוי-טסט ב-CI. שני המחסומים מיושמים ככאלה:
| החלטה | נימוק | מקורות |
|-------|--------|--------|
| GAP-21: `--verify` יוצא **non-zero על drift**; adapter_type-mismatch = **drift** (לא silent skip) + דיווח רם | drift-verify הוא gate רק אם הוא יוצא non-zero (Terraform 0/2); "alert, don't skip silently" | InfoQ fitness-functions; Firefly CI-drift; Spacelift drift |
| GAP-22: **fitness-function (pytest שסורק את ה-repo)**, לא import-linter | הכלל הוא דפוס-*שימוש*/מחרוזת (http ל-URL, מחרוזת-SQL), לא גבול-import; fitness-function כ-test-assertion ב-CI הוא הכלי | InfoQ; Lukas Niessen; aipatternbook |
| לרוץ ב-**חבילת-הטסטים הקיימת** (לא CI חדש) | אין CI-lint בפרויקט (רק deploy.yaml); חבילת ה-pytest היא שער-האיכות הקיים | (נגזר מהמצב) |
## 3. הרכיבים
- **Modify** `scripts/sync_agents_across_companies.py` (GAP-21):
- `--verify` יחזיר **exit 1** כש-`plan` לא-ריק **או** כשיש adapter_type-mismatch (כיום `return` שקט).
- adapter_type-mismatch: נספר ל-`mismatches` ומדווח רם (`❌`), לא רק "SKIPPING"; נכלל בסיגנל-הכשל
של `--verify`. (ה-skip עצמו ב-`--apply` נשמר — לא מסנכרנים adapter_type אוטומטית — אבל `--verify`
**נכשל** כדי לאלץ טיפול ידני.)
- **Create** `mcp-server/tests/test_paperclip_access_guard.py` (GAP-22) — fitness-function שסורק את
עץ-המקור (`web/`, `mcp-server/src/`, `scripts/`, `plugin-legal-ai/` אם רלוונטי) ו**נכשל** אם נמצא:
1. קריאת-HTTP גולמית ל-Paperclip — `httpx`/`requests`/`aiohttp` עם `PAPERCLIP_API_URL` או
`localhost:3100`/`pc.nautilus`**מחוץ** ל-`web/paperclip_api.py` (ה-helper המאושר).
2. `INSERT INTO agent_wakeup_requests` (כל קובץ) — חייב לעבור דרך wakeup API.
3. `curl ... $PAPERCLIP_API_URL` ב-shell — מחוץ ל-`scripts/pc.sh`.
- מימוש: סריקת-טקסט ממוקדת (regex) עם **allowlist** מפורש (הקבצים המאושרים) + הודעת-כשל שמסבירה
את ה-helper הנכון. (AST מלא מיותר — הדפוסים הם מחרוזות-URL/SQL, לא מבנה-קוד.)
- **Create** `scripts/check_paperclip_access.py` (GAP-22, אופציונלי-דק) — wrapper הניתן להרצה ידנית/CI
שמריץ את אותה לוגיקת-סריקה (מייבא מהטסט או חולק helper); exit non-zero על הפרה. (אם הטסט מספיק —
לדלג, YAGNI.)
## 4. שינויי-התנהגות וסיכון
| שינוי | השפעה | סיכון |
|--------|--------|--------|
| `--verify` יוצא 1 על drift | הופך ל-gate שמיש (אפשר לתלות בו cron/CI) | נמוך — לא משנה `--apply`; משנה רק exit-code של verify |
| adapter_type-mismatch רם + נכלל ב-fail | drift של adapter לא נבלע | נמוך — דיווח; ה-skip ב-apply נשמר |
| fitness-function guard | הפרה עתידית תיכשל בטסטים | נמוך — **ה-repo נסרק 2026-05-31: 0 הפרות קיימות** (אין httpx גולמי ל-Paperclip, אין INSERT ל-agent_wakeup_requests, אין curl גולמי). הגדר הוא גדר-קדימה נקי, אפס תיקוני-קוד קיימים |
| allowlist | קבצים מאושרים (paperclip_api.py, pc.sh) פטורים | נמוך — מפורש ומתועד |
## 5. אסטרטגיית בדיקה
- **GAP-22 fitness-function** נבדק על עצמו: הטסט מאתר דפוס-הפרה מוזרק (fixture עם httpx ל-Paperclip)
ומאשר שהוא נתפס; ומאשר שה-helpers המאושרים (paperclip_api.py) **לא** מסומנים. כלומר הטסט בודק
את הסורק על דוגמאות חיוביות+שליליות, ואז מריץ אותו על ה-repo האמיתי (חייב לעבור — אחרת יש הפרה
קיימת לתקן).
- **GAP-21** — בדיקת-יחידה ללוגיקת ה-exit/mismatch: מתוך master/mirror סינתטיים, `--verify` עם drift
מחזיר 1; ללא drift מחזיר 0; adapter_type-mismatch → 1 + הודעה. (refactor של לוגיקת-ההכרעה לפונקציה
טהורה `_verify_exit_code(plan, mismatches)` שניתנת לבדיקה offline בלי DB/Paperclip.)
- חבילה מלאה ירוקה; smoke: `sync...py --verify` מול ה-state הנוכחי (לדווח אם drift קיים).
## 6. סדר-ביצוע
1. בדיקות אדומות: `_verify_exit_code` (GAP-21) + סורק ה-guard על fixtures (GAP-22).
2. GAP-21: refactor `--verify` ל-`_verify_exit_code` + ספירת mismatches + exit 1 + דיווח רם.
3. GAP-22: סורק (`tests/test_paperclip_access_guard.py`) + allowlist; **הרצה על ה-repo** וטיפול בהפרות קיימות (תיקון או allowlist מנומק).
4. (אופציונלי) `scripts/check_paperclip_access.py` אם רוצים הרצה עצמאית.
5. חבילה ירוקה + smoke (`--verify`) + SCRIPTS.md (אם נוסף סקריפט) + PR+merge + TaskMaster #66.
> **GAP-23 (#69)** — חיווט הספ ל-HEARTBEAT/סוכנים — מחוץ-להיקף (משנה התנהגות-ייצור, דורש החלטה).