Langkau ke kandungan

Kitaran hayat tiket

Status Rentetan storan Perihalan
backlog backlog Direkodkan, belum melalui triage ke dalam baris gilir aktif
open open Telah melalui triage dan sedia dikerjakan, belum bermula
inProgress inProgress Sedang dikerjakan secara aktif
blocked blocked Sudah bermula tetapi terhalang oleh sesuatu yang luaran
inReview inReview Kerja selesai, menunggu semakan
done done Selesai dengan jayanya (terminal)
failed failed Selesai dengan ralat (terminal)
cancelled cancelled Ditinggalkan (terminal)

Nilai status disimpan dalam bentuk camelCase (inProgress, inReview), tidak pernah in_progress. Nilai tersimpan yang null dihuraikan sebagai open; nilai yang tidak dikenali melontar ralat dan tidak dipaksakan.

tryParseLoose menerima alias yang dibekalkan ejen, mengabaikan kepekaan huruf besar/kecil dan aksara - / _ / ruang:

Status Alias yang diterima
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

Graf ini mengikat ejen dan automasi sahaja. TicketWorkflowService.transitionStatus menyemaknya apabila force ialah false — alat MCP dan penyelaras. Peralihan haram di situ dilog dan diabaikan. Setiap perubahan status yang dipacu pengguna dalam UI menghantar force: true dan melangkau graf itu sepenuhnya, jadi manusia boleh memindahkan tiket ke mana-mana status yang disenaraikan oleh pemilih (setiap status kecuali failed), termasuk membuka semula tiket yang berada dalam keadaan terminal. Ejen menandakan failed dengan fail_ticket.

Status boleh sentiasa beralih kepada dirinya sendiri. Keadaan terminal tiada tepi keluar.

Daripada Sasaran yang dibenarkan
backlog open, cancelled
open inProgress, blocked, cancelled
inProgress blocked, inReview, done, failed, cancelled
blocked inProgress, cancelled
inReview inProgress, done, failed, cancelled
done — (terminal)
failed — (terminal)
cancelled — (terminal)

Disimpan sebagai integer pada skala asli Linear 0–4. Nilai yang tidak dikenali dihuraikan sebagai none.

Keutamaan Disimpan sebagai
none 0
urgent 1
high 2
medium 3
low 4

Nilai tersimpan yang tidak dikenali atau null dihuraikan sebagai local.

Penyedia Status
local Disimpan sepenuhnya dalam pangkalan data ruang kerja; lalai
linear Dimiliki jauh, dicerminkan secara tempatan

Penyesuai penyegerakan terletak di bawah packages/cc_infra/lib/src/tickets/sync/ untuk Linear, GitHub Issues, Jira Cloud dan ClickUp. GitHub Issues hanyalah vendor penyesuai penyegerakan (vendorId: github) — ia bukan nilai TicketProvider, jadi isu GitHub yang ditarik disimpan dengan originKind: externalSync dan provider: local lalai. Pemetaan vendor terletak pada ticket_sync_links.

createTicket menulis manual. Tarikan vendor menulis externalSync. Anak yang diwakilkan kekal manual.

Nilai Maksud
manual Lalai pada createTicket (baris manusia, ejen dan yang diwakilkan)
externalSync Ditetapkan oleh TicketSyncEngine pada tarikan vendor

Agregat ini merangkumi dua kebimbangan pada satu baris. Cermin (penyedia, kunci, tajuk, perihalan, keutamaan, label, status, cap masa) ialah cache untuk penyedia jauh dan ditulis semula sepenuhnya oleh penyegaran. Tindanan (penugasan, perwakilan, ruang, induk, projek, PR berpaut) hanya milik Control Center dan penyegaran tidak pernah menyentuhnya.

Medan Jenis Perihalan
id String UUID v4
workspaceId String Ruang kerja pemilik
provider TicketProvider Bahagian belakang yang memiliki data kanonik (lalai local)
externalKey String? Kunci asli penyedia (cth. LIN-123). createTicket tempatan menulis UUID berasingan di sini, bukan id baris
url String? URL web pada penyedia jauh
title String Ringkasan pendek; tidak boleh kosong
description String? Badan Markdown yang lebih panjang
priority TicketPriority Lalai none
labels List<String> Label bentuk bebas
status TicketStatus Status ternormal yang kanonik
rawStatus String? Nama keadaan asli penyedia jauh, disimpan untuk paparan tanpa kehilangan
externalSyncedAt DateTime? Penyegerakan berbilang vendor terakhir; null sehingga yang pertama. Pemetaan setiap vendor terletak dalam ticket_sync_links
parentTicketId String? Induk dalam pokok perwakilan / pecahan
projectId String? Projek pemilik; tidak pernah ditolak ke jauh
assignedAgentId String? Id prinsipal yang ditugaskan (metadata sahaja — menugaskan tidak menghantar apa-apa)
assigneeType PrincipalType Jenis prinsipal yang dinamakan oleh assignedAgentId — user atau agent (lalai agent)
createdByType PrincipalType? Jenis prinsipal yang mencipta tiket (user atau agent); null untuk baris legasi/sistem
createdById String? Id prinsipal pencipta
assignedTeamId String? Pasukan yang ditugaskan (metadata sahaja)
delegatedByAgentId String? Ejen yang mewakilkan tiket ini
delegationDepth int Kedalaman dalam rantaian perwakilan; akar ialah 0, disemak terhadap siling kedalaman maksimum
delegationRootTicketId String? Akar rantaian perwakilan; null untuk tiket akar
spaceId String? Perbualan tempat tiket ini dipecahkan, atau ruang ejen yang mewakilkan
errorMessage String? Ditetapkan apabila status ialah failed
linkedPrIds List<String> Id nod PR yang dipautkan pada tiket ini
metadata Map<String, dynamic> Beg bentuk bebas
createdAt DateTime Masa penciptaan
startedAt DateTime? Apabila kerja bermula
blockedAt DateTime? Apabila tiket terhalang
cancelledAt DateTime? Apabila tiket dibatalkan
completedAt DateTime? Apabila tiket selesai dengan jayanya
finishedAt DateTime? Apabila tiket mencapai mana-mana keadaan terminal
updatedAt DateTime Mutasi terakhir (penyegaran cermin atau perubahan tindanan)
version int Pembilang konkurensi optimistik, dinaikkan pada setiap mutasi
originKind TicketOriginKind Bagaimana tiket dicipta (lalai manual)
collaborators List<TicketCollaborator> Dihidratkan oleh repositori atas permintaan

Terbitan: isTerminal, isRemote (provider != local), isAssignedToUser dan displayKey (externalKey ?? id — maka tiket tempatan memaparkan UUID berasingan yang dicipta pada masa penciptaan, bukan id baris).

Tepi yang disimpan ialah baris berarah dalam ticket_links:

Jenis Rentetan storan Maksud
blocks blocks Sumber menyekat sasaran
relatesTo relates_to Hubungan simetri
duplicateOf duplicate_of Sumber ialah pendua kepada sasaran

Perbendaharaan kata yang digunakan oleh UI dan ticket_relation ialah TicketRelationKind, diterbitkan daripada tepi yang disimpan serta hujung mana subjek itu berada:

Jenis Diterbitkan daripada
blockedBy Baris blocks yang subjeknya ialah sasaran
blocking Baris blocks yang subjeknya ialah sumber
relatedTo Baris relates_to (simetri)
duplicateOf Baris duplicate_of yang subjeknya ialah sumber
duplicatedBy Baris duplicate_of yang subjeknya ialah sasaran
subIssueOf tickets.parent_ticket_id pada subjek
parentOf tickets.parent_ticket_id pada tiket yang satu lagi

Pautan induk dan sub-isu bukan baris ticket_links — ia terletak pada tickets.parent_ticket_id.

Lapan kelas ini terletak dalam packages/cc_domain/lib/core/domain/events/ticketing_events.dart. Tiada TicketStarted, TicketDelegated, TicketCollaboratorAdded, atau ExternalTicketWebhookReceived.

Acara Dicetuskan apabila
TicketCreated Tiket dicipta
TicketAssigned Tiket ditugaskan kepada prinsipal atau pasukan
TicketCompleted Kerja selesai dengan jayanya
TicketFailed Kerja gagal
TicketCancelled Tiket dibatalkan
TicketStatusChanged Sebarang perubahan status
TicketReassigned Tiket ditugaskan semula
TicketDetailsUpdated Tajuk, perihalan, keutamaan atau label berubah

TicketAssigned ialah isyarat audit dan pemberitahuan serta pencetus pipeline. Penugasan kepada ejen tidak memulakan sebarang larian — tiada penghantar tiket yang memantau assignedAgentId. Penugasan kepada pasukan ialah pengecualian: TeamRoutingService menghantar ketua pasukan. Menambah kolaborator menulis satu baris dan tidak menerbitkan apa-apa. Webhook vendor digunakan melalui TicketSyncEngine tanpa acara domain.

TicketWorkflowService memiliki setiap mutasi.

Mekanisme Butiran
Titik kawalan tunggal Semua mutasi dihalakan melalui _mutate, yang memuatkan baris itu dan memanggil _assertWorkspace sebelum menerapkannya
Pengasingan ruang kerja _assertWorkspace melontar WorkspaceMismatchException apabila workspaceId baris yang dimuatkan tidak sepadan dengan pemanggil
Konkurensi optimistik _mutate menulis dengan expectedVersion ditetapkan kepada version yang baru dibacanya dan mencuba semula pada ConcurrencyConflictException dengan membaca semula keadaan terkini
Tuntutan eksklusif tryCheckout / releaseCheckout; ejen kedua yang menuntut tiket yang sudah di-checkout mendapat CheckoutConflictException
Pengawal peralihan canTransitionTo, digunakan hanya apabila force ialah false