# Conceitos

Esta página explica **o que cada coisa é** — o vocabulário que as outras páginas usam: agente, execução, item, evento, issue, transaction, step, heartbeat, instância, claim. As outras páginas mostram **como usar**; aqui está o **por que existe**.

Defaults numéricos vivem em [Tempos e intervalos](./api-reference.md#tempos-e-intervalos) e nas [tabelas de opções](./api-reference.md#tabelas-de-opções).

## Mapa rápido

| Conceito | Em uma frase | Quando você usa |
| --- | --- | --- |
| [Agente](#agente) | O seu robô: um processo, uma coisa que ele faz | `hobots.register` / `hobots.start` |
| [Modo sob demanda](#modo-sob-demanda-register) | Uma execução por solicitação do app | O trabalho é disparado por alguém (formulário, agendamento) |
| [Modo sempre ligado](#modo-sempre-ligado-start) | O robô abre a própria execução e roda o seu loop | O robô vive rodando e você só quer ver o que ele faz |
| [Execução (run)](#execução-run) | Uma rodada do agente, com logs, itens e resultado | A unidade de tudo em Trabalhos |
| [Item](#item) | Uma unidade de trabalho entregue | Cada nota, boleto, arquivo processado |
| [Progresso](#progresso) | Quantos de quantos | Barra e tempo restante no app |
| [Log da execução](#log-da-execução) | Narração do que a execução fez | `hobots.log` — nunca lança |
| [Anexo](#anexo) | Um print pendurado num log, item ou step | "Como estava a tela?" |
| [Cancelamento cooperativo](#cancelamento-cooperativo) | O app pede, o handler para no próximo ponto de parada | Automático em items/progress/steps/sleep/retry |
| [Retry](#retry) | Tentar de novo na mesma execução | Instabilidade passageira |
| [Evento e issue](#evento-e-issue) | A foto de um erro, agrupada em issue | `hobots.captureException` e erros não tratados |
| [Transaction](#transaction) e [Step](#step) | Quanto demorou e onde o tempo foi | `hobots.transaction.startChild` |
| [Heartbeat](#heartbeat) | Batimento "estou vivo", sempre ligado | Saber que o robô está de pé |
| [instanceId](#instanceid) | A identidade da instância do robô | Env `HOBOTS_INSTANCE_ID` |
| [Release e environment](#release-e-environment) | Qual versão, em qual ambiente | Separar produção de homologação |
| [Claim](#claim) | O ato de um agente pegar uma execução | Por que dois agentes nunca colidem |
| [Drain mode](#drain-mode) | O agente acelera o poll enquanto tem trabalho | O ritmo do polling |
| [close](#close) | Encerrar sem perder nada | A última linha do programa |

## O SDK e os módulos do app

| No SDK | No app |
| --- | --- |
| **Agente** — dono do Client Secret (`hb_…`) que autentica tudo; o que ele faz é o handler de `register()`/`start()` | **Configurações → Agentes** (o formulário do agente fica aqui) |
| **Instância** — o par `(agente, instanceId)`; heartbeat e execuções convergem nela, com o modo (sob demanda / sempre ligado) | Módulo **Instâncias** (uptime, recursos da máquina, performance) |
| **Execução** — cada solicitação atendida por `register()`, ou a execução que `start()` abre sozinho | Módulo **Trabalhos** |
| **Evento** agrupado em **issue** — `captureException` e erros não tratados | Módulo **Issues** |
| **Transaction / steps** — `hobots.transaction` | Aba de performance da execução |

---

## Agente

O **agente** é o seu robô: um programa que faz **uma coisa** — "processar NF-e", "monitorar a pasta de entrada". Na plataforma ele é identificado pelo Client Secret, não por um nome no código: o processo que sobe com aquele secret *é* o agente, e o handler que você passa a `register()`/`start()` *é* a tarefa. Essa chamada é a única forma de configurar o SDK: não há `init()`. Ela lê as credenciais do ambiente, liga o heartbeat e a captura de erros, define as opções (`environment`, `release`, `tags`, `items`…) e diz como o robô trabalha. **Uma chamada por processo** — a segunda lança. Um robô que faz duas coisas são dois agentes, cada um com o seu processo e o seu Client Secret.

## Modo sob demanda (`register`)

O robô fica **online aguardando**: faz polling na plataforma e, quando alguém cria uma solicitação para o agente (pelo formulário do agente ou por um agendamento), executa o handler com os `params` do formulário. É o modo para trabalho que **alguém pede**. Várias solicitações podem rodar em paralelo (`concurrency`). No painel a instância aparece como "sob demanda".

## Modo sempre ligado (`start`)

O robô **não espera ninguém**: ao iniciar, abre a própria execução na plataforma (`source: agent`, sem formulário nem solicitante) e roda o handler dentro dela. O handler é o seu loop — um `while (true)` que vigia uma pasta, uma fila, um portal. A plataforma recebe logs, itens e progresso enquanto ele roda. Quando o handler retorna, a execução fecha; o processo continua vivo pelo heartbeat, então a instância segue online até `hobots.close()`. É **uma execução por processo**: para recomeçar, reinicie o robô. Como o servidor guarda no máximo 1000 linhas de log por execução, o trabalho feito vai em itens, não em log. No painel a instância aparece como "sempre ligado".

## Execução (run)

Uma **execução** é uma rodada concreta do agente — no app, um trabalho. Ciclo de vida no servidor:

```
pending → claimed → running → succeeded | failed | canceled
```

No modo sob demanda a execução nasce `pending` (alguém pediu), vira `claimed` quando um agente a [reivindica](#claim) e `running` quando o handler começa. No modo sempre ligado ela já nasce `running`, criada pelo próprio robô. O agente só transiciona para `running`, `succeeded` e `failed`; `canceled` vem de alguém cancelando no app. Cada execução tem a sua transaction de performance (`run`), seus logs, itens e progresso — e o objeto `hobots` aponta para a execução **atual**, em qualquer módulo, graças ao contexto async do Node.

## Item

Um **item** é uma unidade de trabalho que a execução entregou: uma nota baixada, um boleto emitido, um arquivo movido. Tem status (`succeeded`, `failed` = falha técnica, `occurrence` = ocorrência de negócio, como "nota não encontrada") e um `payload` de atributos livres que viram colunas na aba de itens da execução — as colunas são declaradas no código, uma vez, pela opção `items` do agente. O `id` é a identidade de negócio do item na execução — repetido, o servidor atualiza em vez de duplicar: é isso que torna retries idempotentes. Diferente do log, o item é **dado estruturado**: dá para contar, filtrar e conferir.

## Progresso

O **progresso** é "quantos de quantos" (`current/total`). Declare o total antes do loop e avance a cada item; o app mostra barra, percentual e tempo restante estimado. É um snapshot absoluto que pega carona nos outros envios — não gera tráfego próprio.

## Log da execução

O **log da execução** narra o que aconteceu, em linhas com nível (`debug`, `info`, `warning`, `error`, `success`), enviadas incrementalmente e visíveis no app quase em tempo real (e ecoadas no terminal como `[hobots] INFO …`). Ele é o único membro de `hobots` que **nunca lança**, justamente para poder ir num `finally` de limpeza. O servidor guarda até 1000 linhas por execução.

## Anexo

Um **anexo** é uma imagem — na prática, um print — vinculada ao evento que ela ilustra: uma linha de log, um item ou uma step. Responde "como estava a tela?" quando texto não basta. Quem captura é o seu código (Playwright, Puppeteer, um arquivo); o SDK sobe direto para o S3 por URL presignada, em segundo plano, sem atrasar o log. Enquanto sobe, o app mostra "enviando imagem…".

## Cancelamento cooperativo

Cancelar no app **não mata** o handler — o SDK não tem como interromper código arbitrário com segurança (um navegador aberto, uma transação pela metade). O que ele faz: detecta o cancelamento (o PATCH periódico recebe 409), dispara `hobots.signal` e faz o próximo **ponto de parada** lançar `RunCanceled`. Os pontos de parada são as fronteiras naturais de trabalho: registrar um item, avançar o progresso, abrir uma step, dormir, tentar de novo. Por isso, num loop comum, o cancelamento funciona sem nenhuma linha extra. `hobots.log` nunca é ponto de parada. Se o handler ignorar o signal por tempo demais (`cancelGraceMs`), o SDK libera o slot e o handler vira um zumbi cujos reportes são descartados.

## Retry

Uma falha do handler costuma ser passageira (portal instável, rede). O SDK **tenta de novo na mesma execução**: aviso no log, espera, mesmos `params`. O que já foi enviado (itens, progresso) fica — a execução é a história completa, incluindo a tentativa que falhou. Por isso o handler precisa ser idempotente. Cancelamento e timeout são terminais: não faz sentido insistir. Para uma chamada específica, `hobots.retry()` aplica a mesma lógica no miúdo, ciente do cancelamento.

## Evento e issue

Um **evento** é a foto de um erro: tipo, mensagem, stack trace, tags, ambiente, release e como estava a máquina. `hobots.captureException(erro)` cria um; erros não tratados (`uncaughtException`, `unhandledRejection`) também. Eventos parecidos são agrupados em uma **issue** pelo servidor (por stack trace, ou pelo `fingerprint` que você definir). O `type` diz o que ele é: `fatal`, `error` e `warning` abrem issue e podem alertar; `success` é registrado e nunca alerta. Log não é evento — narração fica no log da execução.

## Transaction

Uma **transaction** é uma unidade de trabalho **medida**: começa, termina, e o que fica é quanto tempo levou. Cada execução tem exatamente uma, chamada `run` (op `hobots.run`), aberta e finalizada pelo SDK (`hobots.transaction`), correlacionada à execução pelo `run_id`. Você não cria transactions — só adiciona etapas.

## Step

Uma **step** é uma etapa medida dentro da transaction. A transaction diz que a execução levou 4 minutos; as steps dizem que 3min40 foram esperando o portal. `hobots.transaction.startChild(op, descrição)` abre uma; `step.startChild` aninha; `setData` anexa números; `finish()` fecha (steps abertas são fechadas com a transaction). Status `ok`, `warning` (passou com ressalva) ou `error`. No app isso vira o **waterfall** da execução. Abrir uma step é um ponto de parada cooperativo.

## Heartbeat

O **heartbeat** é o batimento "estou vivo", a cada 30 s. O servidor conhece a cadência e marca a instância **offline** quando ele para — a resposta para "o robô está de pé?", que erro e performance não respondem. Ele é sempre ligado e o seu timer **segura o processo vivo**: é o que mantém um robô do modo sempre ligado rodando depois que o handler retorna. `hobots.close()` o para. O primeiro batimento sai no boot; cada um leva os recursos da máquina (CPU, memória, disco).

## instanceId

A identidade da **instância** do robô: qual VM, qual container. Vem da env `HOBOTS_INSTANCE_ID` (ou da opção; sem os dois, o hostname) e deve ser **estável entre reinícios** — um valor novo a cada boot vira uma frota de robôs fantasmas. É a mesma identidade no heartbeat, no poll e na criação de execuções: um agente só no painel. A presença da instância também declara o **modo** (`on_demand` / `always_on`) e as colunas de itens do agente.

## Release e environment

`release` é a versão do programa (`robo-nfe@1.4.2`); `environment` é o ambiente lógico (`development`, `testing`, `staging`, `production`). Vão em todo evento e transaction e na identidade da instância. `release` responde "quebrou desde quando?"; `environment` separa o ruído da homologação dos alertas de produção — cada regra de alerta lista em que ambientes dispara (por padrão só produção). Valores livres antigos (`prod`, `qa`) são normalizados pelo servidor.

## Claim

O **claim** é o ato de um agente reivindicar uma execução pendente. É **server-side e atômico** (`FOR UPDATE SKIP LOCKED`): dois agentes que fazem poll no mesmo instante recebem conjuntos disjuntos — nenhum lock vive no seu processo. O agente pede só o que consegue rodar (`max` = slots livres de `concurrency`), então nunca reivindica trabalho para deixar parado.

## Drain mode

Enquanto o poll traz trabalho, o agente faz o próximo **imediatamente**; quando volta vazio, espera `pollIntervalMs`. O intervalo é o teto da espera quando não há nada para fazer, não a latência típica.

## close

O SDK não envia um HTTP por evento: tudo entra numa fila e sai em envelopes. `hobots.close(timeoutMs?)` é a última linha do programa: para o polling, espera as execuções em voo (as que não terminam a tempo viram `AgentTerminated`), para o heartbeat — liberando o processo — e faz o flush final. Sem handler próprio de sinal, o SDK faz o essencial disso sozinho num `SIGINT`/`SIGTERM`.
