Тикеты и делегирование
Тикет — это единица работы, которую отслеживает 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 у тикета нет; связь с
конвейером живёт в журнале запуска агента.
См. также
Заголовок раздела «См. также»- Конвейеры и автоматизация: события тикета могут запускать конвейер
- Оркестрация: откуда берётся DAG подтикетов
- Модель агента: кому назначается тикет
- Доменные события: события жизненного цикла тикета
- Жизненный цикл тикета: статусы, поля, переходы, связи
- Создавать и управлять тикетами
- Делегировать работу агентам
- Организовать работу через проекты