วงจรชีวิตตั๋วงาน
สถานะตั๋วงาน
หัวข้อที่มีชื่อว่า “สถานะตั๋วงาน”| สถานะ | สตริงที่จัดเก็บ | คำอธิบาย |
|---|---|---|
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 และตัวกระทบยอด การเปลี่ยนสถานะที่ผิดกฎตรงจุดนั้นจะถูกบันทึกและเพิกเฉย ทุกการเปลี่ยนสถานะที่ผู้ใช้สั่งใน 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 issue ที่ถูกดึงมาจึงถูกจัดเก็บด้วย 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 |
แบ็กเอนด์ที่เป็นเจ้าของข้อมูลหลัก (ค่าเริ่มต้น 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 เป็นสัญญาณสำหรับการตรวจสอบและการแจ้งเตือน รวมทั้งเป็นทริกเกอร์ของไปป์ไลน์ การมอบหมายให้เอเจนต์ไม่เริ่มการรันใด ๆ — ไม่มีตัวจัดส่งตั๋วงานที่คอยเฝ้า assignedAgentId ข้อยกเว้นคือการมอบหมายให้ทีม: TeamRoutingService จะจัดส่งหัวหน้าทีม การเพิ่มผู้ร่วมงานเขียนหนึ่งแถวและไม่เผยแพร่สิ่งใด เว็บฮุกจากผู้ให้บริการภายนอกถูกนำไปใช้ผ่าน TicketSyncEngine โดยไม่มีเหตุการณ์โดเมน
จุดคอขวดของการแก้ไข
หัวข้อที่มีชื่อว่า “จุดคอขวดของการแก้ไข”TicketWorkflowService เป็นเจ้าของการแก้ไขทุกครั้ง
| กลไก | รายละเอียด |
|---|---|
| จุดคอขวดจุดเดียว | การแก้ไขทั้งหมดวิ่งผ่าน _mutate ซึ่งโหลดแถวข้อมูลและเรียก _assertWorkspace ก่อนที่จะนำไปใช้ |
| การแยกเวิร์กสเปซ | _assertWorkspace โยน WorkspaceMismatchException เมื่อ workspaceId ของแถวที่โหลดมาไม่ตรงกับของผู้เรียก |
| การทำงานพร้อมกันแบบออปติมิสติก | _mutate เขียนโดยตั้ง expectedVersion เป็น version ที่เพิ่งอ่านมา และลองใหม่เมื่อเกิด ConcurrencyConflictException โดยอ่านสถานะใหม่ล่าสุด |
| การจองแบบเอกสิทธิ์ | tryCheckout / releaseCheckout; เอเจนต์ตัวที่สองที่จองตั๋วงานที่ถูกเช็คเอาต์อยู่จะได้รับ CheckoutConflictException |
| ตัวป้องกันการเปลี่ยนสถานะ | canTransitionTo ใช้เฉพาะเมื่อ force เป็น false |