Salta ai contenuti

Ciclo di vita dei ticket

Stato Stringa memorizzata Descrizione
backlog backlog Acquisito, non ancora sottoposto a triage nella coda attiva
open open Sottoposto a triage e pronto per essere lavorato, non avviato
inProgress inProgress In lavorazione attiva
blocked blocked Avviato ma bloccato da qualcosa di esterno
inReview inReview Lavoro completato, in attesa di revisione
done done Terminato con successo (terminale)
failed failed Terminato con errore (terminale)
cancelled cancelled Abbandonato (terminale)

I valori di stato sono memorizzati in camelCase (inProgress, inReview), mai in_progress. Un valore memorizzato null viene interpretato come open; un valore sconosciuto genera un errore invece di essere convertito.

tryParseLoose accetta gli alias forniti dagli agenti, senza distinguere maiuscole e minuscole e ignorando - / _ / spazio:

Stato Alias accettati
open todo, ready, pending, new
inProgress doing, started, active, wip
blocked stuck
inReview review, reviewing
done closed, complete, completed, finished
failed error, errored
cancelled canceled, abandoned, dropped
backlog ──→ open ──→ inProgress ──→ inReview ──→ done
│ │ │
│ └──→ failed ┘
└──→ blocked ──→ inProgress
Any non-terminal status ──→ cancelled; inReview ──→ failed

Questo grafo vincola solo gli agenti e l’automazione. TicketWorkflowService.transitionStatus lo verifica quando force è false — gli strumenti MCP e i riconciliatori. Una transizione non valida in quel contesto viene registrata e ignorata. Ogni modifica di stato guidata dall’utente nell’interfaccia passa force: true e aggira del tutto il grafo, quindi una persona può spostare un ticket in qualsiasi stato elenchi il selettore (tutti gli stati tranne failed), compreso riaprirne uno terminale. Gli agenti impostano failed con fail_ticket.

Uno stato può sempre transitare verso se stesso. Gli stati terminali non hanno archi in uscita.

Da Destinazioni consentite
backlog open, cancelled
open inProgress, blocked, cancelled
inProgress blocked, inReview, done, failed, cancelled
blocked inProgress, cancelled
inReview inProgress, done, failed, cancelled
done — (terminale)
failed — (terminale)
cancelled — (terminale)

Memorizzate come numero intero sulla scala nativa 0–4 di Linear. Un valore sconosciuto viene interpretato come none.

Priorità Memorizzata come
none 0
urgent 1
high 2
medium 3
low 4

Un valore memorizzato sconosciuto o null viene interpretato come local.

Provider Stato
local Memorizzato interamente nel database dello spazio di lavoro; è il valore predefinito
linear Di proprietà remota, con mirror locale

Gli adattatori di sincronizzazione si trovano in packages/cc_infra/lib/src/tickets/sync/ per Linear, GitHub Issues, Jira Cloud e ClickUp. GitHub Issues è solo un fornitore di adattatori di sincronizzazione (vendorId: github) — non è un valore di TicketProvider, quindi un issue GitHub recuperato viene memorizzato con originKind: externalSync e provider: local predefinito. La mappatura dei fornitori vive su ticket_sync_links.

createTicket scrive manual. Una pull dal fornitore scrive externalSync. I figli delegati restano manual.

Valore Significato
manual Predefinito in createTicket (righe umane, degli agenti e delegate)
externalSync Impostato da TicketSyncEngine durante una pull dal fornitore

L’aggregato copre due aspetti su un’unica riga. Il mirror (provider, chiavi, titolo, descrizione, priorità, etichette, stati, timestamp) è una cache per un provider remoto e viene riscritto in blocco da un aggiornamento. L’overlay (assegnazione, delega, spazio, padre, progetto, PR collegate) è esclusivo di Control Center e un aggiornamento non lo tocca mai.

Campo Tipo Descrizione
id String UUID v4
workspaceId String Spazio di lavoro proprietario
provider TicketProvider Backend che possiede i dati canonici (predefinito local)
externalKey String? Chiave nativa del provider (es. LIN-123). La createTicket locale scrive qui un UUID separato, non l’id della riga
url String? URL web sul provider remoto
title String Riepilogo breve; non deve essere vuoto
description String? Corpo Markdown più lungo
priority TicketPriority Predefinita none
labels List<String> Etichette libere
status TicketStatus Stato normalizzato canonico
rawStatus String? Il nome nativo dello stato presso il provider remoto, conservato per una visualizzazione senza perdite
externalSyncedAt DateTime? Ultima sincronizzazione multi-fornitore; null fino alla prima. La mappatura per fornitore vive in ticket_sync_links
parentTicketId String? Padre nell’albero di delega / scomposizione
projectId String? Progetto proprietario; non viene mai inviato a un remoto
assignedAgentId String? Id del principale assegnato (solo metadati — l’assegnazione non manda nulla in esecuzione)
assigneeType PrincipalType Che tipo di principale indica assignedAgentId — user o agent (predefinito agent)
createdByType PrincipalType? Tipo di principale che ha creato il ticket (user o agent); null per le righe legacy/di sistema
createdById String? Id del principale che l’ha creato
assignedTeamId String? Team assegnato (solo metadati)
delegatedByAgentId String? Agente che ha delegato questo ticket
delegationDepth int Profondità nella catena di delega; la radice è 0, verificata rispetto al tetto di profondità massima
delegationRootTicketId String? Radice della catena di delega; null per un ticket radice
spaceId String? Conversazione da cui è nato il ticket, oppure lo spazio dell’agente che delega
errorMessage String? Impostato quando status è failed
linkedPrIds List<String> Id dei nodi PR a cui questo ticket è collegato
metadata Map<String, dynamic> Contenitore libero
createdAt DateTime Ora di creazione
startedAt DateTime? Quando è iniziato il lavoro
blockedAt DateTime? Quando il ticket è stato bloccato
cancelledAt DateTime? Quando il ticket è stato annullato
completedAt DateTime? Quando il ticket è stato completato con successo
finishedAt DateTime? Quando il ticket ha raggiunto un qualsiasi stato terminale
updatedAt DateTime Ultima mutazione (aggiornamento del mirror o modifica dell’overlay)
version int Contatore di concorrenza ottimistica, incrementato a ogni mutazione
originKind TicketOriginKind Come è stato creato il ticket (predefinito manual)
collaborators List<TicketCollaborator> Idratati dal repository su richiesta

Derivati: isTerminal, isRemote (provider != local), isAssignedToUser e displayKey (externalKey ?? id — i ticket locali mostrano quindi l’UUID separato creato al momento della creazione, non l’id della riga).

Gli archi memorizzati sono righe direzionali in ticket_links:

Tipo Stringa memorizzata Significato
blocks blocks La sorgente blocca la destinazione
relatesTo relates_to Relazione simmetrica
duplicateOf duplicate_of La sorgente è un duplicato della destinazione

Il vocabolario usato dall’interfaccia e da ticket_relation è TicketRelationKind, derivato da un arco memorizzato più l’estremo su cui si trova il soggetto:

Categoria Derivato da
blockedBy Una riga blocks in cui il soggetto è la destinazione
blocking Una riga blocks in cui il soggetto è la sorgente
relatedTo Una riga relates_to (simmetrica)
duplicateOf Una riga duplicate_of in cui il soggetto è la sorgente
duplicatedBy Una riga duplicate_of in cui il soggetto è la destinazione
subIssueOf tickets.parent_ticket_id sul soggetto
parentOf tickets.parent_ticket_id sull’altro ticket

I link genitore e sotto-ticket non sono righe di ticket_links — vivono su tickets.parent_ticket_id.

Queste otto classi vivono in packages/cc_domain/lib/core/domain/events/ticketing_events.dart. Non esistono TicketStarted, TicketDelegated, TicketCollaboratorAdded né ExternalTicketWebhookReceived.

Evento Emesso quando
TicketCreated Il ticket viene creato
TicketAssigned Il ticket viene assegnato a un principale o a un team
TicketCompleted Il lavoro termina con successo
TicketFailed Il lavoro fallisce
TicketCancelled Il ticket viene annullato
TicketStatusChanged Qualsiasi modifica di stato
TicketReassigned Il ticket viene riassegnato
TicketDetailsUpdated Cambiano titolo, descrizione, priorità o etichette

TicketAssigned è 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. L’assegnazione a un team è l’eccezione: TeamRoutingService manda in esecuzione il leader del team. Aggiungere un collaboratore scrive una riga e non pubblica nulla. Un webhook del fornitore viene applicato tramite TicketSyncEngine senza un evento di dominio.

TicketWorkflowService possiede ogni mutazione.

Meccanismo Dettaglio
Punto di controllo unico Tutte le mutazioni passano da _mutate, che carica la riga e chiama _assertWorkspace prima di applicare
Isolamento dello spazio di lavoro _assertWorkspace lancia WorkspaceMismatchException quando il workspaceId della riga caricata non corrisponde a quello del chiamante
Concorrenza ottimistica _mutate scrive con expectedVersion impostato alla version appena letta e riprova su ConcurrencyConflictException rileggendo lo stato aggiornato
Prenotazione esclusiva tryCheckout / releaseCheckout; un secondo agente che rivendica un ticket già prenotato riceve una CheckoutConflictException
Guardia di transizione canTransitionTo, applicata solo quando force è false