> ## Documentation Index
> Fetch the complete documentation index at: https://docs.evolutionfoundation.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Tickets

> Threads de conversa duráveis com checkout atômico e workflow definido

# Tickets

<img src="https://mintcdn.com/evoai-683d737d/KKvOt8ip-TVpeDVn/evo-nexus/imgs/doc-issues.webp?fit=max&auto=format&n=KKvOt8ip-TVpeDVn&q=85&s=47cbc67353c0543511d34e64e7ae5f95" alt="Inbox de issues — tickets com status, prioridade e responsável" width="1920" height="975" data-path="evo-nexus/imgs/doc-issues.webp" />

**Tickets são threads de conversa duráveis.** Diferente de uma sessão de chat — que vive enquanto o terminal está aberto — um ticket sobrevive a restarts, acumula contexto, pode ser pego por qualquer agente e acompanha o progresso por um workflow definido.

Use uma sessão para perguntas descartáveis. Use um ticket para qualquer coisa que você queira retomar, repassar a outro agente ou acompanhar até o fim.

## Anatomia de um Ticket

| Campo | O que é |
| - | - |
| `id` | UUID, gerado na criação |
| `title` | Resumo de uma linha |
| `description` | Contexto completo, em markdown |
| `status` | `open` / `in_progress` / `waiting` / `resolved` / `closed` |
| `priority` | `urgent` / `high` / `medium` / `low` |
| `assignee_agent` | Slug do agente (opcional — vazio significa "qualquer agente") |
| `project_id` | Link opcional para um project |
| `goal_id` | Link opcional para um goal |
| `locked_at`, `locked_by` | Estado de checkout atômico |
| `created_at`, `updated_at`, `resolved_at` | Timestamps |
| `comments` | Conversa append-only |
| `activity` | Eventos gerados pelo sistema (mudanças de status, atribuições, checkouts) |

Comentários e atividade juntos formam a **timeline** do ticket — o histórico completo de tudo que aconteceu.

## Estados do Workflow

```
open  →  in_progress  →  waiting  →  resolved  →  closed
                 ↑____________________|
```

* **open** — ninguém pegou ainda
* **in\_progress** — um agente fez checkout e está trabalhando
* **waiting** — bloqueado em input externo (resposta do cliente, decisão humana)
* **resolved** — trabalho terminado, aguardando confirmação final
* **closed** — terminal; é o que um soft-delete vira

O backend grava `resolved_at` automaticamente quando o status vira `resolved` ou `closed`.

## Criando um Ticket

### Via skill `create-ticket`

No Claude Code:

> "Abra um ticket para o Hawk sobre o webhook do Stripe retornando 500."

A skill pergunta título, descrição, prioridade, assignee/project/goal opcionais e chama `POST /api/tickets`.

### Via dashboard

A página **Tickets** tem um formulário "Novo Ticket".

### Via API

```bash theme={null}
curl -X POST http://localhost:8080/api/tickets \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Webhook do Stripe retornando 500",
    "description": "Logs em anexo. Começou após o deploy #423.",
    "priority": "urgent",
    "assignee_agent": "hawk-debugger",
    "goal_id": 12
  }'
```

## Checkout Atômico

A feature de segurança central. Quando um agente quer trabalhar em um ticket, ele chama:

```bash theme={null}
POST /api/tickets/{id}/checkout
{"agent": "hawk-debugger", "lock_timeout_seconds": 1800}
```

O backend executa:

```sql theme={null}
UPDATE tickets SET locked_at = NOW(), locked_by = :agent
WHERE id = :id AND locked_at IS NULL
```

Exatamente um agente ganha o lock. Todos os outros recebem HTTP 409 `already_locked` com o nome do detentor atual. É isso que torna tickets seguros quando múltiplos heartbeats apontam para a mesma fila — só um agente consegue trabalhar no ticket por vez.

### Release

```bash theme={null}
POST /api/tickets/{id}/release
{"agent": "hawk-debugger"}
```

Apenas o detentor do lock pode liberar. O backend verifica `locked_by == agent` antes de limpar o lock.

### Limpeza de locks parados

`ticket_janitor.py` roda periodicamente e libera qualquer lock mais antigo que seu `lock_timeout_seconds`. Isso evita que um agente que crashou segure um ticket para sempre.

## Comentários e Mentions

Comentários são a conversa viva. A descrição é estática; os comentários se acumulam.

```bash theme={null}
curl -X POST http://localhost:8080/api/tickets/{id}/comments \
  -d '{"body": "começando — @hawk-debugger consegue pegar?"}'
```

### @Mentions disparam triggers de heartbeat

Se o comentário contém `@agent-slug` e esse agente tem um **heartbeat habilitado** com `mention` em seus `wake_triggers`, o backend insere uma linha em `heartbeat_triggers`. O dispatcher pega no próximo tick e acorda o agente — com o ticket + comentário como contexto.

É assim que você passa trabalho a um agente proativo sem precisar acionar um humano antes.

### Storm guard

Máximo de 3 mentions por comentário. Os extras são silenciosamente descartados. Isso evita que alguém (humano ou agente) acidentalmente dispare 50 heartbeats numa mensagem só.

## Tickets vs Sessões

| | Sessão | Ticket |
| - | - | - |
| **Duração** | Um chat | Dias / semanas |
| **Pickup** | Apenas o usuário atual | Qualquer agente com permissão |
| **Estado** | Em memória | SQLite + audit trail |
| **Locking** | N/A | `checkout` atômico |
| **Aciona agentes** | Não | Sim (via @mention) |
| **Visibilidade** | Scrollback do terminal | Dashboard, API, exportação CSV |

Se você quer fazer uma pergunta rápida e seguir adiante — sessão. Se o resultado importa, múltiplos agentes podem mexer ou você quer um registro — ticket.

### Auto-binding de sessão

Quando um agente cria um ticket dentro de uma sessão de chat, o terminal-server detecta a resposta da API e amarra a sessão ao ticket automaticamente. O sidebar da sessão mostra um chip `#xxxxxxxx` para você ver de relance qual ticket cada conversa cobre.

<img src="https://mintcdn.com/evoai-683d737d/KKvOt8ip-TVpeDVn/evo-nexus/imgs/doc-chat-ticket-bound.webp?fit=max&auto=format&n=KKvOt8ip-TVpeDVn&q=85&s=eba5c61b8f3fcb03ae3f77ab9b72edba" alt="Chat com badge de ticket no sidebar" width="1920" height="975" data-path="evo-nexus/imgs/doc-chat-ticket-bound.webp" />

Sessões também podem ser anexadas manualmente a um ticket existente via dropdown "No ticket" acima do input de mensagem.

### Autocomplete de slash-commands

Digitar `/` no chat abre um popup com todas as skills disponíveis — filtre por substring, navegue com ↑↓, insira com Enter/Tab, feche com Esc.

<img src="https://mintcdn.com/evoai-683d737d/KKvOt8ip-TVpeDVn/evo-nexus/imgs/doc-chat-autocomplete.webp?fit=max&auto=format&n=KKvOt8ip-TVpeDVn&q=85&s=ec8190268199c320c8b861b1709ae0c4" alt="Autocomplete de slash-commands no chat" width="1920" height="975" data-path="evo-nexus/imgs/doc-chat-autocomplete.webp" />

## Operando em Tickets

<img src="https://mintcdn.com/evoai-683d737d/KKvOt8ip-TVpeDVn/evo-nexus/imgs/doc-ticket-detail.webp?fit=max&auto=format&n=KKvOt8ip-TVpeDVn&q=85&s=a585eacde3479a96f09765320c29b06b" alt="Detalhe do ticket — header, timeline, comentários" width="1920" height="975" data-path="evo-nexus/imgs/doc-ticket-detail.webp" />

### Listar / filtrar

```bash theme={null}
# Tickets urgentes abertos atribuídos a zara-cs
curl "http://localhost:8080/api/tickets?status=open&priority=urgent&assignee_agent=zara-cs"

# Todos os tickets ligados a um goal
curl "http://localhost:8080/api/tickets?goal_id=12"

# Busca full-text em título, descrição, comentários
curl "http://localhost:8080/api/tickets?q=webhook"
```

### Atualizar

```bash theme={null}
curl -X PATCH http://localhost:8080/api/tickets/{id} \
  -d '{"status": "in_progress", "assignee_agent": "hawk-debugger"}'
```

Campos permitidos: `status`, `priority`, `assignee_agent`, `title`, `description`, `project_id`, `goal_id`. Toda mudança é registrada no activity log.

### Timeline

```bash theme={null}
curl http://localhost:8080/api/tickets/{id}/timeline
```

Retorna comentários + atividade, mesclados e ordenados cronologicamente.

### Ações em lote

```bash theme={null}
curl -X POST http://localhost:8080/api/tickets/bulk \
  -d '{"ids": ["id1", "id2"], "action": "close"}'
```

Suportadas: `close`, `reassign`, `relink_goal`.

### Close vs delete

* `DELETE /api/tickets/{id}` (padrão, soft) — vira o status para `closed`, mantém o ticket e o histórico
* `DELETE /api/tickets/{id}?hard=true` (apenas admin) — remove a linha do ticket completamente

Prefira soft. Histórico importa quando você volta a um problema parecido seis meses depois.

### Exportar

```bash theme={null}
curl http://localhost:8080/api/tickets/export.csv > tickets.csv
```

Mesmos filtros do list. Até 10.000 linhas por exportação.

## Anti-padrões

* **Pular o checkout num agente automatizado.** Dois heartbeats disparam no mesmo segundo, ambos pegam o mesmo ticket, ambos rodam — o estado corrompe. Sempre faça checkout.
* **Escrever tickets só com descrição para trabalho em andamento.** A descrição é estática. Updates ao vivo vão em comentários, para que a timeline conte a história.
* **Mencionar agentes sem heartbeat habilitado.** A mention não dispara nada. Se você precisa que aquele agente pegue o trabalho, habilite o heartbeat dele primeiro.
* **Usar `?hard=true` casualmente.** Você perde o histórico. Se precisa que o ticket suma mas ainda seja revisável, faça soft-close e arquive.
* **Inflação de prioridade.** Se tudo é `urgent`, nada é. Reserve `urgent` para outages e issues P1 de cliente.

## Skills do CLI

| Skill | O que faz |
| - | - |
| `create-ticket` | Wizard interativo para abrir um ticket |

## Relacionados

* `docs/heartbeats.md` — @mentions em comentários de ticket acordam heartbeats
* `docs/goals.md` — ligar tickets a goals para acompanhamento de progresso
* Código: `dashboard/backend/routes/tickets.py`, `dashboard/backend/ticket_janitor.py`, `dashboard/backend/ticket_inbox.py`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.