# מחיקת תיק — runbook

> **מתי להשתמש:** reset שלם של תיק (לבדיקות end-to-end), מחיקת תיק שנפתח בטעות, או ניקיון לפני העלאה חוזרת של מסמכים.
>
> **חשוב:** ה-API `DELETE /api/cases` בלבד **לא מספיק** — הוא מטפל רק בצד legal-ai (DB + on-disk dir). תיק חי במקביל ב-4 מערכות והכול חייב להתנקות יחד.

---

## איפה ה-state של תיק חי

| מערכת | מה נשמר | איך מנקים |
|---|---|---|
| **legal-ai DB** (port 5433) | `cases` + `documents` + `document_chunks` + `claims` + `appraiser_facts` + `decisions` + `qa_results` + `case_precedents` | API DELETE (cascade על FK) |
| **legal-ai disk** | `/data/cases/{N}/` בתוך ה-container — מכיל drafts/, documents/, .git/ | API עם `remove_files=true` (`shutil.rmtree` בתוך ה-container) |
| **Paperclip DB** (port 54329) | `projects` + `issues` + `issue_comments` + `agent_wakeup_requests` + `heartbeat_runs` (audit) + עוד 6+ טבלאות | SQL ידני (אין API) |
| **Gitea** | repo `cases/{N}` אם נוצר ב-case-create | Gitea API |

ה-API לא מטפל ב-Paperclip ו-Gitea כי אלה מערכות חיצוניות שלגמרי מחוץ ל-DB של legal-ai. תועד מפורשות ב-docstring של [`services/db.py:delete_case`](../mcp-server/src/legal_mcp/services/db.py).

---

## תהליך מחיקה מלא — שלב אחרי שלב

הצב את מספר התיק במשתנה לפני שמתחילים:

```bash
CASE_NUMBER=8174-24
```

### שלב 1 — legal-ai (DB + disk)

```bash
curl -s -X DELETE \
  "https://legal-ai.nautilus.marcusgroup.org/api/cases?case_number=${CASE_NUMBER}&remove_files=true" \
  -w "\nhttp=%{http_code}\n"
```

תוצאה צפויה: `200` עם `{"deleted": true, "removed_files": true, ...}`.

מה זה עושה מאחורי הקלעים:
1. `DELETE FROM cases` — מפעיל **CASCADE** ל-7 טבלאות, **SET NULL** ל-`audit_log` ו-`chair_feedback`.
2. `shutil.rmtree(/data/cases/{N})` — מסיר את כל הספרייה כולל `.git`.

> **הערה:** עד לפני [commit `903fb4d`](https://gitea.nautilus.marcusgroup.org/ezer-mishpati/legal-ai/commit/903fb4d) ה-endpoint הזה החזיר 500 כי `db.delete_case` לא היה מוגדר. אם נתקלת ב-500 בגרסה ישנה, השתמש ב-SQL הישיר (ראה Fallback בסוף).

### שלב 2 — Paperclip

אין API. SQL ישיר:

```bash
PGPASSWORD=paperclip psql -h localhost -p 54329 -U paperclip -d paperclip <<SQL
BEGIN;

-- 1. מצא את כל ה-issues של הפרויקט (לפי שם)
CREATE TEMP TABLE _issue_ids AS
  SELECT i.id, i.identifier
  FROM issues i
  JOIN projects p ON i.project_id = p.id
  WHERE p.name LIKE '%${CASE_NUMBER}%';

SELECT identifier FROM _issue_ids ORDER BY identifier;  -- וידוא לפני המחיקה

-- 2. מחק blockers ל-FK עם NO ACTION (אסור למחוק issue אם יש להם reference)
DELETE FROM issue_comments       WHERE issue_id IN (SELECT id FROM _issue_ids);
DELETE FROM cost_events          WHERE issue_id IN (SELECT id FROM _issue_ids);
DELETE FROM finance_events       WHERE issue_id IN (SELECT id FROM _issue_ids);
DELETE FROM feedback_votes       WHERE issue_id IN (SELECT id FROM _issue_ids);
DELETE FROM issue_inbox_archives WHERE issue_id IN (SELECT id FROM _issue_ids);
DELETE FROM issue_read_states    WHERE issue_id IN (SELECT id FROM _issue_ids);

-- 3. מחק את ה-issues. CASCADE מטפל ב-7 טבלאות נוספות:
--    issue_approvals, issue_attachments, issue_documents,
--    issue_execution_decisions, issue_labels, issue_relations,
--    issue_work_products
DELETE FROM issues WHERE id IN (SELECT id FROM _issue_ids);

-- 4. שבור FK מ-heartbeat_runs כדי שאפשר יהיה למחוק wakeup_requests.
--    heartbeat_runs נשמרים כ-audit log לא משויך.
UPDATE heartbeat_runs
SET wakeup_request_id = NULL
WHERE wakeup_request_id IN (
    SELECT id FROM agent_wakeup_requests
    WHERE payload->>'issueId' IN (SELECT id::text FROM _issue_ids)
);

DELETE FROM agent_wakeup_requests
WHERE payload->>'issueId' IN (SELECT id::text FROM _issue_ids);

-- 5. מחק blockers ברמת ה-project (NO ACTION FK ל-projects)
DELETE FROM cost_events    WHERE project_id IN (SELECT id FROM projects WHERE name LIKE '%${CASE_NUMBER}%');
DELETE FROM finance_events WHERE project_id IN (SELECT id FROM projects WHERE name LIKE '%${CASE_NUMBER}%');

-- 6. מחק את הפרויקט. CASCADE מטפל ב:
--    execution_workspaces, project_goals, project_workspaces, routines
DELETE FROM projects WHERE name LIKE '%${CASE_NUMBER}%' RETURNING id, name;

COMMIT;
SQL
```

> **למה Paperclip לא הוסיף API למחיקה?** כי זאת מערכת רב-משתמשית ומחיקה היא הרסנית מטבעה — Paperclip מעדיף `archive` (`projects.archived_at`). אנחנו אכן רוצים מחיקה אמיתית רק לסביבת בדיקות.

### שלב 3 — Gitea (אם repo נוצר)

```bash
GITEA_TOKEN=$(infisical secrets get GITEA__API_TOKEN --silent || \
              echo "$GITEA_TOKEN")  # סגדור מ-Infisical או ENV

curl -s -X DELETE \
  -H "Authorization: token ${GITEA_TOKEN}" \
  "https://gitea.nautilus.marcusgroup.org/api/v1/repos/cases/${CASE_NUMBER}" \
  -w "http=%{http_code}\n"
```

תוצאה צפויה: `204` (deleted) או `404` (לא נוצר מעולם).

### שלב 4 — וידוא ניקיון

```bash
echo "=== legal-ai ==="
PGPASSWORD=$LEGAL_AI_PG psql -h localhost -p 5433 -U legal_ai -d legal_ai -t -c "
SELECT count(*) FROM cases WHERE case_number = '${CASE_NUMBER}';
"  # → 0

ls /home/chaim/legal-ai/data/cases/${CASE_NUMBER} 2>&1 | head -1
# → "No such file or directory"

echo "=== Paperclip ==="
PGPASSWORD=paperclip psql -h localhost -p 54329 -U paperclip -d paperclip -t -c "
SELECT 'projects:'||count(*) FROM projects WHERE name LIKE '%${CASE_NUMBER}%'
UNION ALL SELECT 'issues:'||count(*) FROM issues WHERE title LIKE '%${CASE_NUMBER}%'
UNION ALL SELECT 'comments:'||count(*) FROM issue_comments WHERE body LIKE '%${CASE_NUMBER}%'
UNION ALL SELECT 'wakeups:'||count(*) FROM agent_wakeup_requests WHERE payload::text LIKE '%${CASE_NUMBER}%';
"  # → all 0

echo "=== Gitea ==="
curl -s -H "Authorization: token ${GITEA_TOKEN}" \
  "https://gitea.nautilus.marcusgroup.org/api/v1/repos/cases/${CASE_NUMBER}" \
  | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('full_name','NOT FOUND'))"
# → NOT FOUND
```

---

## Fallback — אם ה-API נשבר

אם משום מה ה-API DELETE לא עובד (ראינו את זה בעבר עם `delete_case` החסר), עשה DELETE ישיר ב-DB. ה-FK constraints יבצעו את העבודה:

```sql
PGPASSWORD=$LEGAL_AI_PG psql -h localhost -p 5433 -U legal_ai -d legal_ai -c "
DELETE FROM cases WHERE case_number = '${CASE_NUMBER}' RETURNING case_number, title;
"
```

לאחר מכן הסר את הספרייה מהדיסק. הספרייה בבעלות `root` כי ה-container רץ כ-root, אז תצטרך `sudo`:

```bash
sudo rm -rf /home/chaim/legal-ai/data/cases/${CASE_NUMBER}
```

---

## הערות שנלמדו תוך כדי

1. **`heartbeat_runs.wakeup_request_id`** הוא ה-trap היחיד. הוא NO ACTION FK, ולכן חוסם מחיקה של `agent_wakeup_requests`. הפתרון: `UPDATE ... SET wakeup_request_id = NULL` לפני המחיקה. ה-runs עצמם נשמרים כ-audit log (לא הפסד).

2. **פרויקט "name" ב-Paperclip** — לפי הקונבנציה הוא מתחיל ב-"ערר {N}" — לכן `LIKE '%{N}%'` מספיק. אם יש מספר תיקים שמכילים את אותו מספר, להחמיר עם match מלא או לפי `id`.

3. **Container ↔ host file ownership** — קבצים שיוצר ה-container (כולל ספריית התיק) שייכים ל-`root`. מחיקה מהמארח דורשת `sudo`, או דרך docker exec, או דרך ה-API (שמבצעת `rmtree` בתוך ה-container).

4. **`audit_log` ו-`chair_feedback` נשארים** — FK שלהם הוא SET NULL כדי לשמור היסטוריה גם אחרי שהתיק נמחק. אם אתה צריך מחיקה היסטרית מוחלטת, מחק שורות אלה ידנית.

---

## TODO — אוטומציה

ה-runbook הזה ניתן להמרה לסקריפט `scripts/delete-case.sh` שמקבל `CASE_NUMBER` ומבצע את 4 השלבים עם prompt confirmation. עדיין לא הוטמע — נכון להיום העבודה ידנית.

מי שמטמיע: שמור את הסקריפט כ-`destructive` ב-SCRIPTS.md ודרוש `--confirm` או prompt אינטראקטיבי. אסור שיעבוד בלי אישור מפורש.
