Ga naar inhoud

Tickets en delegatie

Een ticket is de werkeenheid die Control Center bijhoudt. Het is met opzet een dom artefact: een titel, een tekst, een status, een eigenaar en een plek in een boom. Het is niet het ding waartegen agents uitvoeren — agentwerk leeft in gesprekken en de gestructureerde uitvoer van een run staat in het runlogboek.

Toewijzing legt eigenaarschap vast; ze dispatcht niets

Sectie getiteld “Toewijzing legt eigenaarschap vast; ze dispatcht niets”

Dit is het allerbelangrijkste om goed te hebben bij tickets.

Als je assignedAgentId instelt, wordt er een veld geschreven en wordt een TicketAssigned domeingebeurtenis gepubliceerd. Die gebeurtenis is een audit- en meldingssignaal en een pipelinetrigger. Toewijzing aan een agent start geen run. Er is geen ticketdispatcher die op assignedAgentId let en die agent wekt.

De uitzondering is een team: wanneer assignedTeamId is ingesteld, dispatcht TeamRoutingService de teamleider met het werkprotocol en de ledenlijst.

Wil je dat een specifieke agent aan een ticket werkt, dan vraag je het in een gesprek, of een pipelinestap dispatcht die agent, of een andere agent delegeert ernaar. Een ticket aan een agent toewijzen en dan wachten is het ene ding dat nooit werk oplevert.

Een ticket kan puur lokaal zijn of ondersteund worden door een externe tracker, en het draagt beide op één rij.

De spiegel — provider, externe sleutel, URL, titel, beschrijving, prioriteit, labels, ruwe en genormaliseerde status, tijdstempels — is een cache wanneer het ticket extern staat. De externe tracker is daar de bron van waarheid en een verversing herschrijft de spiegel in zijn geheel.

De overlay — toewijzing, delegatie, ruimtekoppeling, ouder, project, gekoppelde PR’s — bestaat alleen in Control Center. De tracker weet er niets van en een verversing raakt het nooit aan.

Die splitsing is wat synchroniseren überhaupt veilig maakt: een pull bij de leverancier kan verwoestend zijn voor de spiegel zonder ooit de orkestratiemetadata te verliezen die erbovenop ligt.

Statussen lopen backlog → open → inProgress → inReview → done, met blocked als zijlus en failed / cancelled als de andere eindtoestanden.

De transitiegraaf wordt alleen op het agent- en automatiseringspad afgedwongen — MCP-tools en reconcilers. Een ongeldige transitie wordt daar gelogd en genegeerd, zodat een in de war geraakte agent een ticket niet achteruit uit een eindtoestand kan laten lopen.

Elke door de gebruiker veroorzaakte wijziging in de UI geeft force: true mee en omzeilt de graaf volledig. Een mens mag een ticket naar elke status verplaatsen die de keuzelijst toont — inclusief het heropenen van een eindtoestand. De keuzelijst biedt failed niet aan; agents stellen die in via fail_ticket. De graaf is een vangrail voor automatisering, niet een regel over hoe werk mag verlopen.

TicketWorkflowService bezit elke mutatie via één centraal controlepunt dat de rij laadt, controleert dat die bij de werkruimte van de aanroeper hoort en schrijft met een verwachte version, zodat twee gelijktijdige bewerkingen elkaar niet stil kunnen overschrijven.

Zie Ticketlevenscyclus voor de volledige statustabel, transities, veldenlijst en relatiesoorten.

Elk ticket heeft een originKind. De enum heeft vijf waarden — manual, pipelineStep, agentDelegation, externalSync, recovery — maar er worden vandaag maar twee geschreven: manual (de standaardwaarde bij createTicket, inclusief gedelegeerde kinderen) en externalSync (de synchronisatie-engine, bij een pull bij de leverancier). TicketWorkflowService.createTicket neemt geen herkomstargument, dus pipelineStep, agentDelegation en recovery belanden nooit op een rij.

Delegatie is waar een agent werk voor een andere agent creëert, en het is de plek waar privileges kunnen weglekken; daarom staan de controles server-side op één centraal punt in plaats van in promptinstructies.

delegate_task loopt via TicketWorkflowService.delegateGuarded, dat de diepte en de wortel van het kind berekent uit de ouderketen en dan alle vier de controles van DelegationGuards uitvoert: een sprong die een dieptelimiet van 3 zou overschrijden, een sprong terug naar een agent die al in de keten zit (cyclusdetectie), een autonomieplafond (het effectieve niveau van de gedelegeerde in de ruimte mag niet hoger zijn dan dat van de delegator) en een budgetenvelop (delegatie schrijft af op het resterende hard-stopbudget van de delegator en kan niet meer bijmaken). De productieopzet levert beide resolver-poorten. Autonomie en budget worden overgeslagen wanneer from_agent_id ontbreekt (een onbeperkt budget of een budget zonder beleid passeert ook). Een weigering wordt gegooid als een DelegationRefusedException waarvan het bericht woordelijk bij de delegerende agent terechtkomt — een luide weigering, nooit een stille no-op.

delegate_ticket is het oudere, ongebewaakte broertje: het roept createTicket rechtstreeks aan en slaat elke controle over. Gebruik bij voorkeur delegate_task.

Verwar het dieptelimiet van delegatie (3, op ketens van delegate_task) niet met het nestinglimiet voor subagents van de ingebouwde harness (2). Het zijn verschillende limieten op verschillende mechanismen.

Ouder-kindrelaties en subissue-relaties leven op tickets.parent_ticket_id, beheerd door setParent / clearParent. Het zijn geen rijen in ticket_links, dus alles wat de relaties van een ticket leest, moet beide bronnen samenvoegen.

ticket_links bevat de gerichte randen tussen verder niet-verwante tickets:

Koppelingstype Betekenis
blocks De bron blokkeert het doel
relatesTo De twee tickets zijn verwant (symmetrisch)
duplicateOf De bron is een duplicaat van het doel

Een project is een werkruimtegebonden groepering van tickets rond een gedeeld doel. Projecten bestaan alleen in Control Center en synchroniseren nooit naar een externe provider.

Rijen van TicketCollaborator zijn hoe meer dan één principal aan een ticket wordt gekoppeld, met een rol van assignee, collaborator of reviewer. Mensen en agents zijn gelijkwaardige rijen — een principalId plus een collaboratorType — zonder sentinelwaarde voor “de gebruiker” en zonder foreign key naar een van beide tabellen.

Het ticket van Control Center is leidend. Een ticket verwijderen is alleen lokaal, dus een ticket dat door een leverancier wordt ondersteund en dat je verwijdert, wordt niet op de tracker verwijderd en kan bij de volgende synchronisatie weer opduiken. Medewerkers en kindtickets worden mee verwijderd.

De inloggegevens van adapters worden opgehaald uit de inloggegevensopslag van de server (een Linear-sleutel of GitHub-token die je in Instellingen plakt, wordt bij het volgende verzoek opgepikt; de omgeving levert alleen de beginvulling voor die opslag).

Geen enkel pipelineknooppunt maakt, voltooit of annuleert tickets — het palet heeft zo’n stap niet en de engine roept TicketWorkflowService niet aan. Een pipelinetrigger kan nog steeds afgaan op TicketAssigned, TicketCompleted, TicketFailed of TicketCancelled (die vier zijn de ticketgebeurtenissen die EventPayloadMapper daadwerkelijk mapt). Er is geen kolom pipelineRunId op het ticket; de koppeling met pipelines staat in het runlogboek van de agent.