Files
plugin-legal-ai/src/sync-target.ts
Chaim 378e5657b5 fix(sync): סנכרון-הסטטוס יכתוב רק על issue-שורש פתוח, לא על sub-task שנסגר (#446)
הג'וב sync-case-status כתב על כל issue מקושר-לתיק שסטטוסו שונה מהיעד — השומר
היחיד היה 'issue.status !== targetStatus'. לכן הוא החזיר sub-tasks שנסגרו
(done) ל-in_progress, כפי שתועד ב-CMPA-140 / תיק 8125-09-24.

בחירת-היעד חולצה ל-src/sync-target.ts: נבחר שורש יחיד (parentId === null)
שאינו done/cancelled ואינו in_review/blocked; אפס או יותר-מאחד → לא נכתב
דבר, ונרשם לוג עם הסיבה. שני הקבועים מובחנים בכוונה — CLOSED_ISSUE_STATUSES
הוא ההגדרה היחידה של 'סגור' (מראה של legal-ai/web/paperclip_client.py:561),
ו-NON_WRITABLE_STATUSES הוא 'מצב בבעלות Paperclip/בהמתנה-לאדם': כתיבת
in_progress על issue ב-in_review מזמינה auto-block תוך דקה
(legal-ai/docs/paperclip-quirks.md §3) וגונבת אותו מתור-הביקורת של היו"ר.

הלולאה הכפולה הוחלפה במעבר אחד + קיבוץ למפת case_number→מועמדים; הקיבוץ
נדרש כדי לבחור 'השורש היחיד', וכתוצר-לוואי O(NxM)→O(M) קריאות state.get.

מבחן-רגרסיה (node --test, ללא תלויות חדשות) משחזר את 11 ה-issues שנמדדו
לתיק 8125-09-24 ומוכיח את הדלתא: הכלל הישן היה כותב על CMPA-140, החדש לא.

בבעלות ריפו זה בהחלטת פאנל — העברת הלוגיקה ל-legal-ai נפסלה כי היא מוסיפה
UPDATE ישיר ל-DB של Paperclip, בהפרת INV-INT4 (GAP-24/25).
2026-08-24 07:41:32 +03:00

80 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* בחירת יעד-הסנכרון של הג'וב `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<string> = 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<string> = 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,
};
}