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>
10 KiB
HEARTBEAT.md — רשימת ביצוע לכל ריצה
שפה — כלל עליון
כל הפלט שלך חייב להיות בעברית בלבד. זה כולל:
- Comments ב-Paperclip
- הודעות סטטוס
- תיאורי שגיאות
- סיכומים ודיווחים
- חשיבה פנימית (thinking)
אין יוצאים מן הכלל. גם שמות tools, פקודות, ונתיבי קבצים — ההסבר סביבם בעברית.
הרץ את הרשימה הזו בכל heartbeat.
1. זיהוי וסינון חברה
- וודא שאתה יודע מי אתה:
$PAPERCLIP_AGENT_ID - בדוק הקשר:
$PAPERCLIP_TASK_ID,$PAPERCLIP_WAKE_REASON - זהה את החברה שלך:
$PAPERCLIP_COMPANY_ID
⚠️ סינון תיקים לפי חברה — כלל ברזל
אתה אחראי רק על תיקים ששייכים לחברה שלך. הספרה הראשונה של מספר התיק קובעת:
| חברה | COMPANY_ID | סוגי תיקים | טווח מספרים |
|---|---|---|---|
| ועדת ערר רישוי ובניה | 42a7acd0-30c5-4cbd-ac97-7424f65df294 |
רישוי ובניה | 1xxx |
| ועדת ערר היטלי השבחה | 8639e837-4c9d-47fa-a76b-95788d651896 |
היטל השבחה + פיצויים ס' 197 | 8xxx, 9xxx |
- אם
$PAPERCLIP_COMPANY_ID=42a7acd0...→ עבוד רק על תיקים שמתחילים ב-1 - אם
$PAPERCLIP_COMPANY_ID=8639e837...→ עבוד רק על תיקים שמתחילים ב-8 או 9 - לעולם אל תיצור פרויקט, issue, או תוכן לתיק שלא בטווח שלך
- אם issue שהוקצה לך מכוון לתיק שלא בטווח שלך — סרב בנימוס ודווח ב-comment
2. בדוק תיבת דואר
curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" "$PAPERCLIP_API_URL/api/agents/me/inbox-lite"
- תעדוף:
in_progressקודם, אחר כךtodo - אם
PAPERCLIP_TASK_IDמוגדר — תעדף אותו
2b. קרא תגובות אחרונות על ה-issue
לפני שאתה מתחיל לעבוד, בדוק אם יש comments חדשים מחיים:
curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
"$PAPERCLIP_API_URL/api/issues/{issue-id}/comments" | jq '[.[] | select(.authorUserId != null)] | .[-3:]'
- אם יש comment מחיים (authorUserId, לא authorAgentId) שנכתב אחרי ה-comment האחרון שלך — קרא אותו בתשומת לב
- אם ה-comment מכיל הוראות עבודה — עקוב אחריהן
- אם ה-comment מזכיר קובץ שהועלה — בדוק attachments (ראה 2c)
- אם ה-comment מבקש להעביר לסוכן אחר — עצור, פרסם comment שמאשר, והעֵר את ה-CEO
2c. בדוק קבצים מצורפים
אם comment מחיים מזכיר קובץ או טיוטה:
PGPASSWORD="paperclip" psql -h 127.0.0.1 -p 54329 -U paperclip -d paperclip -c "
SELECT a.original_filename, a.content_type, a.object_key, a.byte_size
FROM issue_attachments ia
JOIN assets a ON a.id = ia.asset_id
WHERE ia.issue_id = '{issue-id}'
ORDER BY ia.created_at DESC LIMIT 5;"
- נתיב מלא לקובץ:
/home/chaim/.paperclip/instances/default/data/storage/{object_key} - קבצי DOCX — קרא אותם עם
Read - השתמש בתוכן הקובץ כקלט לעבודתך
3. Checkout ועבודה
curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
"$PAPERCLIP_API_URL/api/issues/{issue-id}/checkout"
- עבוד על המשימה לפי ההוראות ב-AGENTS.md שלך
- השתמש בכלים המשפטיים (legal-ai MCP)
⚠️ self-recovery — issue ב-todo עם תוצרים קיימים
ל-Paperclip יש באג ידוע: לאחר ש-issue מתעדכן ל-done, מנגנון issue.released מחזיר אותו ל-todo תוך כ-30 שניות (תועד ב-docs/paperclip-quirks.md §1). זה גורם ל-wakeup חוזר של אותו סוכן על משימה שכבר בוצעה.
לפני שאתה מתחיל עבודה — בדוק שהמשימה לא בוצעה כבר:
- בדוק תוצרים בדיסק:
Globעל תיקיות ה-output הצפויות ({case_dir}/documents/research/*.mdלחוקר,analysis-and-research.mdלמנתח, וכו') - בדוק תוצרים ב-DB: דרך MCP —
precedent_list,get_claims,extract_appraiser_facts(status=completed) - בדוק comments קודמים על ה-issue — אם הסוכן הקודם פרסם "הושלם בהצלחה" מסוף-מצב
אם הכל קיים ותקין: אל תבצע עבודה כפולה. במקום זאת:
- פרסם comment קצר: "אין שינוי — כל התוצרים קיימים מהריצה הקודמת (X פריטים ב-DB, קובץ Y בדיסק). סוגר את ה-issue."
PATCH /api/issues/{id}→done- צא נקי
אם משהו חסר/שונה: עבוד על מה שחסר בלבד, לא על הכל מחדש.
4. דיווח — חובה!
לפני שאתה מסיים, תמיד:
4א. פרסם comment על ה-issue
ל-body קצר (<500 תווים, בלי backticks/קוד/נתיבים):
curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "Content-Type: application/json" \
"$PAPERCLIP_API_URL/api/issues/{issue-id}/comments" \
-d '{"body": "סיכום העבודה..."}'
ל-body ארוך / markdown עם נתיבים בbacktick / קוד — חובה שתי פעולות נפרדות:
-
כתוב את ה-JSON לקובץ זמני דרך Write tool (לא דרך bash heredoc):
Write(file_path="/tmp/comment-{issue-id}.json", content=json.dumps({"body": markdown_body}, ensure_ascii=False)) -
אז
curl -d @fileשקורא את הקובץ ישירות — בלי 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-{issue-id}.json
⚠️ למה לא bash heredoc / python3 -c: backticks ב-markdown (`path/to/file`) ייפרשו על ידי bash כ-command substitution גם כשהם בתוך מחרוזת Python. תקבל שגיאת Permission denied מטעה (bash מנסה להריץ את הנתיב כפקודה). הפתרון של temp-file חוסם את כל ה-shell quoting traps. תועד ב-docs/paperclip-quirks.md §2.
4ב. קבע סטטוס — done או blocked
אם המשימה הושלמה בהצלחה (כל המסמכים חולצו, כל הבדיקות עברו, אין חסימות):
curl -s -X PATCH -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "Content-Type: application/json" \
"$PAPERCLIP_API_URL/api/issues/{issue-id}" \
-d '{"status": "done"}'
אם המשימה נכשלה או חסומה (מסמך לא חולץ, timeout, חוסר מידע, שגיאה שלא ניתנת לפתרון):
curl -s -X PATCH -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "Content-Type: application/json" \
"$PAPERCLIP_API_URL/api/issues/{issue-id}" \
-d '{"status": "blocked"}'
אסור לסיים issue כ-"done" אם יש כשל שלא טופל. "done" = הכל הושלם בהצלחה. אם משהו נכשל — "blocked".
4ג. העֵר את העוזר המשפטי (CEO) — חובה!
אחרי כל סיום משימה (done או blocked), העֵר את העוזר המשפטי של החברה שלך כדי שיבדוק תוצאות ויחליט על הצעד הבא:
⚠️ בחר CEO לפי חברה:
| חברה | COMPANY_ID | CEO Agent ID |
|---|---|---|
| רישוי ובניה (CMP) | 42a7acd0-... |
752cebdd-6748-4a04-aacd-c7ab0294ef33 |
| היטלי השבחה (CMPA) | 8639e837-... |
cdbfa8bc-3d61-41a4-a2e7-677ec7d34562 |
# קבע CEO_ID לפי חברה:
if [ "$PAPERCLIP_COMPANY_ID" = "8639e837-4c9d-47fa-a76b-95788d651896" ]; then
CEO_ID="cdbfa8bc-3d61-41a4-a2e7-677ec7d34562"
else
CEO_ID="752cebdd-6748-4a04-aacd-c7ab0294ef33"
fi
curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
-H "Content-Type: application/json" \
"$PAPERCLIP_API_URL/api/agents/$CEO_ID/wakeup" \
-d '{"source":"automation","triggerDetail":"system","reason":"סוכן [שמך] סיים משימה [issue-id] בסטטוס [done/blocked]","payload":{"issueId":"[issue-id]","mutation":"agent_completion"}}'
⚠️ כללי ברזל — Paperclip API:
- אסור
INSERT INTO agent_wakeup_requests— לא יוצר heartbeat_run, הסוכן לא יתעורר לעולם - חובה
payload.issueIdבכל wakeup — בלי זה הסוכן מתעורר בלי הקשר (בלי תיק, בלי cwd) - agent JWT לא יכול להעיר סוכנים אחרים — רק את עצמו. כדי להעיר סוכן אחר → צור issue + הקצה אליו (Paperclip מפעיל wakeup אוטומטי)
נתיבי API:
| פעולה | נתיב |
|---|---|
| פרסום comment | POST /api/issues/{issue-id}/comments |
| יצירת issue | POST /api/companies/{company-id}/issues |
| עדכון issue | PATCH /api/issues/{issue-id} |
| wakeup עצמי/CEO | POST /api/agents/{agent-id}/wakeup (עם payload!) |
5. התראת מייל — כשנדרשת תשובה אנושית
כשהתוצאה דורשת החלטה או תשובה של חיים, שלח מייל:
python3 /home/chaim/legal-ai/scripts/notify.py \
"נדרשת תשובתך — [תיאור קצר]" \
"תוכן ההודעה עם סיכום מה נדרש"
מתי לשלוח — תמיד:
- סיום כל משימה — עם סיכום קצר של מה בוצע
- בקשה לקביעת תוצאה (דחייה/קבלה/חלקית)
- בקשה לאישור כיוון נימוק
- דוח QA שנכשל (צריך החלטה על תיקונים)
- החלטה מוכנה לביקורת דפנה
- כל מצב שדורש פעולה אנושית ולא יכול להתקדם לבד
- שגיאה שלא ניתן לפתור ללא התערבות
מתי לא לשלוח:
- עדכוני סטטוס ביניים (רק בסיום)
- שגיאות טכניות שאפשר לפתור לבד
6. Release
curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
"$PAPERCLIP_API_URL/api/issues/{issue-id}/release"