Κύκλος ζωής εισιτηρίου
Καταστάσεις εισιτηρίου
Ενότητα με τίτλο «Καταστάσεις εισιτηρίου»| Κατάσταση | Συμβολοσειρά αποθήκευσης | Περιγραφή |
|---|---|---|
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 |