Ir al contenido

Tickets y delegación

Un ticket es la unidad de trabajo que registra Control Center. A propósito es un artefacto tonto: un título, un cuerpo, un estado, un responsable y un lugar en un árbol. No es aquello contra lo que ejecutan los agentes: el trabajo de los agentes vive en las conversaciones y la salida estructurada de una ejecución vive en el registro de ejecución.

La asignación deja constancia de la titularidad; no despacha nada

Sección titulada «La asignación deja constancia de la titularidad; no despacha nada»

Esto es lo más importante que hay que entender bien sobre los tickets.

Establecer assignedAgentId escribe un campo y publica un TicketAssigned evento de dominio. Ese evento es una señal de auditoría y notificación y un disparador de pipeline. Asignar un ticket a un agente no inicia ninguna ejecución. No hay ningún despachador de tickets que vigile assignedAgentId y despierte a ese agente.

La excepción es un equipo: cuando se establece assignedTeamId, TeamRoutingService despacha al líder del equipo con el protocolo operativo y la lista de miembros.

Si quieres que un agente concreto trabaje en un ticket, se lo pides en una conversación, o un paso de pipeline lo despacha, o bien otro agente le delega el trabajo. Asignar un ticket a un agente y esperar es lo único que nunca producirá trabajo.

Un ticket puede ser puramente local o estar respaldado por un rastreador externo y lleva ambas cosas en una sola fila.

El espejo —proveedor, clave externa, URL, título, descripción, prioridad, etiquetas, estado sin procesar y normalizado, marcas de tiempo— es una caché cuando el ticket es remoto. El sistema remoto es allí la fuente de la verdad y una actualización reescribe el espejo por completo.

La superposición —asignación, delegación, enlace de espacio, padre, proyecto, PR vinculadas— es exclusiva de Control Center. El rastreador no sabe nada de ella y una actualización nunca la toca.

Esa separación es lo que hace que sincronizar sea seguro: una descarga del proveedor puede ser destructiva para el espejo sin llegar a perder nunca los metadatos de orquestación que se superponen encima.

El grafo del ciclo de vida vincula a los agentes, no a ti

Sección titulada «El grafo del ciclo de vida vincula a los agentes, no a ti»

Los estados van de backlog → open → inProgress → inReview → done, con blocked como bucle lateral y failed / cancelled como los otros estados terminales.

El grafo de transiciones se aplica solo en la ruta de los agentes y la automatización: las herramientas MCP y los reconciliadores. Una transición ilegal allí se registra y se ignora, de modo que un agente confundido no puede sacar un ticket hacia atrás de un estado terminal.

Todos los cambios que hace un usuario desde la interfaz pasan force: true y omiten el grafo por completo. Una persona puede mover un ticket a cualquier estado que liste el selector, incluido reabrir uno terminal. El selector no ofrece failed; los agentes lo establecen con fail_ticket. El grafo es una barandilla para la automatización, no una regla sobre cómo puede avanzar el trabajo.

TicketWorkflowService es dueño de todas las mutaciones a través de un único punto de paso que carga la fila, verifica que pertenezca al espacio de trabajo de quien llama y escribe con una version esperada, de modo que dos ediciones simultáneas no puedan sobrescribirse en silencio.

Para ver la tabla completa de estados, las transiciones, la lista de campos y los tipos de relación, consulta Ciclo de vida de tickets.

Procedencia: cómo llegó a existir un ticket

Sección titulada «Procedencia: cómo llegó a existir un ticket»

Todos los tickets tienen un originKind. La enumeración tiene cinco valores —manual, pipelineStep, agentDelegation, externalSync, recovery—, pero hoy solo se escriben dos: manual (el valor predeterminado en createTicket, incluidos los hijos delegados) y externalSync (el motor de sincronización, en una descarga del proveedor). TicketWorkflowService.createTicket no acepta un argumento de origen, así que pipelineStep, agentDelegation y recovery nunca se escriben en una fila.

La delegación es donde un agente crea trabajo para otro agente y es el punto por el que podrían filtrarse privilegios, así que las protecciones viven en el lado del servidor, en un punto de paso, y no en las instrucciones del prompt.

delegate_task pasa por TicketWorkflowService.delegateGuarded, que calcula la profundidad y la raíz del hijo a partir de la cadena de padres y luego ejecuta las cuatro comprobaciones de DelegationGuards: un salto que superaría un límite de profundidad de 3, un salto de vuelta a un agente que ya está en la cadena (detección de ciclos), un techo de autonomía (el nivel efectivo del delegado en el espacio no debe superar el del delegador) y una envolvente de presupuesto (la delegación imputa el presupuesto de parada estricta restante del delegador y no puede acuñar más). El cableado de producción aporta ambos puertos de resolución. La autonomía y el presupuesto se omiten cuando no se indica from_agent_id (un presupuesto ilimitado o sin política también pasa). Un rechazo se lanza como una DelegationRefusedException cuyo mensaje llega literalmente al agente que delega: una denegación ruidosa, nunca una operación nula silenciosa.

delegate_ticket es el hermano antiguo, sin protecciones: llama directamente a createTicket y se salta todas las comprobaciones. Es preferible delegate_task.

No confundas el límite de profundidad de la delegación (3, en las cadenas de delegate_task) con el límite de anidamiento de subagentes del arnés integrado (2). Son límites distintos de mecanismos distintos.

Las relaciones de padre y subincidencia viven en tickets.parent_ticket_id y las gestionan setParent / clearParent. No son filas de ticket_links, así que cualquier cosa que lea las relaciones de un ticket tiene que combinar ambas fuentes.

ticket_links contiene las aristas direccionales entre tickets que, por lo demás, no tienen relación:

Tipo de enlace Significado
blocks El origen bloquea al destino
relatesTo Los dos tickets están relacionados (simétrico)
duplicateOf El origen es un duplicado del destino

Un proyecto es una agrupación de tickets con alcance de espacio de trabajo en torno a un objetivo común. Los proyectos son exclusivos de Control Center y nunca se sincronizan con un proveedor externo.

Las filas de TicketCollaborator son la forma de vincular más de un principal a un ticket, con un rol de assignee, collaborator o reviewer. Las personas y los agentes son filas en pie de igualdad —un principalId más un collaboratorType—, sin ningún valor centinela para «el usuario» y sin clave externa a ninguna de las dos tablas.

El ticket de Control Center es el principal. Eliminar un ticket es solo local, así que un ticket respaldado por un proveedor que elimines no se elimina en el rastreador y puede reaparecer en la siguiente sincronización. Los colaboradores y los tickets hijos se eliminan en cascada.

Las credenciales del adaptador se resuelven desde los almacenes de credenciales del servidor (una clave de Linear o un token de GitHub pegados en Ajustes se recogen en la siguiente solicitud; el entorno solo siembra esos almacenes).

Ningún nodo de pipeline crea, completa ni cancela tickets: la paleta no tiene ese paso y el motor no llama a TicketWorkflowService. Aun así, un disparador de pipeline puede activarse con TicketAssigned, TicketCompleted, TicketFailed o TicketCancelled (esos cuatro son los eventos de ticket que EventPayloadMapper realmente asigna). No hay ninguna columna pipelineRunId en el ticket; el acoplamiento con pipelines vive en el registro de ejecución del agente.