Zum Inhalt springen

Tickets und Delegation

Ein Ticket ist die Arbeitseinheit, die Control Center festhält. Es ist absichtlich ein dummes Artefakt: ein Titel, eine Beschreibung, ein Status, ein Eigentümer und ein Platz in einem Baum. Ausgeführt wird die Arbeit nicht am Ticket. Sie lebt in Unterhaltungen, und die strukturierte Ausgabe eines Laufs liegt im Laufprotokoll.

Zuweisung hält die Zuständigkeit fest; sie startet nichts

Abschnitt betitelt „Zuweisung hält die Zuständigkeit fest; sie startet nichts“

Das ist das Wichtigste, das du bei Tickets richtig verstehen musst.

assignedAgentId zu setzen schreibt ein Feld und veröffentlicht ein TicketAssigned-Domain-Ereignis. Das Ereignis ist ein Audit- und Benachrichtigungssignal und ein Pipeline-Trigger. Die Zuweisung an einen Agenten startet keinen Lauf. Es gibt keinen Ticket-Dispatcher, der assignedAgentId beobachtet und diesen Agenten aufweckt.

Die Ausnahme ist ein Team: Ist assignedTeamId gesetzt, startet TeamRoutingService den Teamleiter mit dem Betriebsprotokoll und der Besetzung.

Soll ein bestimmter Agent an einem Ticket arbeiten, fragst du ihn in einer Unterhaltung, ein Pipeline-Schritt startet ihn, oder ein anderer Agent delegiert an ihn. Ein Ticket einem Agenten zuzuweisen und dann zu warten erzeugt nie Arbeit.

Ein Ticket kann rein lokal sein oder bei einem externen Tracker liegen, und beides steht in derselben Zeile.

Der Spiegel — Anbieter, externer Schlüssel, URL, Titel, Beschreibung, Priorität, Labels, roher und normalisierter Status, Zeitstempel — ist ein Cache, wenn das Ticket extern liegt. Dort ist die Gegenstelle die Quelle der Wahrheit, und eine Aktualisierung schreibt den Spiegel vollständig neu.

Die Überlagerung — Zuweisung, Delegation, Bereichsverknüpfung, Eltern-Ticket, Projekt, verknüpfte PRs — gehört nur Control Center. Der Tracker weiß nichts davon, und eine Aktualisierung fasst sie nie an.

Erst diese Trennung macht das Synchronisieren überhaupt sicher: Ein Abruf beim Anbieter darf den Spiegel zerstören, ohne die Orchestrierungsmetadaten zu verlieren, die darüber liegen.

Die Status laufen backlog → open → inProgress → inReview → done, mit blocked als Seitenschleife und failed / cancelled als den anderen Endzuständen.

Der Übergangsgraph gilt nur auf dem Agenten- und Automatisierungspfad, also bei MCP-Tools und Reconcilern. Ein unerlaubter Übergang wird dort protokolliert und ignoriert, damit ein verwirrter Agent ein Ticket nicht rückwärts aus einem Endzustand herausführt.

Jede Änderung aus der Oberfläche übergibt force: true und umgeht den Graphen vollständig. Ein Mensch darf ein Ticket auf jeden Status setzen, den die Auswahl anbietet, auch einen Endzustand wieder öffnen. Die Auswahl bietet failed nicht an; den setzen Agenten über fail_ticket. Der Graph ist eine Leitplanke für die Automatisierung, keine Vorschrift dafür, wie Arbeit verlaufen darf.

TicketWorkflowService besitzt jede Änderung über einen einzigen Engpass. Der lädt die Zeile, prüft, dass sie zum Arbeitsbereich des Aufrufers gehört, und schreibt mit einer erwarteten version, damit zwei gleichzeitige Bearbeitungen einander nicht still überschreiben.

Die vollständige Statustabelle, die Übergänge, die Feldliste und die Beziehungsarten stehen unter Ticket-Lebenszyklus.

Jedes Ticket hat ein originKind. Das Enum hat fünf Werte — manual, pipelineStep, agentDelegation, externalSync, recovery —, geschrieben werden heute aber nur zwei: manual (der Standard von createTicket, auch bei delegierten Untertickets) und externalSync (die Sync-Engine, bei einem Abruf vom Anbieter). TicketWorkflowService.createTicket nimmt kein Argument für die Herkunft, deshalb landen pipelineStep, agentDelegation und recovery nie in einer Zeile.

Delegation ist die Stelle, an der ein Agent Arbeit für einen anderen Agenten anlegt, und genau dort könnten Privilegien durchsickern. Die Prüfungen sitzen deshalb serverseitig an einem Engpass und nicht in den Anweisungen des Prompts.

delegate_task läuft über TicketWorkflowService.delegateGuarded. Der berechnet Tiefe und Wurzel des Untertickets aus der Elternkette und führt dann alle vier Prüfungen von DelegationGuards aus: ein Sprung, der eine Tiefenobergrenze von 3 überschreiten würde, ein Sprung zurück zu einem Agenten, der schon in der Kette steht (Zykluserkennung), eine Autonomieobergrenze (die wirksame Stufe des Delegierten im Bereich darf die des Delegierenden nicht übersteigen) und ein Budgetrahmen (die Delegation bucht auf das verbleibende Budget des Delegierenden bis zum harten Stopp und kann kein weiteres erzeugen). Die Verdrahtung in der Produktion stellt beide Resolver-Ports bereit. Autonomie und Budget entfallen, wenn from_agent_id fehlt (ein unbegrenztes Budget und eines ohne Richtlinie gehen ebenfalls durch). Eine Ablehnung wird als DelegationRefusedException geworfen, und ihre Meldung erreicht den delegierenden Agenten wortgetreu: eine laute Absage, nie ein stilles Nichtstun.

delegate_ticket ist das ältere, ungesicherte Gegenstück: Es ruft createTicket direkt auf und überspringt jede Prüfung. Bevorzuge delegate_task.

Verwechsle die Tiefenobergrenze der Delegation (3, auf Ketten von delegate_task) nicht mit der Verschachtelungsobergrenze für Subagenten des eingebauten Harness (2). Das sind verschiedene Grenzen an verschiedenen Mechanismen.

Eltern- und Untertickets leben auf tickets.parent_ticket_id, gesetzt über setParent / clearParent. Das sind keine Zeilen in ticket_links. Wer die Beziehungen eines Tickets liest, muss beide Quellen zusammenführen.

ticket_links hält die gerichteten Kanten zwischen Tickets, die sonst nichts miteinander zu tun haben:

Linktyp Bedeutung
blocks Die Quelle blockiert das Ziel
relatesTo Die beiden Tickets gehören zusammen (symmetrisch)
duplicateOf Die Quelle ist ein Duplikat des Ziels

Ein Projekt ist eine auf den Arbeitsbereich begrenzte Gruppe von Tickets mit einem gemeinsamen Ziel. Projekte gehören nur Control Center und werden nie zu einem externen Anbieter synchronisiert.

Über TicketCollaborator-Zeilen hängen mehr als ein Prinzipal an einem Ticket, in der Rolle assignee, collaborator oder reviewer. Menschen und Agenten sind gleichrangige Zeilen — eine principalId plus ein collaboratorType —, ohne Sentinel-Wert für „den Benutzer“ und ohne Fremdschlüssel auf eine der beiden Tabellen.

Maßgeblich ist das Ticket in Control Center. Löschen ist nur lokal. Löschst du ein Ticket, das beim Anbieter liegt, bleibt es auf dem Tracker bestehen und kann bei der nächsten Synchronisierung wieder auftauchen. Mitwirkende und Untertickets werden kaskadierend gelöscht.

Die Zugangsdaten des Adapters kommen aus den Zugangsdatenspeichern des Servers (ein Linear-Schlüssel oder ein GitHub-Token, den du in den Einstellungen einfügst, gilt ab der nächsten Anfrage; die Umgebung befüllt diese Speicher nur als Startwert).

Kein Pipeline-Knoten legt Tickets an, schließt sie ab oder bricht sie ab. Die Palette hat keinen solchen Schritt, und die Engine ruft TicketWorkflowService nicht auf. Ein Pipeline-Trigger kann trotzdem bei TicketAssigned, TicketCompleted, TicketFailed oder TicketCancelled auslösen (genau diese vier Ticket-Ereignisse bildet EventPayloadMapper ab). Eine Spalte pipelineRunId gibt es am Ticket nicht. Die Kopplung an die Pipeline liegt im Laufprotokoll des Agenten.