הג'וב סיים עד כה בשורה אחת — `casesChecked` — שאינה מבחינה בין "אין מה
לעשות", "נדחה בכוונה" ו"נפל". 13,872 ריצות דיווחו בריאות ירוקה, ובפועל
היו בהן 389 כתיבות בלבד; האבחנה ב-#604 נבנתה על היעדר-ראיה.
כל ריצה פולטת מעתה: scanned · matched · written · declined{no_linked_issues,
no_writable_root, ambiguous_writable_roots, unknown_status, already_matching}.
הפליטה יושבת ב-`finally` וזורקת את השגיאה המקורית הלאה, כך שריצה שנפלה
נשארת `failed` ב-`plugin_job_runs` **וגם** נושאת את המונים שהספיקה לצבור.
שני משטחים קיימים וצורכים, שנמדדו:
- `ctx.logger.info` → stdout של pm2 — שם בוצע בפועל האבחון של #626/#637.
- `ctx.metrics.write` → `plugin_logs` (level='metric') — הטבלה שפאנל
"Recent Logs" בדף-הפלאגין מרנדר, וניתנת לשאילתת-SQL (AC4).
המונים בתוך מחרוזת-ההודעה ולא רק ב-meta, כי שני המשטחים מתעלמים מ-meta
בפועל (הפאנל מרנדר createdAt/level/message בלבד; ומ-pino נמדד ששדה
`{error:…}` נופל). נדרשה הרשאת `metrics.write` ב-manifest — זו הדרך
היחידה לכתוב ל-`plugin_logs`.
אפס שינוי בהתנהגות-הכתיבה: `sync-target.ts` לא נגע כלל, ובכלל זה
`NON_WRITABLE_STATUSES` (הכרעת-יו"ר, #626).
Closesezer-mishpati/legal-ai#617
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ה-manifest.tools הוא המקור-הקובע לרישום/חשיפת כלים ב-Paperclip — ctx.tools.register
ב-worker.ts נותן רק את ה-handler. בלי הצהרה ב-manifest, הכלי לא נחשף (הלוגים הראו
toolCount=8, בלי legal_predecessor_context). מוסיף את ההצהרה → toolCount=9, הכלי חשוף.
אומת חי: reinstall+restart → logs מראים toolCount:9 + legal_predecessor_context רשום.
(המשך #220; משלים את PR #5.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The event-driven comment→CEO route (issue.comment.created) is best-effort:
a failed/coalesced/cancelled CEO invoke, or a comment that lands while an
agent is mid-run, can leave a chair comment unrouted with nothing to retry.
This is exactly how a chair comment on CMP-190 (2026-06-30) was silently
dropped — the host-native wake was cancelled (issue_execution_promoted /
assignee_changed) and no plugin route fired.
Adds a reconciliation sweep (route-pending-comments, every 2 min) that
re-routes any active issue whose LAST comment is the chair's (no agent reply
after it) and whose id ≠ the per-issue `last-routed-comment-id` marker.
Both the fast path and the sweep stamp the marker → idempotent, no
double-routing of healthy comments. "Last comment is user" precisely targets
the failure (chair commented, no agent response) and skips historically
answered comments, so first-run-after-deploy footprint is 0.
- worker.ts: extract shared routeCommentToCeo() + markCommentRouted();
event handler stamps marker (incl. the native-handles-it branch);
register route-pending-comments sweep with per-company/per-issue guards.
- manifest.ts: declare route-pending-comments job (*/2 * * * *).
Invariants: G12 (Paperclip touch only in the declared wrapper — plugin),
G2 (extends the existing route, no parallel path). Refs legal-ai #164.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Plugin now mounts two React components inside Paperclip via the
SDK's UI slot mechanism. Both are read-only views over data the
plugin worker fetches from legal-ai on demand.
## Slots
* **LegalCaseTab** (type: detailTab, entityTypes: ["issue"])
Mounted as a "ערר" tab on every issue page. Shows case summary
(status / practice_area / appeal_subtype), legal_arguments
grouped by party (עוררים/ועדה/משיבה/מבקשי היתר), attached
precedents, and open missing_precedents.
* **LegalCasesWidget** (type: dashboardWidget)
Dashboard tile with case counts by status + 7-day activity.
## Worker handlers (ctx.data.register)
Five handlers added at the end of setup() — all read-only over the
existing legal-ai HTTP API, all wrapped in try/catch so a transient
failure shows a placeholder instead of crashing the host:
- legal-case-summary → /api/cases/{n}/details
- legal-case-arguments → /api/cases/{n}/legal-arguments
- legal-case-precedents → /api/cases/{n}/precedents
- legal-case-missing-precedents → /api/missing-precedents?case_number=&status=open
- legal-dashboard-stats → in-memory aggregation over /api/cases
case_number is resolved from plugin state (scopeKind=issue,
stateKey=legal-case-number) — populated by legal_case_create.
## Build pipeline
- esbuild.ui.config.mjs uses createPluginBundlerPresets from the SDK
to build src/ui/index.tsx → dist/ui/index.js (13.5kb, react +
@paperclipai/plugin-sdk/ui externalized)
- package.json: build = "build:worker" (tsc) + "build:ui" (esbuild)
- tsconfig.json: jsx=react-jsx, lib += DOM
- New deps: react@19, @types/react, esbuild
## Manifest
- capabilities += ui.detailTab.register, ui.dashboardWidget.register
- entrypoints.ui = "dist/ui"
- ui.slots declared with entityTypes (not "entities" — fixed against
PluginUiSlotDeclaration validator)
## Verified
- tsc + esbuild + biome clean
- Plugin re-installs (20 capabilities) and activates with worker
+ 8 tools + 3 jobs + 1 webhook + 2 event subs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two new event types accepted on the existing case-status webhook
(eventType discriminator added; legacy status_change still default):
* **missing_precedent_created** → ``ctx.issues.askUserQuestions``
with a single-choice question {upload | irrelevant | defer}.
continuationPolicy=wake_assignee_on_accept routes the chair's
answer straight back to the CEO heartbeat without an extra hop.
* **export_complete** → ``ctx.issues.documents.upsert`` with a
markdown "final-decision" doc that links back to the DOCX on
legal-ai. (The SDK's documents API stores text only — binary
attachment isn't natively supported here.)
Manifest: +issue.documents.write capability (issue.interactions.create
was already declared in the previous SDK upgrade).
Tested: plugin activates with all 18 capabilities, 8 tools + 3 jobs
+ 1 webhook + 2 event subs registered.
200-version bump from 2026.325.0 → 2026.525.0 (matches host).
**Why now**: host is already on 2026.525.0; staying on 325 was unnecessary
technical debt. Production logs showed real bugs ("missing, expired,
or unknown invocation scope") that newer SDK versions are known to
have fixed.
**Compatibility**:
- TS build clean with zero changes to worker.ts or legal-api.ts.
- apiVersion: 1 still the schema (no v2 yet).
- Plugin activated successfully — 8 tools + 3 jobs + 1 webhook + 2 event
subs all registered, worker reports "Legal AI plugin ready".
**Manifest gaps fixed**: added 3 capabilities that the worker actually
uses (one of them was the root cause of repeated host-side errors):
* `agents.invoke` — required by `ctx.agents.invoke()` calls (CEO wakeup
from event handlers + comment routing). Previously the plugin was
invoking agents without the declared capability, raising
"missing scope" errors on every issue.created event.
* `issue.comments.read` — required by `ctx.issues.listComments()`
calls in the comment routing path.
* `issue.interactions.create` — forward-looking capability for the
AskUserQuestion-like flows the SDK exposes via `createInteraction`.
**Backup**: full 5-layer snapshot at /tmp/plugin-legal-ai-backup-*.tar.gz
+ tag `pre-sdk-upgrade-2026-05-26` (pushed). Rollback: `git reset --hard
pre-sdk-upgrade-2026-05-26 && npm ci && npm run build && reinstall`.
Closes TaskMaster #26.
Declare two new cron jobs in plugin.json and manifest.ts, and implement
their handlers in worker.ts. stale-case-reminder runs daily at 08:00 and
posts a warning comment on any Paperclip issue linked to a legal-ai case
that has not been updated in 3+ days. weekly-feedback-analysis runs every
Sunday at 19:00, fetches the weekly chair-feedback summary from legal-ai,
and invokes the CEO agent to update decision-lessons.md with new lessons.
Add webhooks[] array to plugin.json and manifest.ts with the
'case-status' endpointKey. Without this declaration, Paperclip
registers 0 webhooks even when onWebhook() is implemented, so
POST /api/plugins/marcusgroup.legal-ai/webhooks/case-status would
return 501 and never reach the handler.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>