Перейти до вмісту

Тікети та делегування

Тікет — це одиниця роботи, яку відстежує Control Center. Він навмисно примітивний артефакт: заголовок, тіло, стан, власник і місце в дереві. Це не те, з чим працюють агенти — робота агентів живе в розмовах, а структурований вивід запуску живе в журналі запусків.

Призначення фіксує власність; воно нічого не запускає

Розділ із назвою «Призначення фіксує власність; воно нічого не запускає»

Це найважливіше, що треба правильно зрозуміти про тікети.

Встановлення assignedAgentId записує поле й публікує доменну подію TicketAssigned. Ця подія — сигнал аудиту й сповіщення та тригер конвеєра. Призначення агенту не запускає жодного запуску. Немає диспетчера тікетів, який стежив би за assignedAgentId і будив би цього агента.

Виняток — команда: коли встановлено assignedTeamId, TeamRoutingService запускає лідера команди з робочим протоколом і реєстром.

Якщо ви хочете, щоб над тікетом працював конкретний агент, попросіть його в розмові, або його запускає крок конвеєра, або йому делегує інший агент. Призначити тікет агенту й чекати — це єдине, що ніколи не дасть роботи.

Тікет може бути цілком локальним або підкріпленим зовнішнім трекером, і він несе обидва аспекти в одному рядку.

Дзеркало — постачальник, зовнішній ключ, URL, заголовок, опис, пріоритет, мітки, сирий і нормалізований стан, часові мітки — це кеш, коли тікет віддалений. Там джерело істини — віддалена система, і оновлення повністю перезаписує дзеркало.

Оверлей — призначення, делегування, зв’язок із простором, батьківський тікет, проєкт, пов’язані PR — існує лише в 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; зв’язок із конвеєром живе в журналі запусків агента.