Salta ai contenuti

Ticket e delega

Un ticket è l’unità di lavoro che Control Center traccia. È volutamente un artefatto stupido: un titolo, un corpo, uno stato, un proprietario e una posizione in un albero. Non è la cosa su cui gli agenti eseguono — il lavoro degli agenti vive nelle conversazioni e l’output strutturato di un’esecuzione vive nel log di esecuzione.

L’assegnazione registra la proprietà; non manda nulla in esecuzione

Sezione intitolata “L’assegnazione registra la proprietà; non manda nulla in esecuzione”

Questa è la cosa più importante da capire bene sui ticket.

Impostare assignedAgentId scrive un campo e pubblica un TicketAssigned evento di dominio. Quell’evento è un segnale di audit e notifica e un trigger di pipeline. L’assegnazione a un agente non avvia alcuna esecuzione. Non esiste alcun dispatcher dei ticket che osservi assignedAgentId e risvegli quell’agente.

L’eccezione è un team: quando è impostato assignedTeamId, TeamRoutingService manda in esecuzione il leader del team con il protocollo operativo e il roster.

Se vuoi che un agente specifico lavori su un ticket, glielo chiedi in una conversazione, oppure un passaggio di pipeline lo manda in esecuzione, oppure un altro agente gli delega il lavoro. Assegnare un ticket a un agente e aspettare è l’unica cosa che non produrrà mai lavoro.

Un ticket può essere puramente locale o supportato da un tracker esterno, e porta entrambe le cose su un’unica riga.

Il mirror — provider, chiave esterna, URL, titolo, descrizione, priorità, etichette, stato grezzo e normalizzato, timestamp — è una cache quando il ticket è remoto. Lì il remoto è la fonte di verità e un aggiornamento riscrive il mirror in blocco.

L’overlay — assegnazione, delega, collegamento allo spazio, ticket padre, progetto, PR collegate — è esclusivo di Control Center. Il tracker non ne sa nulla e un aggiornamento non lo tocca mai.

È questa separazione a rendere sicura la sincronizzazione: una pull dal fornitore può essere distruttiva per il mirror senza mai perdere i metadati di orchestrazione sovrapposti.

Il grafo del ciclo di vita vincola gli agenti, non te

Sezione intitolata “Il grafo del ciclo di vita vincola gli agenti, non te”

Gli stati sono backlog → open → inProgress → inReview → done, con blocked come anello laterale e failed / cancelled come gli altri stati terminali.

Il grafo delle transizioni è applicato solo sul percorso degli agenti e dell’automazione — strumenti MCP e riconciliatori. Una transizione non valida viene registrata e ignorata, così un agente confuso non può far regredire un ticket fuori da uno stato terminale.

Ogni modifica guidata dall’utente nell’interfaccia passa force: true e aggira del tutto il grafo. Una persona può spostare un ticket in qualsiasi stato elenchi il selettore — compreso riaprirne uno terminale. Il selettore non offre failed; gli agenti lo impostano tramite fail_ticket. Il grafo è una barriera di sicurezza per l’automazione, non una regola su come il lavoro può procedere.

TicketWorkflowService possiede ogni mutazione attraverso un unico punto di controllo che carica la riga, verifica che appartenga allo spazio di lavoro del chiamante e scrive con una version attesa, così due modifiche concorrenti non possono sovrascriversi in silenzio.

Per la tabella completa degli stati, le transizioni, l’elenco dei campi e i tipi di relazione, vedi Ciclo di vita dei ticket.

Ogni ticket ha un originKind. L’enum ha cinque valori — manual, pipelineStep, agentDelegation, externalSync, recovery — ma oggi ne vengono scritti solo due: manual (il valore predefinito in createTicket, compresi i figli delegati) e externalSync (il motore di sincronizzazione, durante una pull dal fornitore). TicketWorkflowService.createTicket non accetta un argomento di origine, quindi pipelineStep, agentDelegation e recovery non arrivano mai su una riga.

La delega è il punto in cui un agente crea lavoro per un altro agente ed è il luogo in cui un privilegio potrebbe trapelare, perciò le protezioni vivono lato server in un punto di controllo, non nelle istruzioni del prompt.

delegate_task passa attraverso TicketWorkflowService.delegateGuarded, che calcola profondità e radice del figlio a partire dalla catena dei ticket padre e poi esegue tutte e quattro le verifiche di DelegationGuards: un salto che supererebbe un tetto di profondità di 3, un salto all’indietro verso un agente già presente nella catena (rilevamento dei cicli), un tetto di autonomia (il livello effettivo dell’agente delegato nello spazio non deve superare quello del delegante) e un perimetro di budget (la delega addebita il budget di arresto forzato residuo del delegante e non può crearne altro). Il cablaggio di produzione fornisce entrambe le porte resolver. Autonomia e budget vengono saltati quando from_agent_id è omesso (passa anche un budget illimitato / senza policy). Un rifiuto viene lanciato come DelegationRefusedException, il cui messaggio raggiunge l’agente delegante parola per parola — un rifiuto a voce alta, mai un no-op silenzioso.

delegate_ticket è il gemello più vecchio e senza guardie: chiama createTicket direttamente e salta ogni verifica. Preferisci delegate_task.

Non confondere il tetto di profondità della delega (3, sulle catene di delegate_task) con il tetto di annidamento dei subagenti dell’harness integrato (2). Sono limiti diversi su meccanismi diversi.

Le relazioni genitore e sotto-ticket vivono su tickets.parent_ticket_id, di proprietà di setParent / clearParent. Non sono righe di ticket_links, quindi chiunque legga le relazioni di un ticket deve unire entrambe le fonti.

ticket_links contiene gli archi direzionali tra ticket altrimenti non correlati:

Tipo di link Significato
blocks La sorgente blocca la destinazione
relatesTo I due ticket sono correlati (simmetrica)
duplicateOf La sorgente è un duplicato della destinazione

Un progetto è un raggruppamento di ticket con ambito di spazio di lavoro verso un obiettivo condiviso. I progetti sono esclusivi di Control Center e non si sincronizzano mai con un provider esterno.

Le righe di TicketCollaborator sono il modo in cui più di un principale viene collegato a un ticket, con un ruolo tra assignee, collaborator e reviewer. Persone e agenti sono righe paritarie — un principalId più un collaboratorType — senza alcun valore sentinella per «l’utente» e senza chiave esterna verso nessuna delle due tabelle.

Il ticket di Control Center è primario. L’eliminazione di un ticket è solo locale, quindi un ticket supportato da un fornitore che elimini non viene eliminato sul tracker e può ricomparire alla sincronizzazione successiva. I collaboratori e i ticket figlio seguono a cascata.

Le credenziali dell’adattatore vengono risolte dagli archivi delle credenziali del server (una chiave Linear o un token GitHub incollato in Settings viene raccolto alla richiesta successiva; l’ambiente si limita a popolare inizialmente quegli archivi).

Nessun nodo di pipeline crea, completa o annulla ticket — la palette non ha un passaggio del genere e il motore non chiama TicketWorkflowService. Un trigger di pipeline può comunque attivarsi su TicketAssigned, TicketCompleted, TicketFailed o TicketCancelled (quelli sono i quattro eventi dei ticket che EventPayloadMapper mappa davvero). Non esiste una colonna pipelineRunId sul ticket; l’accoppiamento con le pipeline vive nel log di esecuzione dell’agente.