Files
legal-ai/docs/superpowers/plans/2026-05-16-hooks-execution.md
Chaim 4de555367d
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m32s
G12 Leak-Guard / leak-guard (push) Successful in 5s
Lint — undefined names / undefined-names (push) Successful in 11s
chore: fix .gitignore inline comments + add untracked code files
Inline # comments in gitignore are not supported — they were silently
breaking three patterns (data/checkpoints/, data/adapter-migration-state.json,
.claude/agents/.generated/). Moved comments to their own lines and added
missing entries for runtime dirs (data/audit/, data/logs/, etc.) and
temp files (.interaction_tmp.json, .design-build/, .taskmaster bak files).

Also tracks previously untracked legitimate files: scripts, tests, docs,
skills references, .env.example, taskmaster templates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 12:49:32 +00:00

10 KiB
Raw Permalink Blame History

Hooks: Case Status Webhooks Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: כאשר status של תיק משתנה ב-legal-ai (e.g. qa_failed, exported), ה-plugin מקבל webhook, מעדכן את ה-issue ב-Paperclip, ומעיר את ה-CEO במקרה הצורך.

Architecture: Legal-ai REST API קורא pc_request("POST", "/api/plugins/marcusgroup.legal-ai/webhooks/case-status", ...) אחרי כל שינוי status. ה-plugin מטפל ב-onWebhook() ומגיב: מוסיף תגובה לissue, מעיר CEO אם QA נכשל.

Tech Stack: TypeScript (plugin-legal-ai), Python/FastAPI (legal-ai web), @paperclipai/plugin-sdk@2026.325.0, httpx (Python), pc_request helper.


File Map

Action File
Modify plugin-legal-ai/src/worker.ts — add onWebhook() to definePlugin({})
Modify plugin-legal-ai/plugin.json — add "webhooks.receive" capability
Modify plugin-legal-ai/src/manifest.ts — add webhook capability
Modify legal-ai/web/app.py — emit webhook after PUT /api/cases/{case_number}
Modify legal-ai/web/paperclip_api.py — add emit_webhook() helper

Task 1: Add emit_webhook helper ב-Python

Files:

  • Modify: legal-ai/web/paperclip_api.py

  • Step 1: קרא את הקובץ הקיים

head -90 /home/chaim/legal-ai/web/paperclip_api.py
  • Step 2: הוסף את ה-helper בסוף הקובץ

פתח /home/chaim/legal-ai/web/paperclip_api.py והוסף אחרי הפונקציה pc_request:

async def emit_case_status_webhook(
    case_number: str,
    old_status: str,
    new_status: str,
    company_id: str | None = None,
    run_id: str | None = None,
) -> None:
    """Notify the Paperclip plugin that a case status changed.

    Fire-and-forget: logs errors but never raises, so callers aren't blocked.
    """
    try:
        await pc_request(
            "POST",
            "/api/plugins/marcusgroup.legal-ai/webhooks/case-status",
            json={
                "caseNumber": case_number,
                "oldStatus": old_status,
                "newStatus": new_status,
                "companyId": company_id,
                "timestamp": datetime.utcnow().isoformat() + "Z",
            },
            run_id=run_id,
            timeout=5.0,
        )
    except Exception as exc:
        logger.warning("emit_case_status_webhook failed: %s", exc)

הערה: datetime ו-logger כבר מיובאים ב-app.py. בדוק שהם מיובאים גם ב-paperclip_api.py — אם לא, הוסף from datetime import datetime ו-import logging; logger = logging.getLogger(__name__) בראש הקובץ.

  • Step 3: Commit
cd /home/chaim/legal-ai
git add web/paperclip_api.py
git commit -m "feat: add emit_case_status_webhook helper"

Task 2: צרף webhook לendpoint PUT /api/cases/{case_number}

Files:

  • Modify: legal-ai/web/app.py

  • Step 1: מצא את ה-endpoint

grep -n "PUT\|case_number\|update_case" /home/chaim/legal-ai/web/app.py | head -30
  • Step 2: קרא את הenable endpoint המלא

זהה את הסקציה המלאה של ה-endpoint ואת הייבוא הקיים.

  • Step 3: הוסף import ל-emit_webhook

בראש app.py, בסקציית ה-imports מ-paperclip_api:

from .paperclip_api import pc_request, emit_case_status_webhook
  • Step 4: הוסף webhook emit בתוך הendpoint

אחרי שהקוד מעדכן את ה-case (לפני ה-return), הוסף:

# Notify plugin about status change (fire-and-forget)
if updates.get("status") and old_status != updates["status"]:
    background_tasks.add_task(
        emit_case_status_webhook,
        case_number=case_number,
        old_status=old_status,
        new_status=updates["status"],
        company_id=str(case.get("company_id")),
    )

אם ה-endpoint כבר מקבל background_tasks: BackgroundTasks — השתמש בו. אם לא, הוסף background_tasks: BackgroundTasks לחתימת הפונקציה. הוסף from fastapi import BackgroundTasks ל-imports.

  • Step 5: שמור את ה-old_status לפני ה-update

בתחילת ה-endpoint handler, לפני קריאת ה-DB update:

old_status = (await db.get_case(case_number) or {}).get("status", "")
  • Step 6: בדיקה בסיסית — שלח PUT ידני
curl -s -X PUT https://legal-ai.nautilus.marcusgroup.org/api/cases/1130-25 \
  -H "Content-Type: application/json" \
  -d '{"status": "in_progress"}' | jq .status

בדוק ב-Paperclip logs שהwebhook נשלח (עדיין לא מטופל בצד ה-plugin):

pm2 logs paperclip --lines 20
  • Step 7: Commit
cd /home/chaim/legal-ai
git add web/app.py
git commit -m "feat: emit case-status webhook on PUT /api/cases/:case"

Task 3: הוסף onWebhook() ל-plugin

Files:

  • Modify: plugin-legal-ai/src/worker.ts

  • Step 1: קרא את definePlugin({}) הקיים

grep -n "definePlugin\|onWebhook\|onHealth\|onShutdown" /home/chaim/plugin-legal-ai/src/worker.ts
  • Step 2: הוסף את onWebhook handler

בתוך הobject שמועבר ל-definePlugin({}), אחרי setup(ctx):

onWebhook: async (input) => {
  const { endpointKey, payload, companyId } = input as {
    endpointKey: string;
    payload: {
      caseNumber: string;
      oldStatus: string;
      newStatus: string;
      companyId: string;
      timestamp: string;
    };
    companyId: string;
  };

  if (endpointKey !== "case-status") return;

  const { caseNumber, oldStatus, newStatus } = payload;
  ctx.logger.info(`Webhook: ${caseNumber} ${oldStatus}${newStatus}`);

  // Find the Paperclip issue linked to this case
  const stateKey = `case:${caseNumber}`;
  const issueId = await ctx.state.get({ companyId }, stateKey);
  if (!issueId) {
    ctx.logger.warn(`No issue found for case ${caseNumber}`);
    return;
  }

  const statusLabels: Record<string, string> = {
    in_progress: "🔄 בעבודה",
    drafted: "✍️ טיוטה מוכנה",
    qa_failed: "❌ QA נכשל",
    exported: "📄 יוצא ל-DOCX",
    reviewed: "✅ נבדק",
    final: "🎯 סופי",
  };

  const label = statusLabels[newStatus] ?? newStatus;

  // Post a status comment on the issue
  await ctx.issues.createComment({
    issueId: issueId as string,
    body: `**עדכון סטטוס:** ${label} (היה: ${oldStatus})`,
  });

  // Wake CEO if QA failed
  if (newStatus === "qa_failed") {
    const companies = await ctx.companies.list();
    const company = companies.find((c) => c.id === companyId);
    if (!company) return;

    const CEO_IDS: Record<string, string> = {
      "42a7acd0-30c5-4cbd-ac97-7424f65df294": "752cebdd-6748-4a04-aacd-c7ab0294ef33",
      "8639e837-4c9d-47fa-a76b-95788d651896": "cdbfa8bc-3d61-41a4-a2e7-677ec7d34562",
    };
    const ceoId = CEO_IDS[companyId];
    if (ceoId) {
      await ctx.agents.invoke(ceoId, companyId, {
        prompt: `תיק ${caseNumber} נכשל בבדיקת QA. עיין בתוצאות QA ותקן את הבעיות.`,
        reason: "qa_failed webhook",
      });
    }
  }
},

הערה: ctx חייב להיות נגיש ב-onWebhook. אם ctx מוגדר בתוך setup() בלבד — הוצא אותו ל-closure חיצוני של ה-plugin object (ראה את הדפוס הקיים ב-worker.ts).

  • Step 3: בדק TypeScript
cd /home/chaim/plugin-legal-ai && npx tsc --noEmit

Expected: 0 errors.

  • Step 4: Build
cd /home/chaim/plugin-legal-ai && npm run build

Expected: dist/worker.js נוצר ללא שגיאות.

  • Step 5: Commit
cd /home/chaim/plugin-legal-ai
git add src/worker.ts
git commit -m "feat: add onWebhook handler for case-status events"

Task 4: הוסף capability ל-plugin.json ול-manifest.ts

Files:

  • Modify: plugin-legal-ai/plugin.json

  • Modify: plugin-legal-ai/src/manifest.ts

  • Step 1: הוסף "webhooks.receive" ל-capabilities

ב-plugin-legal-ai/plugin.json, בarray "capabilities", הוסף:

"webhooks.receive"
  • Step 2: הוסף גם ב-manifest.ts
grep -n "capabilities\|webhooks" /home/chaim/plugin-legal-ai/src/manifest.ts

הוסף "webhooks.receive" לarray שם.

  • Step 3: Re-install plugin
cd /home/chaim/plugin-legal-ai && npm run build
npx paperclipai plugin uninstall marcusgroup.legal-ai \
  --api-base http://localhost:3100 --api-key pcapi_legal_install_key_2026
npx paperclipai plugin install /home/chaim/plugin-legal-ai \
  --api-base http://localhost:3100 --api-key pcapi_legal_install_key_2026
pm2 restart paperclip
  • Step 4: Commit
cd /home/chaim/plugin-legal-ai
git add plugin.json src/manifest.ts
git commit -m "feat: add webhooks.receive capability to plugin manifest"

Task 5: בדיקה end-to-end

  • Step 1: Deploy legal-ai
cd /home/chaim/legal-ai
git push origin main
# המתן לבנייה (~2-4 דקות)
  • Step 2: שנה סטטוס תיק
curl -s -X PUT https://legal-ai.nautilus.marcusgroup.org/api/cases/1130-25 \
  -H "Content-Type: application/json" \
  -d '{"status": "qa_failed"}' | jq .status
  • Step 3: בדוק שהתגובה הוספה ל-Paperclip issue
# מצא את ה-issue הקשור לתיק 1130-25
pm2 logs paperclip --lines 30 | grep "1130-25\|webhook\|qa_failed"

Expected: תגובה " QA נכשל" הוספה לissue. CEO הועיר.

  • Step 4: בדוק שינוי סטטוס שגרתי (לא QA)
curl -s -X PUT https://legal-ai.nautilus.marcusgroup.org/api/cases/1130-25 \
  -H "Content-Type: application/json" \
  -d '{"status": "drafted"}' | jq .status

Expected: תגובה "✍️ טיוטה מוכנה" בissue. CEO לא הועיר.


אימות סופי

בדיקה פקודה תוצאה מצופה
QA נכשל → CEO מועיר PUT status=qa_failed תגובה + agent invocation
exported → תגובה בלבד PUT status=exported תגובה בלבד
שינוי ללא status PUT title=... שום webhook
תיק ללא issue webhook לתיק חדש לוג warning, ללא crash