Files
legal-ai/docs/paperclip-quirks.md
Chaim 6a38789379
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 7s
docs+heartbeat: paperclip quirks + temp-file pattern + self-recovery
Two latent issues surfaced today while watching the case 8174-24
end-to-end run, both worth documenting and engineering around because
they will recur on every future case.

Bug 1 — issue.released flips done→todo
  After an agent successfully PATCHes its issue to "done", Paperclip's
  internal issue.released action reverts the status to "todo" within
  ~30 seconds. This triggers a fresh wakeup of the same agent on a
  task that is already complete.
  Reproduced on CMPA-18 (30/04/26):
      18:14:57  agent PATCH → status: done
      18:15:35  Paperclip   → issue.released → status: todo
      18:15:54  new researcher run started
  The fix at the right altitude (Paperclip itself) is outside our repo.
  Mitigation in HEARTBEAT.md §3 — when an agent boots and finds the
  issue in `todo` while expected outputs (file, DB rows) already exist,
  it must short-circuit: post a "no change" comment, PATCH back to done,
  and exit. Costs ~$0.20 per false wakeup but breaks the loop.

Bug 2 — Bash backtick trap on long comment bodies
  Researcher agent built a curl pipeline like:
      curl ... -d "$(python3 -c "body = '''...
        📁 קובץ מחקר: `/path/to/file.md`
        '''")"
  The backticks around the file path (markdown convention) get
  evaluated by the OUTER bash $(...) as command substitution. Bash
  then tries to exec /path/to/file.md, which is not executable, and
  prints "Permission denied" — a misleading error since the actual
  file ownership is fine. The curl itself succeeded; only the bash
  prelude noised up the log.
  Fix in HEARTBEAT.md §4א: long bodies must go via Write→tempfile
  then `curl -d @file`. Avoids every shell quoting edge case.

Files:
  • docs/paperclip-quirks.md — new. Full writeup of both bugs plus
    two prior known-quirks (CEO auto-block in_progress, INSERT vs
    API for wakeups). Each section: what happens, empirical evidence
    from logs, impact, workaround, status.
  • .claude/agents/HEARTBEAT.md — added the self-recovery section to
    §3 and the temp-file pattern to §4א. The temp-file pattern is the
    canonical answer for any agent posting markdown comments —
    applies to all 7 agents in this skill set.
  • CLAUDE.md — referenced the new doc from the docs index.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 18:23:32 +00:00

158 lines
7.6 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.
# Paperclip Quirks — מלכודות ידועות
> **הקשר:** מה ש-Paperclip עושה בעצמו, מתחת לרגליהם של הסוכנים שלנו, ושאנחנו צריכים לעקוף אותו או לחיות איתו.
>
> כל מלכודת מתועדת עם:
> 1. מה קורה בפועל
> 2. ראיה אמפירית מתוך לוגים
> 3. ההשפעה על הצינור שלנו
> 4. עקיפה / תיקון / קבלה
---
## 1. `issue.released` הופך `done` ל-`todo`
### מה קורה
לאחר שסוכן מבצע `PATCH /api/issues/{id}` עם `status: done`, **Paperclip מבצע פעולה נוספת בשם `issue.released`** מספר שניות מאוחר יותר. ל-`issue.released` יש side-effect לא-מתועד שמחזיר את ה-status ל-`todo`.
### ראיה אמפירית — תיק 8174-24, CMPA-18 (30/04/26)
מתוך `activity_log`:
```
ts | action | actor_type | details
----------+---------------------+------------+----------------------------------------
18:14:49 | issue.comment_added | agent | comment by researcher
18:14:57 | issue.updated | agent | {"status": "done", "_previous": {"status": "in_progress"}}
18:15:35 | issue.released | agent | ← here
```
מצב מ-`issues` table 38 שניות לאחר ה-`released`:
```
identifier | status | updated_at
CMPA-18 | todo | 18:15:35
```
ה-status חזר מ-`done` ל-`todo` למרות שאף סוכן או משתמש לא ביקש זאת.
### ההשפעה על הצינור שלנו
Paperclip מזהה issue ב-`todo` כ"יש עבודה לעשות" → מיד מפעיל wakeup לסוכן הרלוונטי → הסוכן רץ שוב עם prompt cache מלא (~$0.10-0.50 פר-ריצה) → מסתכל סביב ומבין שהעבודה כבר נעשתה → סוגר את ה-issue שוב → `issue.released` חוזר על עצמו ⇒ פוטנציאל ללולאה.
### עקיפה — בצד שלנו (ללא תיקון Paperclip)
הסוכן שלנו **עושה זאת כבר היום בהצלחה** במקרה שהוא רואה issue ב-`todo` עם תוצרים קיימים:
1. בודק שהקבצים הצפויים קיימים (`Glob /documents/research/*.md`)
2. בודק שה-DB מאוכלס (`mcp__legal-ai__precedent_list`, `get_claims`, וכו')
3. אם הכל קיים → לא מבצע עבודה כפולה → כותב comment "אין שינוי" → `PATCH issue → done`
**הראיה:** בריצה החוזרת (PID 309786 ב-30/04/26 18:15:54), המנתח של החוקר זיהה תוך 90 שניות שכל 9 התקדימים והקובץ קיימים, וסגר את ה-issue ב-`PATCH → done` שוב. הריצה הזאת עלתה כ-$0.20 — לא חינם, אבל לא לולאה.
### אם תרצה לחקור פנימה
ה-`issue.released` נרשם ב-`activity_log` עם `actor_type=agent` אבל בלי `agent_id` שמסביר מי. הוא לא נכתב על ידי הסקריפטים שלנו (אנחנו לא קוראים endpoint כזה). מקור אפשרי:
- מנגנון `executionLockedAt` / `executionWorkspaceId` של Paperclip שמשחרר משאבים אחרי שריצה מסתיימת ובמקביל מאפס status
האפשרות הנכונה לסגור את הבאג היא **ב-Paperclip עצמו** — לתקן את `issue.released` שלא ידרוס status מסוף-מצב כמו `done`. עד שזה נסגר אצלם, אנחנו חיים עם self-recovery.
### סטטוס
- **לא נסגר ב-Paperclip** (ידוע לפי 30/04/26)
- **טופל בצד שלנו** דרך self-recovery בסקייל של הסוכן (HEARTBEAT.md §4-recovery)
- **לתעד עלות**: כל ריצת self-recovery מוסיפה ~$0.20 לתיק
---
## 2. Bash backtick trap בעת בניית comment body דרך curl
### מה קורה
הסוכן בונה pipeline מורכב כדי לפרסם comment עם markdown ארוך:
```bash
curl ... -d "$(python3 -c "
body = '''## כותרת
📁 קובץ: \`/path/to/file.md\`
'''
print(json.dumps({'body': body}))")"
```
ה-`bash` שמריץ את ה-`$(...)` הראשון רואה את ה-backticks (` ` ` ) בתוך המחרוזת של Python ומפרש אותם **כ-command substitution של bash**. הוא מנסה להריץ את `/path/to/file.md` כפקודה, ומכיוון שהקובץ לא executable — מחזיר:
```
/bin/bash: line 56: /path/to/file.md: Permission denied
```
### ההטעיה
ההודעה `Permission denied` היא **לא** באמת בעיית הרשאות:
- `ls -la` מראה שהקובץ הוא `chaim:chaim` עם `-rw-r--r--`
- `touch` ידני באותו נתיב מצליח
- ה-Write tool כבר כתב את הקובץ הזה בהצלחה דקה קודם
### למה זה קורה דווקא בנתיבי מסמכים
Backticks הם תחביר markdown נפוץ לציטוט נתיבים: `` `/home/chaim/...` ``. בפלט markdown זה נכון, אבל כשהסוכן מטמיע את ה-markdown בתוך bash heredoc / command substitution, ה-backticks מפעילים את עצמם.
### תיקון — דפוס "כתוב לקובץ זמני אז curl -d @file"
במקום:
```bash
curl ... -d "$(python3 -c "...long body with backticks...")"
```
עשה:
```python
# 1. כתוב את ה-body לקובץ זמני דרך Write tool (בלי שום bash quoting)
Write("/tmp/comment.json", json.dumps({"body": markdown_body}))
```
```bash
# 2. אז curl קורא מהקובץ — אין shell expansion על התוכן
curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "Content-Type: application/json" \
"$PAPERCLIP_API_URL/api/issues/{issue-id}/comments" \
-d @/tmp/comment.json
```
הנתיב `-d @file` קורא את התוכן של הקובץ **בלי שום ניתוח** — אין shell, אין quoting, אין backticks-as-commands. זה גם מאפשר body של 10K+ תווים ללא הגבלת ARG_MAX.
### סטטוס
- **תיעוד ב-HEARTBEAT.md** עם הוראה מפורשת להשתמש ב-Write+`-d @file` ל-bodies מעל 500 תווים
- **השפעה היסטורית**: לפני התיקון, הריצה ב-CMPA-18 (30/04/26) הצליחה (curl באמת רץ) — אבל ה-`Permission denied` בלוג היה מבלבל וגרם לחקירה. עתה שהסיבה ידועה, אפשר להתעלם.
---
## 3. CEO main issue auto-block ב-`in_progress`
### מה קורה
CEO שמסיים turn (פרסם comment "ממתין לסיום של סוכן Y") ומשאיר את ה-issue ב-`in_progress` יקבל auto-block תוך דקה אחת מ-Paperclip ("live execution disappeared"). הסטטוס יקפוץ ל-`blocked` ויידרש wakeup ידני להמשיך.
### עקיפה
CEO צריך להעביר את ה-issue ל-`in_review` (לא `in_progress`) כשהוא ממתין למשאב חיצוני (סוכן אחר, יו"ר). זה מתועד ב-CLAUDE.md זיכרון: `feedback_paperclip_enums.md`.
### סטטוס
- **תיקון ב-`legal-ceo.md`** (commit a1969dd)
- נצפה עובד ב-CMPA-15 ב-30/04/26 — ה-CEO עבר ל-`in_review` נכון
---
## 4. Wakeup דרך DB ישיר ≠ wakeup דרך API
### מה קורה
`INSERT INTO agent_wakeup_requests` ידני בלי לעבור דרך `POST /api/agents/{id}/wakeup` יוצר רשומת wakeup אבל **לא יוצר `heartbeat_run`**. בלי `heartbeat_run`, ה-runtime של Paperclip לא מזהה שיש משהו להריץ → הסוכן לעולם לא מתעורר.
### עקיפה
תמיד להשתמש ב-API. כל הסקייל שלנו תועדו עם האזהרה הזאת.
### סטטוס
- **תיקון בכל הסקייל** (CLAUDE.md זיכרון: `reference_paperclip_wakeup.md`)