מחזור חיי כרטיס
סטטוסי כרטיסים
קטע בשם “סטטוסי כרטיסים”| סטטוס | מחרוזת אחסון | תיאור |
|---|---|---|
backlog |
backlog |
נלכד, אך טרם עבר טריאז’ אל התור הפעיל |
open |
open |
עבר טריאז’ ומוכן לעבודה, אך טרם התחיל |
inProgress |
inProgress |
בעבודה פעילה |
blocked |
blocked |
התחיל אך חסום בגלל משהו חיצוני |
inReview |
inReview |
העבודה הושלמה, ממתין לסקירה |
done |
done |
הסתיים בהצלחה (מצב סיום) |
failed |
failed |
הסתיים בשגיאה (מצב סיום) |
cancelled |
cancelled |
נזנח (מצב סיום) |
ערכי סטטוס נשמרים ב-camelCase (inProgress, inReview), ולעולם לא in_progress. ערך null שמור מפוענח כ-open; ערך לא מוכר זורק שגיאה במקום לכפות המרה.
tryParseLoose מקבל כינויים שמספקים סוכנים, תוך התעלמות מאותיות רישיות ומהתווים - / _ / רווח:
| סטטוס | כינויים מקובלים |
|---|---|
open |
todo, ready, pending, new |
inProgress |
doing, started, active, wip |
blocked |
stuck |
inReview |
review, reviewing |
done |
closed, complete, completed, finished |
failed |
error, errored |
cancelled |
canceled, abandoned, dropped |
מעברי סטטוס
קטע בשם “מעברי סטטוס”backlog ──→ open ──→ inProgress ──→ inReview ──→ done │ │ │ │ └──→ failed ┘ └──→ blocked ──→ inProgress
Any non-terminal status ──→ cancelled; inReview ──→ failedהגרף הזה מחייב סוכנים ואוטומציה בלבד. TicketWorkflowService.transitionStatus בודק אותו כאשר force הוא false — כלי MCP ומנגנוני היישור. מעבר לא חוקי שם נרשם ביומן ומתעלמים ממנו. כל שינוי סטטוס שנעשה בידי משתמש בממשק עובר עם force: true ועוקף את הגרף לחלוטין, כך שאדם רשאי להעביר כרטיס לכל סטטוס שהבורר מציג (כל סטטוס מלבד failed), כולל פתיחה מחדש של כרטיס במצב סיום. סוכנים מסמנים failed עם fail_ticket.
סטטוס רשאי תמיד לעבור לעצמו. למצבי סיום אין קצוות יוצאים.
| מ- | יעדים מותרים |
|---|---|
backlog |
open, cancelled |
open |
inProgress, blocked, cancelled |
inProgress |
blocked, inReview, done, failed, cancelled |
blocked |
inProgress, cancelled |
inReview |
inProgress, done, failed, cancelled |
done |
— (מצב סיום) |
failed |
— (מצב סיום) |
cancelled |
— (מצב סיום) |
עדיפויות כרטיסים
קטע בשם “עדיפויות כרטיסים”נשמרת כמספר שלם לפי הסקאלה המקורית של Linear, 0–4. ערך לא מוכר מפוענח כ-none.
| עדיפות | נשמר כ- |
|---|---|
none |
0 |
urgent |
1 |
high |
2 |
medium |
3 |
low |
4 |
ספקי כרטיסים
קטע בשם “ספקי כרטיסים”ערך שמור לא מוכר או null מפוענח כ-local.
| ספק | סטטוס |
|---|---|
local |
נשמר כולו במסד הנתונים של סביבת העבודה; ברירת המחדל |
linear |
בבעלות מרוחקת, משוקף מקומית |
מתאמי סנכרון יושבים תחת packages/cc_infra/lib/src/tickets/sync/ עבור Linear,
GitHub Issues, Jira Cloud ו-ClickUp. GitHub Issues הוא ספק של מתאם סנכרון
(vendorId: github) בלבד — הוא אינו ערך של TicketProvider, ולכן סוגיה שנמשכה
מ-GitHub נשמרת עם originKind: externalSync וברירת המחדל
provider: local. מיפוי הספקים יושב ב-ticket_sync_links.
מקור הכרטיס
קטע בשם “מקור הכרטיס”createTicket כותב manual. משיכה מספק כותבת externalSync. ילדים מואצלים
נשארים manual.
| ערך | משמעות |
|---|---|
manual |
ברירת המחדל ב-createTicket (שורות של אדם, סוכן וילדים מואצלים) |
externalSync |
נקבע בידי TicketSyncEngine במשיכה מספק |
שדות הכרטיס
קטע בשם “שדות הכרטיס”האגרגט משתרע על שני תחומים בשורה אחת. השיקוף (ספק, מפתחות, כותרת, תיאור, עדיפות, תוויות, סטטוסים, חותמות זמן) הוא מטמון לספק מרוחק ונכתב מחדש כולו ברענון. השכבת־על (הקצאה, האצלה, מרחב, הורה, פרויקט, בקשות משיכה מקושרות) קיימת רק ב-Control Center, ורענון לעולם אינו נוגע בה.
| שדה | סוג | תיאור |
|---|---|---|
id |
String |
UUID v4 |
workspaceId |
String |
סביבת העבודה שאליה הכרטיס שייך |
provider |
TicketProvider |
הבקאנד שמחזיק בנתונים הקנוניים (ברירת מחדל local) |
externalKey |
String? |
מפתח מקורי של הספק (למשל LIN-123). createTicket מקומי כותב כאן UUID נפרד, ולא את ה-id של השורה |
url |
String? |
כתובת URL באינטרנט אצל הספק המרוחק |
title |
String |
תקציר קצר; אסור שיהיה ריק |
description |
String? |
גוף Markdown ארוך יותר |
priority |
TicketPriority |
ברירת מחדל none |
labels |
List<String> |
תוויות חופשיות |
status |
TicketStatus |
סטטוס קנוני ומנורמל |
rawStatus |
String? |
שם המצב המקורי של הספק המרוחק, נשמר לתצוגה ללא אובדן |
externalSyncedAt |
DateTime? |
הסנכרון הרב-ספקי האחרון; null עד לסנכרון הראשון. מיפוי לפי ספק יושב ב-ticket_sync_links |
parentTicketId |
String? |
ההורה בעץ ההאצלה / הפירוק |
projectId |
String? |
הפרויקט שאליו הכרטיס שייך; לעולם אינו נדחף למרוחק |
assignedAgentId |
String? |
ה-id של הישות הראשית שהוקצתה (מטא-נתונים בלבד — הקצאה אינה משגרת דבר) |
assigneeType |
PrincipalType |
איזה סוג של ישות ראשית assignedAgentId מציין — user או agent (ברירת מחדל agent) |
createdByType |
PrincipalType? |
סוג הישות הראשית שיצרה את הכרטיס (user או agent); null בשורות מדור קודם או מערכת |
createdById |
String? |
ה-id של הישות הראשית שיצרה אותו |
assignedTeamId |
String? |
הצוות שהוקצה (מטא-נתונים בלבד) |
delegatedByAgentId |
String? |
הסוכן שהאציל את הכרטיס הזה |
delegationDepth |
int |
העומק בשרשרת ההאצלה; השורש הוא 0, ונבדק מול תקרת העומק המרבית |
delegationRootTicketId |
String? |
השורש של שרשרת ההאצלה; null בכרטיס שורש |
spaceId |
String? |
השיחה שהכרטיס נוצר ממנה, או המרחב של הסוכן המאציל |
errorMessage |
String? |
נקבע כאשר status הוא failed |
linkedPrIds |
List<String> |
מזהי הצמתים של בקשות המשיכה שאליהן הכרטיס מקושר |
metadata |
Map<String, dynamic> |
שק חופשי |
createdAt |
DateTime |
זמן היצירה |
startedAt |
DateTime? |
מתי העבודה התחילה |
blockedAt |
DateTime? |
מתי הכרטיס נחסם |
cancelledAt |
DateTime? |
מתי הכרטיס בוטל |
completedAt |
DateTime? |
מתי הכרטיס הושלם בהצלחה |
finishedAt |
DateTime? |
מתי הכרטיס הגיע למצב סיום כלשהו |
updatedAt |
DateTime |
השינוי האחרון (רענון שיקוף או שינוי בשכבת־על) |
version |
int |
מונה מקביליות אופטימיסטית, שמתקדם בכל שינוי |
originKind |
TicketOriginKind |
איך הכרטיס נוצר (ברירת מחדל manual) |
collaborators |
List<TicketCollaborator> |
נטען בידי המאגר לפי דרישה |
נגזרים: isTerminal, isRemote (provider != local), isAssignedToUser ו-displayKey (externalKey ?? id — כרטיסים מקומיים מציגים אפוא את ה-UUID הנפרד מזמן היצירה, ולא את ה-id של השורה).
קשרי כרטיסים
קטע בשם “קשרי כרטיסים”הקצוות השמורים הם שורות כיווניות ב-ticket_links:
| סוג | מחרוזת אחסון | משמעות |
|---|---|---|
blocks |
blocks |
המקור חוסם את היעד |
relatesTo |
relates_to |
קשר סימטרי |
duplicateOf |
duplicate_of |
המקור הוא כפיל של היעד |
אוצר המילים שבהם מדברים הממשק והכלי ticket_relation הוא TicketRelationKind, שנגזר מקצה שמור ומהצד שבו יושב הכרטיס הנדון:
| סוג | נגזר מ- |
|---|---|
blockedBy |
שורת blocks שבה הכרטיס הנדון הוא היעד |
blocking |
שורת blocks שבה הכרטיס הנדון הוא המקור |
relatedTo |
שורת relates_to (סימטרי) |
duplicateOf |
שורת duplicate_of שבה הכרטיס הנדון הוא המקור |
duplicatedBy |
שורת duplicate_of שבה הכרטיס הנדון הוא היעד |
subIssueOf |
tickets.parent_ticket_id של הכרטיס הנדון |
parentOf |
tickets.parent_ticket_id של הכרטיס האחר |
קשרי הורה ותת־סוגיה אינם שורות ב-ticket_links — הם יושבים על tickets.parent_ticket_id.
אירועי תחום
קטע בשם “אירועי תחום”שמונת המחלקות האלה יושבות ב-packages/cc_domain/lib/core/domain/events/ticketing_events.dart. אין TicketStarted, TicketDelegated, TicketCollaboratorAdded או ExternalTicketWebhookReceived.
| אירוע | נפלט כאשר |
|---|---|
TicketCreated |
כרטיס נוצר |
TicketAssigned |
כרטיס מוקצה לישות ראשית או לצוות |
TicketCompleted |
העבודה מסתיימת בהצלחה |
TicketFailed |
העבודה נכשלת |
TicketCancelled |
כרטיס מבוטל |
TicketStatusChanged |
כל שינוי סטטוס |
TicketReassigned |
כרטיס מוקצה מחדש |
TicketDetailsUpdated |
כותרת, תיאור, עדיפות או תוויות משתנים |
TicketAssigned הוא אות ביקורת והתראה וגם טריגר לפייפליין. הקצאה לסוכן אינה מפעילה שום ריצה — אין משגר כרטיסים שצופה ב-assignedAgentId. הקצאה לצוות היא החריג: TeamRoutingService משגר את מוביל הצוות. הוספת משתף פעולה כותבת שורה ואינה מפרסמת דבר. webhook מספק מיושם דרך TicketSyncEngine בלי אירוע תחום.
נקודת החנק של השינויים
קטע בשם “נקודת החנק של השינויים”TicketWorkflowService מחזיק בכל שינוי.
| מנגנון | פירוט |
|---|---|
| נקודת חנק אחת | כל השינויים עוברים דרך _mutate, שטוען את השורה וקורא ל-_assertWorkspace לפני היישום |
| בידוד סביבות עבודה | _assertWorkspace זורק WorkspaceMismatchException כאשר ה-workspaceId של השורה שנטענה אינו תואם לזה של הקורא |
| מקביליות אופטימיסטית | _mutate כותב עם expectedVersion שמוגדר ל-version שהוא קרא זה עתה, ומנסה שוב בעת ConcurrencyConflictException על ידי קריאה מחדש של מצב עדכני |
| תפיסה בלעדית | tryCheckout / releaseCheckout; סוכן שני שתופס כרטיס תפוס מקבל CheckoutConflictException |
| שומר מעברים | canTransitionTo, מיושם רק כאשר force הוא false |