Životní cyklus ticketu
Stavy ticketu
Sekce “Stavy ticketu”| Stav | Uložený řetězec | Popis |
|---|---|---|
backlog |
backlog |
Zachycený, ještě nezařazený do aktivní fronty |
open |
open |
Zařazený a připravený k práci, ještě nezahájený |
inProgress |
inProgress |
Aktivně se na něm pracuje |
blocked |
blocked |
Zahájený, ale zablokovaný něčím vnějším |
inReview |
inReview |
Práce je hotová a čeká na kontrolu |
done |
done |
Úspěšně dokončený (koncový) |
failed |
failed |
Dokončený s chybou (koncový) |
cancelled |
cancelled |
Opuštěný (koncový) |
Hodnoty stavů se ukládají v camelCase (inProgress, inReview), nikdy jako in_progress. Null uložená hodnota se parsuje jako open; neznámá hodnota vyhodí výjimku a na jiný stav se nepřinutí.
tryParseLoose přijímá aliasy dodané agentem a ignoruje velikost písmen i - / _ / mezeru:
| Stav | Přijímané aliasy |
|---|---|
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 |
Přechody stavů
Sekce “Přechody stavů”backlog ──→ open ──→ inProgress ──→ inReview ──→ done │ │ │ │ └──→ failed ┘ └──→ blocked ──→ inProgress
Any non-terminal status ──→ cancelled; inReview ──→ failedTenhle graf váže jen agenty a automatizaci. TicketWorkflowService.transitionStatus ho kontroluje, když je force false: nástroje MCP a rekoncileři. Nepovolený přechod se tam zapíše do logu a ignoruje. Každá změna stavu, kterou člověk udělá v rozhraní, posílá force: true a graf úplně obejde, takže člověk smí ticket přesunout na jakýkoli stav, který výběr nabízí (všechny stavy kromě failed), včetně znovuotevření koncového. Agenti označí failed přes fail_ticket.
Stav smí vždy přejít sám na sebe. Koncové stavy nemají odchozí hrany.
| Z | Povolené cíle |
|---|---|
backlog |
open, cancelled |
open |
inProgress, blocked, cancelled |
inProgress |
blocked, inReview, done, failed, cancelled |
blocked |
inProgress, cancelled |
inReview |
inProgress, done, failed, cancelled |
done |
— (koncový) |
failed |
— (koncový) |
cancelled |
— (koncový) |
Priority ticketu
Sekce “Priority ticketu”Ukládají se jako celé číslo na nativní stupnici Linear 0–4. Neznámá hodnota se parsuje na none.
| Priorita | Uloženo jako |
|---|---|
none |
0 |
urgent |
1 |
high |
2 |
medium |
3 |
low |
4 |
Poskytovatelé ticketu
Sekce “Poskytovatelé ticketu”Neznámá nebo null hodnota se parsuje na local.
| Poskytovatel | Stav |
|---|---|
local |
Celý uložený v databázi pracovního prostoru; výchozí |
linear |
Vlastní ho vzdálená strana, místně se zrcadlí |
Synchronizační adaptéry žijí pod packages/cc_infra/lib/src/tickets/sync/ pro Linear,
GitHub Issues, Jira Cloud a ClickUp. GitHub Issues je jen dodavatel synchronizačního
adaptéru (vendorId: github), ne hodnota TicketProvider, takže stažený issue
z GitHub se uloží s originKind: externalSync a výchozím
provider: local. Mapování dodavatele žije na ticket_sync_links.
Původ ticketu
Sekce “Původ ticketu”createTicket zapíše manual. Stažení od dodavatele zapíše externalSync. Delegovaní
potomci zůstanou manual.
| Hodnota | Význam |
|---|---|
manual |
Výchozí u createTicket (řádky člověka, agenta i delegované) |
externalSync |
Nastaví TicketSyncEngine při stažení od dodavatele |
Pole ticketu
Sekce “Pole ticketu”Agregát nese na jednom řádku dvě věci. Zrcadlo (poskytovatel, klíče, název, popis, priorita, štítky, stavy, časová razítka) je cache vzdáleného poskytovatele a obnovení ho přepíše celé. Překryv (přiřazení, delegování, prostor, rodič, projekt, propojené PR) patří jen Control Center a obnovení se ho nedotkne.
| Pole | Typ | Popis |
|---|---|---|
id |
String |
UUID v4 |
workspaceId |
String |
Vlastnící pracovní prostor |
provider |
TicketProvider |
Backend, který vlastní kanonická data (výchozí local) |
externalKey |
String? |
Nativní klíč poskytovatele (např. LIN-123). Místní createTicket sem zapíše samostatné UUID, ne řádkové id |
url |
String? |
Webová URL u vzdáleného poskytovatele |
title |
String |
Krátké shrnutí; nesmí být prázdné |
description |
String? |
Delší tělo v Markdownu |
priority |
TicketPriority |
Výchozí none |
labels |
List<String> |
Volné štítky |
status |
TicketStatus |
Kanonický normalizovaný stav |
rawStatus |
String? |
Nativní název stavu u vzdáleného poskytovatele, ponechaný kvůli bezztrátovému zobrazení |
externalSyncedAt |
DateTime? |
Poslední synchronizace napříč dodavateli; null do první. Mapování po dodavatelích žije v ticket_sync_links |
parentTicketId |
String? |
Rodič ve stromu delegování a rozpadu |
projectId |
String? |
Vlastnící projekt; na vzdálenou stranu se nikdy neposílá |
assignedAgentId |
String? |
Id přiřazeného principála (jen metadata: přiřazení nic neodešle) |
assigneeType |
PrincipalType |
Jaký druh principála assignedAgentId označuje, user nebo agent (výchozí agent) |
createdByType |
PrincipalType? |
Druh principála, který ticket vytvořil (user nebo agent); null u starších a systémových řádků |
createdById |
String? |
Id principála, který ticket vytvořil |
assignedTeamId |
String? |
Přiřazený tým (jen metadata) |
delegatedByAgentId |
String? |
Agent, který tenhle ticket delegoval |
delegationDepth |
int |
Hloubka v řetězci delegování; kořen je 0, kontroluje se proti stropu hloubky |
delegationRootTicketId |
String? |
Kořen řetězce delegování; null u kořenového ticketu |
spaceId |
String? |
Konverzace, ze které ticket vznikl, nebo prostor delegujícího agenta |
errorMessage |
String? |
Nastaví se, když je status failed |
linkedPrIds |
List<String> |
Id uzlů PR, ke kterým je ticket propojený |
metadata |
Map<String, dynamic> |
Volná mapa |
createdAt |
DateTime |
Čas vytvoření |
startedAt |
DateTime? |
Kdy práce začala |
blockedAt |
DateTime? |
Kdy byl ticket zablokovaný |
cancelledAt |
DateTime? |
Kdy byl ticket zrušený |
completedAt |
DateTime? |
Kdy se ticket úspěšně dokončil |
finishedAt |
DateTime? |
Kdy ticket dosáhl libovolného koncového stavu |
updatedAt |
DateTime |
Poslední mutace (obnovení zrcadla, nebo změna překryvu) |
version |
int |
Čítač optimistické souběžnosti; při každé mutaci se zvýší |
originKind |
TicketOriginKind |
Jak ticket vznikl (výchozí manual) |
collaborators |
List<TicketCollaborator> |
Repozitář je doplní na vyžádání |
Odvozené: isTerminal, isRemote (provider != local), isAssignedToUser a displayKey (externalKey ?? id: místní tickety proto ukazují samostatné UUID z okamžiku založení, ne řádkové id).
Vazby ticketu
Sekce “Vazby ticketu”Uložené hrany jsou orientované řádky v ticket_links:
| Typ | Uložený řetězec | Význam |
|---|---|---|
blocks |
blocks |
Zdroj blokuje cíl |
relatesTo |
relates_to |
Symetrická vazba |
duplicateOf |
duplicate_of |
Zdroj je duplikát cíle |
Slovník, kterým mluví rozhraní a ticket_relation, je TicketRelationKind. Odvozuje se z uložené hrany a z toho, na kterém konci subjekt sedí:
| Druh | Odvozeno z |
|---|---|
blockedBy |
Řádek blocks, kde je subjekt cíl |
blocking |
Řádek blocks, kde je subjekt zdroj |
relatedTo |
Řádek relates_to (symetrický) |
duplicateOf |
Řádek duplicate_of, kde je subjekt zdroj |
duplicatedBy |
Řádek duplicate_of, kde je subjekt cíl |
subIssueOf |
tickets.parent_ticket_id na subjektu |
parentOf |
tickets.parent_ticket_id na druhém ticketu |
Vazby rodiče a podticketu nejsou řádky ticket_links. Žijí na tickets.parent_ticket_id.
Doménové události
Sekce “Doménové události”Těch osm tříd žije v packages/cc_domain/lib/core/domain/events/ticketing_events.dart. TicketStarted, TicketDelegated, TicketCollaboratorAdded ani ExternalTicketWebhookReceived neexistují.
| Událost | Kdy se publikuje |
|---|---|
TicketCreated |
Ticket se vytvoří |
TicketAssigned |
Ticket se přiřadí principálovi nebo týmu |
TicketCompleted |
Práce úspěšně skončí |
TicketFailed |
Práce selže |
TicketCancelled |
Ticket se zruší |
TicketStatusChanged |
Jakákoli změna stavu |
TicketReassigned |
Ticket se přeřadí |
TicketDetailsUpdated |
Změní se název, popis, priorita nebo štítky |
TicketAssigned je signál pro audit a oznámení a zároveň spouštěč pipeline. Přiřazení agentovi nespustí žádný běh: žádný dispečer ticketů nesleduje assignedAgentId. Výjimka je tým: TeamRoutingService odešle vedoucího týmu. Přidání spolupracovníka zapíše řádek a nic nepublikuje. Webhook dodavatele se uplatní přes TicketSyncEngine a doménová událost u toho nevznikne.
Průchod mutací
Sekce “Průchod mutací”TicketWorkflowService vlastní každou mutaci.
| Mechanismus | Detail |
|---|---|
| Jediný průchod | Všechny mutace jdou přes _mutate, které načte řádek a před uplatněním zavolá _assertWorkspace |
| Izolace pracovního prostoru | _assertWorkspace vyhodí WorkspaceMismatchException, když workspaceId načteného řádku nesedí s volajícím |
| Optimistická souběžnost | _mutate zapisuje s expectedVersion nastaveným na version, kterou právě přečetlo, a při ConcurrencyConflictException to zkusí znovu po načtení čerstvého stavu |
| Výhradní převzetí | tryCheckout / releaseCheckout; druhý agent, který si nárokuje už převzatý ticket, dostane CheckoutConflictException |
| Pojistka přechodu | canTransitionTo, uplatní se jen když je force false |