/** * בחירת יעד-הסנכרון של הג'וב `sync-case-status` (issue #446). * * המקור לבאג: הג'וב הישן כתב על **כל** issue מקושר-לתיק, כולל sub-issues * ו-issues בסטטוסים סגורים/בהמתנה-לאדם. זה החזיר CMPA-140 מ-`done` ל-`in_progress` * (8125-09-24) — כי sub-task שהושלם עדיין נמנה כ"מקושר". * * המודול הזה טהור בכוונה — בלי import מה-SDK ובלי side effects — כדי שאפשר * יהיה לייבא אותו ישירות בטסט בלי להריץ את `runWorker(plugin, import.meta.url)` * שקורה בזמן import של worker.ts. */ /** "סגור" — ההגדרה היחידה. מראה של web/paperclip_client.py:561 ב-legal-ai. */ export const CLOSED_ISSUE_STATUSES: ReadonlySet = new Set([ "done", "cancelled", ]); /** * מצב בבעלות Paperclip / בהמתנה-לאדם — **לא** הגדרה שנייה של "סגור". * `in_review` הוא מצב-ההמתנה-ליו"ר המכוון: CEO שמשאיר issue ב-`in_progress` * מקבל auto-block מ-Paperclip תוך דקה, ולכן הוא מעביר ל-`in_review` * (legal-ai/docs/paperclip-quirks.md §3). כתיבת-סטטוס אוטומטית עליו גונבת את * ה-issue מתור-הביקורת של היו"ר ומזמינה קרב-סטטוסים. * `blocked` — כתיבה עליו מסתירה חוסם קיים. */ export const NON_WRITABLE_STATUSES: ReadonlySet = new Set([ "in_review", "blocked", ]); export interface SyncCandidate { id: string; status: string; parentId: string | null; } export type SyncTargetReason = | "ok" | "no_linked_issues" | "no_writable_root" | "ambiguous_writable_roots"; export interface SyncTargetResult { target: SyncCandidate | null; reason: SyncTargetReason; /** כמה שורשים ברי-כתיבה נמצאו — לצורך לוג. */ writableRoots: number; } export function isWritableStatus(status: string): boolean { return ( !CLOSED_ISSUE_STATUSES.has(status) && !NON_WRITABLE_STATUSES.has(status) ); } export function pickSyncTargetIssue( candidates: readonly SyncCandidate[], ): SyncTargetResult { if (candidates.length === 0) { return { target: null, reason: "no_linked_issues", writableRoots: 0 }; } const roots = candidates.filter( (c) => c.parentId === null && isWritableStatus(c.status), ); if (roots.length === 1) { return { target: roots[0], reason: "ok", writableRoots: 1 }; } if (roots.length === 0) { return { target: null, reason: "no_writable_root", writableRoots: 0 }; } return { target: null, reason: "ambiguous_writable_roots", writableRoots: roots.length, }; }