דלגו לתוכן

כרטיסים והאצלה

כרטיס הוא יחידת העבודה ש-Control Center עוקב אחריה. הוא בכוונה חפץ טיפש: כותרת, גוף, סטטוס, בעלים ומקום בעץ. הוא אינו הדבר שסוכנים מבצעים מולו — עבודת סוכנים מתקיימת בשיחות, והפלט המובנה של ריצה יושב ביומן ההרצות.

הקצאה רושמת בעלות; היא אינה משגרת דבר

קטע בשם “הקצאה רושמת בעלות; היא אינה משגרת דבר”

זה הדבר היחיד והחשוב ביותר להבין נכון בכרטיסים.

הגדרת assignedAgentId כותבת שדה ומפרסמת אירוע תחום בשם TicketAssigned. האירוע הזה הוא אות ביקורת והתראה וגם טריגר לפייפליין. הקצאה לסוכן אינה מפעילה שום ריצה. אין משגר כרטיסים שצופה ב-assignedAgentId ומעיר את אותו סוכן.

החריג הוא צוות: כאשר assignedTeamId מוגדר, TeamRoutingService משגר את המוביל של הצוות עם פרוטוקול ההפעלה ורשימת הצוות.

אם אתם רוצים שסוכן מסוים יעבוד על כרטיס, אתם מבקשים זאת בשיחה, או ששלב בפייפליין משגר אותו, או שסוכן אחר מאציל לו. הקצאת כרטיס לסוכן והמתנה היא הדבר היחיד שלעולם לא יניב עבודה.

כרטיס יכול להיות מקומי בלבד או מגובה במערכת ניהול כרטיסים חיצונית, והוא נושא את שניהם באותה שורה.

השיקוף — ספק, מפתח חיצוני, URL, כותרת, תיאור, עדיפות, תוויות, סטטוס גולמי ומנורמל, חותמות זמן — הוא מטמון כשהכרטיס מרוחק. המרוחק הוא מקור האמת שם, ורענון כותב מחדש את השיקוף כולו.

השכבת־על — הקצאה, האצלה, קישור למרחב, הורה, פרויקט, בקשות משיכה מקושרות — קיימת רק ב-Control Center. מערכת ניהול הכרטיסים לא יודעת עליה דבר, ורענון לעולם אינו נוגע בה.

הפיצול הזה הוא מה שהופך את הסנכרון לבטוח מלכתחילה: משיכה מהספק יכולה להיות הרסנית לשיקוף בלי לאבד לעולם את מטא־נתוני התזמור שנשכבו מעליו.

גרף מחזור החיים מחייב סוכנים, לא אתכם

קטע בשם “גרף מחזור החיים מחייב סוכנים, לא אתכם”

הסטטוסים נעים backlog → open → inProgress → inReview → done, כאשר blocked הוא לולאת צד ו-failed / cancelled הם מצבי הסיום האחרים.

גרף המעברים נאכף רק במסלול של הסוכנים והאוטומציה — כלי MCP ומנגנוני יישור. מעבר לא חוקי שם נרשם ביומן ומתעלמים ממנו, כך שסוכן מבולבל לא יכול להזיז כרטיס לאחור החוצה ממצב סיום.

כל שינוי שנעשה בידי משתמש בממשק עובר עם force: true ועוקף את הגרף לחלוטין. אדם רשאי להעביר כרטיס לכל סטטוס שהבורר מציג — כולל פתיחה מחדש של כרטיס במצב סיום. הבורר אינו מציע failed; סוכנים קובעים אותו דרך fail_ticket. הגרף הוא מעקה בטיחות לאוטומציה, ולא כלל שקובע איך מותר לעבודה להתנהל.

TicketWorkflowService מחזיק בכל שינוי דרך נקודת חנק אחת שטוענת את השורה, מוודאת שהיא שייכת לסביבת העבודה של הקורא וכותבת עם version צפוי, כך ששתי עריכות במקביל לא יכולות לדרוס זו את זו בשקט.

לטבלת הסטטוסים המלאה, המעברים, רשימת השדות וסוגי הקשרים, ראו מחזור חיי כרטיס.

מקוריות: איך כרטיס נוצר

קטע בשם “מקוריות: איך כרטיס נוצר”

לכל כרטיס יש originKind. לטיפוס המנוי יש חמישה ערכים — manual, pipelineStep, agentDelegation, externalSync, recovery — אבל רק שניים נכתבים בפועל היום: manual (ברירת המחדל ב-createTicket, כולל ילדים מואצלים) ו-externalSync (מנוע הסנכרון, במשיכה מהספק). TicketWorkflowService.createTicket אינו מקבל ארגומנט מקור, ולכן pipelineStep, agentDelegation ו-recovery לעולם אינם נוחתים על שורה.

האצלה היא המקום שבו סוכן יוצר עבודה לסוכן אחר, והיא הנקודה שבה הרשאה עלולה לדלוף, ולכן ההגנות יושבות בצד השרת בנקודת חנק ולא בהוראות שבתוך ההנחיה.

delegate_task עובר דרך TicketWorkflowService.delegateGuarded, שמחשב את העומק והשורש של הילד משרשרת ההורים ואז מריץ את כל ארבע הבדיקות של DelegationGuards: קפיצה שתחרוג מתקרת עומק של 3, קפיצה חזרה אל סוכן שכבר נמצא בשרשרת (זיהוי מעגלים), תקרת אוטונומיה (הרמה האפקטיבית של המואצל במרחב לא תעלה על זו של המאציל) ומעטפת תקציב (ההאצלה מחייבת את תקציב העצירה הקשה שנותר למאציל ואינה יכולה להנפיק עוד). החיווט בייצור מספק את שתי יציאות הפותר. אוטונומיה ותקציב מדולגים כאשר from_agent_id מושמט (תקציב בלתי מוגבל / בלי מדיניות גם עובר). סירוב נזרק כ-DelegationRefusedException שההודעה שלו מגיעה לסוכן המאציל מילה במילה — דחייה קולנית, ולעולם לא אי־פעולה שקטה.

delegate_ticket הוא האח המבוגר, חסר ההגנות: הוא קורא ל-createTicket ישירות ומדלג על כל הגנה. עדיפו delegate_task.

אל תבלבלו בין תקרת העומק של ההאצלה (3, בשרשראות delegate_task) לבין תקרת הקינון של תתי־סוכנים במנגנון ההרצה המובנה (2). אלה מגבלות שונות על מנגנונים שונים.

היררכיה, קשרים ופרויקטים

קטע בשם “היררכיה, קשרים ופרויקטים”

יחסי הורה ותת־סוגיה יושבים על tickets.parent_ticket_id, בבעלות setParent / clearParent. הם אינם רשומות ב-ticket_links, ולכן כל דבר שקורא את הקשרים של כרטיס חייב למזג את שני המקורות.

ticket_links מחזיק את הקצוות הכיווניים בין כרטיסים שאינם קשורים אחרת:

סוג קשר משמעות
blocks המקור חוסם את היעד
relatesTo שני הכרטיסים קשורים זה לזה (סימטרי)
duplicateOf המקור הוא כפיל של היעד

פרויקט הוא קיבוץ של כרטיסים בהיקף סביבת עבודה לקראת מטרה משותפת. פרויקטים קיימים רק ב-Control Center ולעולם אינם מסונכרנים לספק חיצוני.

רשומות TicketCollaborator הן הדרך שבה יותר משחקן אחד משויך לכרטיס, בתפקיד assignee, collaborator או reviewer. בני אדם וסוכנים הם רשומות שוות־מעמד — principalId בתוספת collaboratorType — בלי ערך סימן מיוחד ל“המשתמש” ובלי מפתח זר לאף אחד משני הטבלאות.

הכרטיס של Control Center הוא הראשי. מחיקת כרטיס היא מקומית בלבד, ולכן כרטיס שמגובה בספק ואתם מוחקים לא נמחק במערכת ניהול הכרטיסים ועלול להופיע שוב בסנכרון הבא. משתפי פעולה וכרטיסי ילד נמחקים בשרשור.

פרטי הגישה של המתאם נפתרים ממאגרי פרטי הגישה של השרת (מפתח Linear או אסימון GitHub שהודבקו בהגדרות נקלטים בבקשה הבאה; הסביבה רק זורעת את המאגרים האלה).

אף צומת בפייפליין לא יוצר, משלים או מבטל כרטיסים — לפלטה אין שלב כזה והמנוע אינו קורא ל-TicketWorkflowService. טריגר של פייפליין עדיין יכול לפעול על TicketAssigned, TicketCompleted, TicketFailed או TicketCancelled (אלה ארבעת אירועי הכרטיס ש-EventPayloadMapper ממפה בפועל). אין עמודת pipelineRunId בכרטיס; הצימוד לפייפליין יושב ביומן ההרצות של הסוכן.