feat(escalation): primitive הסלמה-לאדם loop-safe בשער-הפלטפורמה (#218)
מוסיף escalate_issue(issue_id, severity, reason) — חלופה loop-safe להשארת
issue משויך-סוכן+blocked, שהיא מקור לולאות source_scoped_recovery_action /
stranded_assigned_issue / issue_reopened_via_comment (reference_paperclip_recovery_loops).
שני אפקטים בטרנזקציית-DB אחת, במנגנון הקנוני של reset_case_agents (raw SQL, לא REST):
- מעבר-אטומי למצב human-owned {status:in_review, assignee_agent_id:null,
assignee_user_id:CHAIM_USER_ID} (rule #7). direct-DB בכוונה — עוקף את
disposition-resolver של Paperclip שהוא-עצמו מקור-הלולאות; נוחת human-owned בשוט
אחד בלי done→todo flip.
- הערת-severity עמידה author_type='system' — אינרטית ל-route-pending-comments
(מנתב רק author_type='user'), לכן לא מעירה CEO. אפס wakeup — המטרה היא מסירה-לאדם.
חיווט:
- Port: pc_escalate_issue עטוף ב-agent.escalated (מרכיב על טלמטריית #219 —
הסלמות נספרות per-case באותו זרם כמו ה-wakeups שהן מחליפות).
- app.py: POST /api/cases/{case}/agents/escalate (Literal severity).
- 3 בדיקות pytest (fake-asyncpg): severity לא-תקין, happy-path (מעבר+system-note+
אפס-wakeup), issue-לא-נמצא.
Invariants: מקיים G2 (אותו מנגנון-העברה-לאדם כמו reset_case_agents, לא מסלול מקביל),
G12/INV-PORT1 (המגע מהשער בלבד; ליבה נקייה), §6 (אין בליעה שקטה — severity לא-תקין
וissue-חסר מוחזרים מפורשות).
follow-up מוצהר: חיווט-סוכנים (MCP tool + HEARTBEAT §4 להחליף blocked+prose),
auto-escalation מה-reaper, והתראת-מייל (notify.py) לחיים.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,7 @@ from web.paperclip_client import (
|
||||
# ── raw imports: wrapped with telemetry below (state-affecting ops, #219) ──
|
||||
cancel_interaction as _cancel_interaction,
|
||||
cancel_run as _cancel_run,
|
||||
escalate_issue as _escalate_issue,
|
||||
post_comment as _post_comment,
|
||||
reap_stale_interactions as _reap_stale_interactions,
|
||||
reset_agent_session as _reset_agent_session,
|
||||
@@ -107,6 +108,12 @@ pc_post_comment = instrument(
|
||||
pc_cancel_interaction = instrument(
|
||||
"interaction.cancelled", keys=("issue_id", "interaction_id"),
|
||||
)(_cancel_interaction)
|
||||
# First-class escalation-to-chair (#218): the loop-safe alternative to leaving an
|
||||
# issue agent-owned+blocked. Emits a severity-tagged event so escalations are
|
||||
# countable per case in the same stream as the wakeups they replace.
|
||||
pc_escalate_issue = instrument(
|
||||
"agent.escalated", keys=("issue_id", "severity", "company_id", "reason"),
|
||||
)(_escalate_issue)
|
||||
pc_reap_stale_interactions = instrument(
|
||||
"interaction.reaped", result_keys=("cancelled",),
|
||||
)(_reap_stale_interactions)
|
||||
@@ -168,6 +175,7 @@ __all__ = [
|
||||
"pc_respond_to_interaction",
|
||||
"pc_cancel_interaction",
|
||||
"pc_reap_stale_interactions",
|
||||
"pc_escalate_issue",
|
||||
# agent-run observability + control (live view + smart management)
|
||||
"pc_list_live_runs",
|
||||
"pc_get_run_log",
|
||||
|
||||
Reference in New Issue
Block a user