Μετάβαση στο περιεχόμενο

Κύκλος ζωής εισιτηρίου

Κατάσταση Συμβολοσειρά αποθήκευσης Περιγραφή
backlog backlog Καταγραμμένο, δεν έχει ακόμη ταξινομηθεί στην ενεργή ουρά
open open Ταξινομημένο και έτοιμο για εργασία, δεν έχει ξεκινήσει
inProgress inProgress Βρίσκεται σε ενεργή επεξεργασία
blocked blocked Ξεκίνησε αλλά έχει μπλοκάρει σε κάτι εξωτερικό
inReview inReview Η εργασία ολοκληρώθηκε, αναμένει έλεγχο
done done Ολοκληρώθηκε με επιτυχία (τελική)
failed failed Ολοκληρώθηκε με σφάλμα (τελική)
cancelled cancelled Εγκαταλείφθηκε (τελική)

Οι τιμές κατάστασης αποθηκεύονται σε camelCase (inProgress, inReview), ποτέ ως in_progress. Μια αποθηκευμένη τιμή null ερμηνεύεται ως open· μια άγνωστη τιμή προκαλεί σφάλμα αντί να εξαναγκαστεί σε μετατροπή.

Το tryParseLoose δέχεται ψευδώνυμα που παρέχει ο πράκτορας, αγνοώντας τη διάκριση πεζών-κεφαλαίων καθώς και τα - / _ / το κενό:

Κατάσταση Αποδεκτά ψευδώνυμα
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

Αυτός ο γράφος δεσμεύει μόνο τους πράκτορες και τον αυτοματισμό. Το TicketWorkflowService.transitionStatus τον ελέγχει όταν το force είναι false — τα εργαλεία MCP και οι reconcilers. Μια μη έγκυρη μετάβαση εκεί καταγράφεται και αγνοείται. Κάθε αλλαγή κατάστασης που προέρχεται από τον χρήστη στο UI περνά force: true και παρακάμπτει τον γράφο εντελώς, οπότε ένας άνθρωπος μπορεί να μετακινήσει ένα εισιτήριο σε οποιαδήποτε κατάσταση εμφανίζει ο επιλογέας (κάθε κατάσταση εκτός από failed), ακόμη και να ανοίξει ξανά μια τελική κατάσταση. Οι πράκτορες σημειώνουν το failed με το fail_ticket.

Μια κατάσταση μπορεί πάντα να μεταβεί στον εαυτό της. Οι τελικές καταστάσεις δεν έχουν εξερχόμενες ακμές.

Από Επιτρεπόμενοι προορισμοί
backlog open, cancelled
open inProgress, blocked, cancelled
inProgress blocked, inReview, done, failed, cancelled
blocked inProgress, cancelled
inReview inProgress, done, failed, cancelled
done — (τελική)
failed — (τελική)
cancelled — (τελική)

Αποθηκεύονται ως ακέραιος στην εγγενή κλίμακα 0–4 του Linear. Μια άγνωστη τιμή ερμηνεύεται ως none.

Προτεραιότητα Αποθηκεύεται ως
none 0
urgent 1
high 2
medium 3
low 4

Μια άγνωστη ή null αποθηκευμένη τιμή ερμηνεύεται ως local.

Πάροχος Κατάσταση
local Αποθηκεύεται εξ ολοκλήρου στη βάση δεδομένων του χώρου εργασίας· η προεπιλογή
linear Ανήκει σε απομακρυσμένο σύστημα, κατοπτρίζεται τοπικά

Οι προσαρμογείς συγχρονισμού βρίσκονται κάτω από το packages/cc_infra/lib/src/tickets/sync/ για τα Linear, GitHub Issues, Jira Cloud και ClickUp. Το GitHub Issues είναι μόνο προμηθευτής προσαρμογέα συγχρονισμού (vendorId: github) — δεν είναι τιμή TicketProvider, οπότε ένα ζήτημα GitHub που λαμβάνεται με συγχρονισμό αποθηκεύεται με originKind: externalSync και προεπιλεγμένο provider: local. Η αντιστοίχιση των προμηθευτών βρίσκεται στο ticket_sync_links.

Το createTicket γράφει manual. Μια λήψη από προμηθευτή γράφει externalSync. Τα ανατεθειμένα θυγατρικά παραμένουν manual.

Τιμή Σημασία
manual Προεπιλογή στο createTicket (γραμμές ανθρώπων, πρακτόρων και ανατεθειμένων)
externalSync Ορίζεται από το TicketSyncEngine σε μια λήψη από προμηθευτή

Το σύνολο καλύπτει δύο διαφορετικές πτυχές στην ίδια γραμμή. Το κατοπτρικό αντίγραφο (πάροχος, κλειδιά, τίτλος, περιγραφή, προτεραιότητα, ετικέτες, καταστάσεις, χρονικές σημάνσεις) είναι μια προσωρινή μνήμη για έναν απομακρυσμένο πάροχο και ξαναγράφεται εξ ολοκλήρου από μια ανανέωση. Η επικάλυψη (εκχώρηση, ανάθεση, χώρος, γονέας, έργο, συνδεδεμένα PR) υπάρχει μόνο στο Control Center και μια ανανέωση δεν την αγγίζει ποτέ.

Πεδίο Τύπος Περιγραφή
id String UUID v4
workspaceId String Ο χώρος εργασίας στον οποίο ανήκει
provider TicketProvider Το backend που κατέχει τα κανονικά δεδομένα (προεπιλογή local)
externalKey String? Εγγενές κλειδί του παρόχου (π.χ. LIN-123). Το τοπικό createTicket γράφει εδώ ένα ξεχωριστό UUID, όχι το id της γραμμής
url String? URL ιστού στον απομακρυσμένο πάροχο
title String Σύντομη σύνοψη· δεν πρέπει να είναι κενή
description String? Μεγαλύτερο σώμα Markdown
priority TicketPriority Προεπιλογή none
labels List<String> Ετικέτες ελεύθερης μορφής
status TicketStatus Κανονική κατάσταση μετά την κανονικοποίηση
rawStatus String? Το εγγενές όνομα κατάστασης του απομακρυσμένου παρόχου, που διατηρείται για απεικόνιση χωρίς απώλειες
externalSyncedAt DateTime? Ο τελευταίος συγχρονισμός πολλών προμηθευτών· null μέχρι τον πρώτο. Η αντιστοίχιση ανά προμηθευτή βρίσκεται στο ticket_sync_links
parentTicketId String? Γονέας στο δέντρο ανάθεσης / κατάτμησης
projectId String? Έργο στο οποίο ανήκει· δεν προωθείται ποτέ σε απομακρυσμένο σύστημα
assignedAgentId String? Το id του εκχωρημένου υποκειμένου (μόνο μεταδεδομένα — η εκχώρηση δεν αποστέλλει τίποτα)
assigneeType PrincipalType Τι είδους υποκείμενο ονομάζει το assignedAgentId — user ή agent (προεπιλογή agent)
createdByType PrincipalType? Το είδος του υποκειμένου που δημιούργησε το εισιτήριο (user ή agent)· null για γραμμές παλαιού τύπου/συστήματος
createdById String? Το id του υποκειμένου που το δημιούργησε
assignedTeamId String? Εκχωρημένη ομάδα (μόνο μεταδεδομένα)
delegatedByAgentId String? Ο πράκτορας που ανέθεσε αυτό το εισιτήριο
delegationDepth int Βάθος στην αλυσίδα ανάθεσης· η ρίζα είναι 0, ελέγχεται ως προς το ανώτατο όριο βάθους
delegationRootTicketId String? Η ρίζα της αλυσίδας ανάθεσης· null για ένα εισιτήριο ρίζας
spaceId String? Η συνομιλία από την οποία ξεκίνησε το εισιτήριο, ή ο χώρος του πράκτορα που το ανέθεσε
errorMessage String? Ορίζεται όταν το status είναι failed
linkedPrIds List<String> Τα id κόμβων PR με τα οποία είναι συνδεδεμένο αυτό το εισιτήριο
metadata Map<String, dynamic> Σακούλα ελεύθερης μορφής
createdAt DateTime Χρόνος δημιουργίας
startedAt DateTime? Πότε ξεκίνησε η εργασία
blockedAt DateTime? Πότε μπλοκαρίστηκε το εισιτήριο
cancelledAt DateTime? Πότε ακυρώθηκε το εισιτήριο
completedAt DateTime? Πότε το εισιτήριο ολοκληρώθηκε με επιτυχία
finishedAt DateTime? Πότε το εισιτήριο έφτασε σε οποιαδήποτε τελική κατάσταση
updatedAt DateTime Η τελευταία τροποποίηση (ανανέωση του κατοπτρικού αντιγράφου ή αλλαγή της επικάλυψης)
version int Μετρητής αισιόδοξης ταυτοχρονικότητας, που αυξάνεται σε κάθε τροποποίηση
originKind TicketOriginKind Πώς δημιουργήθηκε το εισιτήριο (προεπιλογή manual)
collaborators List<TicketCollaborator> Φορτώνονται από το αποθετήριο κατ’ απαίτηση

Παράγωγα: isTerminal, isRemote (provider != local), isAssignedToUser και displayKey (externalKey ?? id — τα τοπικά εισιτήρια εμφανίζουν επομένως το ξεχωριστό UUID της στιγμής δημιουργίας, όχι το id της γραμμής).

Οι αποθηκευμένες ακμές είναι κατευθυντικές γραμμές στο ticket_links:

Τύπος Συμβολοσειρά αποθήκευσης Σημασία
blocks blocks Η πηγή μπλοκάρει τον στόχο
relatesTo relates_to Συμμετρική σχέση
duplicateOf duplicate_of Η πηγή είναι αντίγραφο του στόχου

Το λεξιλόγιο που μιλούν το UI και το ticket_relation είναι το TicketRelationKind, το οποίο προκύπτει από μια αποθηκευμένη ακμή συν σε ποιο άκρο βρίσκεται το υποκείμενο εισιτήριο:

Είδος Προκύπτει από
blockedBy Μια γραμμή blocks όπου το υποκείμενο εισιτήριο είναι ο στόχος
blocking Μια γραμμή blocks όπου το υποκείμενο εισιτήριο είναι η πηγή
relatedTo Μια γραμμή relates_to (συμμετρική)
duplicateOf Μια γραμμή duplicate_of όπου το υποκείμενο εισιτήριο είναι η πηγή
duplicatedBy Μια γραμμή duplicate_of όπου το υποκείμενο εισιτήριο είναι ο στόχος
subIssueOf Το tickets.parent_ticket_id στο υποκείμενο εισιτήριο
parentOf Το tickets.parent_ticket_id στο άλλο εισιτήριο

Οι σύνδεσμοι γονέα και υποζητήματος δεν είναι γραμμές του ticket_links — βρίσκονται στο tickets.parent_ticket_id.

Αυτές οι οκτώ κλάσεις βρίσκονται στο packages/cc_domain/lib/core/domain/events/ticketing_events.dart. Δεν υπάρχει TicketStarted, TicketDelegated, TicketCollaboratorAdded ή ExternalTicketWebhookReceived.

Γεγονός Πυροδοτείται όταν
TicketCreated Δημιουργείται ένα εισιτήριο
TicketAssigned Ένα εισιτήριο εκχωρείται σε ένα υποκείμενο ή μια ομάδα
TicketCompleted Η εργασία ολοκληρώνεται με επιτυχία
TicketFailed Η εργασία αποτυγχάνει
TicketCancelled Ένα εισιτήριο ακυρώνεται
TicketStatusChanged Οποιαδήποτε αλλαγή κατάστασης
TicketReassigned Ένα εισιτήριο ανατίθεται εκ νέου
TicketDetailsUpdated Αλλάζουν ο τίτλος, η περιγραφή, η προτεραιότητα ή οι ετικέτες

Το TicketAssigned είναι σήμα ελέγχου και ειδοποίησης και ενεργοποιητής pipeline. Η εκχώρηση σε έναν πράκτορα δεν ξεκινά καμία εκτέλεση — δεν υπάρχει αποστολέας εισιτηρίων που να παρακολουθεί το assignedAgentId. Η εκχώρηση σε μια ομάδα αποτελεί την εξαίρεση: το TeamRoutingService αποστέλλει τον αρχηγό της ομάδας. Η προσθήκη ενός συνεργάτη γράφει μια γραμμή και δεν δημοσιεύει τίποτα. Ένα webhook προμηθευτή εφαρμόζεται μέσω του TicketSyncEngine χωρίς γεγονός τομέα.

Το TicketWorkflowService κατέχει κάθε τροποποίηση.

Μηχανισμός Λεπτομέρεια
Ένα μοναδικό σημείο συμφόρησης Όλες οι τροποποιήσεις δρομολογούνται μέσω του _mutate, το οποίο φορτώνει τη γραμμή και καλεί το _assertWorkspace πριν εφαρμόσει
Απομόνωση χώρου εργασίας Το _assertWorkspace εκπέμπει WorkspaceMismatchException όταν το workspaceId της φορτωμένης γραμμής δεν ταιριάζει με αυτό του καλούντος
Αισιόδοξη ταυτοχρονικότητα Το _mutate γράφει με expectedVersion ορισμένο στο version που μόλις διάβασε και επαναλαμβάνει σε ConcurrencyConflictException διαβάζοντας εκ νέου την τρέχουσα κατάσταση
Αποκλειστική διεκδίκηση tryCheckout / releaseCheckout· ένας δεύτερος πράκτορας που διεκδικεί ένα εισιτήριο σε κατάσταση checkout λαμβάνει CheckoutConflictException
Φρουρός μετάβασης canTransitionTo, που εφαρμόζεται μόνο όταν το force είναι false