ข้ามไปยังเนื้อหา

ตั๋วงานและการมอบหมาย

ตั๋วงานเป็นหน่วยของงานที่ Control Center ติดตาม มันเป็นอาร์ติแฟกต์ที่ โง่ โดยเจตนา: มีชื่อ เนื้อหา สถานะ เจ้าของ และตำแหน่งในโครงสร้างต้นไม้ มันไม่ใช่สิ่งที่เอเจนต์ลงมือทำ — งานของเอเจนต์อยู่ในบทสนทนา และเอาต์พุตแบบมีโครงสร้างของการรันอยู่บนบันทึกรัน

การกำหนดผู้รับมอบหมายบันทึกความเป็นเจ้าของ ไม่ได้จัดส่งสิ่งใด

หัวข้อที่มีชื่อว่า “การกำหนดผู้รับมอบหมายบันทึกความเป็นเจ้าของ ไม่ได้จัดส่งสิ่งใด”

นี่คือสิ่งสำคัญที่สุดเพียงเรื่องเดียวที่ต้องทำให้ถูกต้องเกี่ยวกับตั๋วงาน

การตั้งค่า assignedAgentId เขียนค่าลงฟิลด์หนึ่งและเผยแพร่ เหตุการณ์โดเมน TicketAssigned เหตุการณ์นั้นเป็นสัญญาณสำหรับการตรวจสอบและการแจ้งเตือน รวมทั้งเป็นทริกเกอร์ของไปป์ไลน์ การมอบหมายให้เอเจนต์ไม่เริ่มการรันใด ๆ ไม่มีตัวจัดส่งตั๋วงานที่คอยเฝ้า assignedAgentId แล้วปลุกเอเจนต์ตัวนั้น

ข้อยกเว้นคือทีม: เมื่อตั้งค่า assignedTeamId แล้ว TeamRoutingService จะจัดส่งหัวหน้าทีมพร้อมโปรโตคอลการทำงานและรายชื่อสมาชิก

หากคุณต้องการให้เอเจนต์ตัวใดตัวหนึ่งทำงานบนตั๋วงาน คุณต้องขอให้มันทำในบทสนทนา หรือให้ขั้นตอนของไปป์ไลน์จัดส่งมัน หรือให้เอเจนต์อีกตัวมอบหมายงานให้มัน การมอบหมายตั๋วงานให้เอเจนต์แล้วรอ คือสิ่งเดียวที่จะไม่มีวันทำให้เกิดงานขึ้นมา

ตั๋วงานอาจอยู่ภายในเครื่องล้วน ๆ หรือมีตัวติดตามภายนอกหนุนหลัง และมันเก็บทั้งสองส่วนไว้ในแถวเดียวกัน

มิเรอร์ — ผู้ให้บริการ คีย์ภายนอก URL ชื่อ คำอธิบาย ลำดับความสำคัญ ป้ายกำกับ สถานะดิบและสถานะที่ปรับรูปแบบแล้ว และการประทับเวลา — เป็นแคชเมื่อตั๋วงานอยู่บนระบบระยะไกล ในกรณีนั้นระบบระยะไกลคือแหล่งความจริง และการรีเฟรชจะเขียนมิเรอร์ใหม่ทั้งหมด

โอเวอร์เลย์ — การกำหนดผู้รับมอบหมาย การมอบหมาย ลิงก์สเปซ ตั๋วแม่ โปรเจกต์ และ PR ที่ลิงก์ไว้ — เป็นข้อมูลของ Control Center เท่านั้น ตัวติดตามไม่รู้อะไรเกี่ยวกับส่วนนี้เลย และการรีเฟรชไม่เคยแตะต้องมัน

การแยกส่วนแบบนี้เองที่ทำให้การซิงค์ปลอดภัยตั้งแต่แรก: การดึงข้อมูลจากผู้ให้บริการอาจเขียนทับมิเรอร์ได้อย่างรุนแรง โดยไม่สูญเสียเมทาดาทาของการประสานงานที่ซ้อนอยู่ด้านบนเลย

สถานะไหลเป็น backlog → open → inProgress → inReview → done โดยมี blocked เป็นวงวนข้าง และ failed / cancelled เป็นสถานะสิ้นสุดอีกสองสถานะ

กราฟการเปลี่ยนสถานะถูกบังคับใช้ เฉพาะบนเส้นทางของเอเจนต์และระบบอัตโนมัติ เท่านั้น — เครื่องมือ MCP และตัวกระทบยอด การเปลี่ยนสถานะที่ผิดกฎในเส้นทางนั้นจะถูกบันทึกไว้และเพิกเฉย ดังนั้นเอเจนต์ที่สับสนจะพาตั๋วงานย้อนกลับออกจากสถานะสิ้นสุดไม่ได้

ทุกการเปลี่ยนแปลงที่ผู้ใช้สั่งใน UI จะส่ง force: true และข้ามกราฟไปทั้งหมด มนุษย์ย้ายตั๋วงานไปยังสถานะใดก็ได้ที่ตัวเลือกแสดงไว้ — รวมถึงการเปิดสถานะสิ้นสุดกลับมาอีกครั้ง ตัวเลือกนี้ไม่มี failed เอเจนต์จะตั้งสถานะนั้นผ่าน fail_ticket กราฟนี้เป็นราวกันตกของระบบอัตโนมัติ ไม่ใช่กฎว่าตัวงานต้องดำเนินไปอย่างไร

TicketWorkflowService เป็นเจ้าของการแก้ไขทุกครั้งผ่านจุดคอขวดจุดเดียว ซึ่งโหลดแถวข้อมูล ตรวจสอบว่าแถวนั้นอยู่ในเวิร์กสเปซของผู้เรียก และเขียนด้วย version ที่คาดไว้ เพื่อให้การแก้ไขสองครั้งที่เกิดพร้อมกันเขียนทับกันอย่างเงียบ ๆ ไม่ได้

สำหรับตารางสถานะทั้งหมด การเปลี่ยนสถานะ รายการฟิลด์ และชนิดความสัมพันธ์ ดูได้ที่ วงจรชีวิตตั๋วงาน

ตั๋วงานทุกใบมี originKind อีนัมนี้มีห้าค่า — manual, pipelineStep, agentDelegation, externalSync, recovery — แต่ทุกวันนี้มีเพียงสองค่าที่ถูกเขียนจริง: manual (ค่าเริ่มต้นของ createTicket รวมถึงตั๋วลูกที่ถูกมอบหมาย) และ externalSync (เอนจินซิงค์ เมื่อดึงข้อมูลจากผู้ให้บริการ) TicketWorkflowService.createTicket ไม่รับอาร์กิวเมนต์ที่มาของตั๋ว ดังนั้น pipelineStep, agentDelegation และ recovery จึงไม่เคยถูกเขียนลงแถวข้อมูล

การมอบหมายคือจุดที่เอเจนต์สร้างงานให้เอเจนต์อีกตัว และเป็นจุดที่สิทธิ์อาจรั่วไหลได้ ตัวป้องกันจึงอยู่ฝั่งเซิร์ฟเวอร์ที่จุดคอขวด ไม่ได้อยู่ในคำสั่งในพรอมต์

delegate_task วิ่งผ่าน TicketWorkflowService.delegateGuarded ซึ่งคำนวณความลึกและรากของตั๋วลูกจากสายของตั๋วแม่ แล้วรันการตรวจทั้งสี่ข้อของ DelegationGuards: การกระโดดที่จะเกิน เพดานความลึกที่ 3, การกระโดดกลับไปยังเอเจนต์ที่อยู่ในสายแล้ว (การตรวจจับวงจร), เพดานความเป็นอิสระ (ระดับที่มีผลจริงของผู้รับมอบหมายในสเปซต้องไม่เกินของผู้มอบหมาย) และ กรอบวงเงินงบประมาณ (การมอบหมายหักจากงบประมาณหยุดแข็งที่เหลืออยู่ของผู้มอบหมาย และสร้างเพิ่มไม่ได้) การต่อสายในโปรดักชันจัดหาพอร์ตรีโซลเวอร์ทั้งสองตัว การตรวจความเป็นอิสระและงบประมาณจะถูกข้ามเมื่อไม่ระบุ from_agent_id (งบประมาณแบบไม่จำกัด/ไม่มีนโยบายก็ผ่านเช่นกัน) การปฏิเสธจะถูกโยนออกมาเป็น DelegationRefusedException ซึ่งข้อความของมันไปถึงเอเจนต์ที่มอบหมายแบบคำต่อคำ — เป็นการปฏิเสธที่ดังก้อง ไม่ใช่การไม่ทำอะไรอย่างเงียบ ๆ

delegate_ticket เป็นคู่หูรุ่นเก่าที่ ไม่มีการป้องกัน: มันเรียก createTicket โดยตรงและข้ามการตรวจทุกข้อ ควรใช้ delegate_task แทน

อย่าสับสนระหว่างเพดานความลึกของการมอบหมาย (3 สำหรับสายของ delegate_task) กับเพดานการซ้อนของซับเอเจนต์ของฮาร์เนสในตัว (2) ทั้งสองเป็นขีดจำกัดที่ต่างกันของกลไกที่ต่างกัน

ความสัมพันธ์แบบตั๋วแม่และตั๋วย่อยอยู่ใน tickets.parent_ticket_id ซึ่งดูแลโดย setParent / clearParent ความสัมพันธ์เหล่านั้น ไม่ใช่ แถวใน ticket_links ดังนั้นอะไรก็ตามที่อ่านความสัมพันธ์ของตั๋วงานต้องรวมข้อมูลจากทั้งสองแหล่ง

ticket_links เก็บเส้นเชื่อมแบบมีทิศทางระหว่างตั๋วงานที่ไม่เกี่ยวข้องกันโดยตรง:

ชนิดลิงก์ ความหมาย
blocks ตั๋วต้นทางบล็อกตั๋วปลายทาง
relatesTo ตั๋วงานทั้งสองเกี่ยวข้องกัน (สมมาตร)
duplicateOf ตั๋วต้นทางเป็นสำเนาซ้ำของตั๋วปลายทาง

โปรเจกต์ คือการจัดกลุ่มตั๋วงานภายในขอบเขตของเวิร์กสเปซเพื่อมุ่งสู่เป้าหมายร่วมกัน โปรเจกต์เป็นข้อมูลของ Control Center เท่านั้นและไม่เคยซิงค์ไปยังผู้ให้บริการภายนอก

แถวของ TicketCollaborator คือวิธีที่ผูกตัวตนมากกว่าหนึ่งตัวเข้ากับตั๋วงาน โดยมีบทบาทเป็น assignee, collaborator หรือ reviewer มนุษย์และเอเจนต์เป็นแถวที่เท่าเทียมกัน — มี principalId บวกกับ collaboratorType — โดยไม่มีค่าพิเศษแทน “ผู้ใช้” และไม่มีคีย์นอกไปยังตารางใดตารางหนึ่ง

ตั๋วงานของ Control Center เป็นข้อมูลหลัก การลบตั๋วงานมีผล เฉพาะภายในเครื่องเท่านั้น ดังนั้นตั๋วงานที่หนุนหลังโดยผู้ให้บริการซึ่งคุณลบไปจะไม่ถูกลบบนตัวติดตาม และอาจปรากฏกลับมาอีกครั้งในการซิงค์ครั้งถัดไป ผู้ร่วมงานและตั๋วงานลูกจะถูกลบตามไปด้วย

ข้อมูลรับรองของอะแดปเตอร์จะถูกดึงจากที่เก็บข้อมูลรับรองของเซิร์ฟเวอร์ (คีย์ Linear หรือโทเค็น GitHub ที่วางไว้ในการตั้งค่าจะถูกนำมาใช้ในการร้องขอครั้งถัดไป ส่วนตัวแปรสภาพแวดล้อมเพียงเติมค่าเริ่มต้นให้ที่เก็บเหล่านั้นเท่านั้น)

ไม่มีโหนดไปป์ไลน์ใดสร้าง ทำเครื่องหมายเสร็จ หรือยกเลิกตั๋วงาน — พาเลตต์ไม่มีขั้นตอนแบบนั้น และเอนจินไม่ได้เรียก TicketWorkflowService ทริกเกอร์ของไปป์ไลน์ยังทำงานเมื่อเกิด TicketAssigned, TicketCompleted, TicketFailed หรือ TicketCancelled ได้ (สี่เหตุการณ์นี้คือเหตุการณ์ตั๋วงานที่ EventPayloadMapper แมปจริง) ไม่มีคอลัมน์ pipelineRunId บนตั๋วงาน การเชื่อมโยงกับไปป์ไลน์อยู่บนบันทึกรันของเอเจนต์