# HageTag — Referência Técnica

> **Versão da documentação:** `2026.08` · **Última atualização:** 27/08/2026
> **Fonte canônica:** https://hagelab.com.br/solucoes/rastreabilidade

HageTag é o motor de rastreabilidade assistida da HageLab: modela operações físicas
como fluxos digitais rastreáveis. Você define etapas, dados, responsáveis, evidências
e regras; o motor orienta a execução, valida a regra no ato do registro, bloqueia o
avanço quando a condição crítica não é satisfeita e preserva a cadeia de custódia.

## Como ler este documento

Esta é a **referência normativa** do HageTag para implementação, integração e agentes
de IA. O que está aqui é o contrato da plataforma: se a documentação e o comportamento
divergirem, a divergência é defeito e deve ser reportada.

Marcações usadas ao longo do texto:

| Marcação | Significado |
|---|---|
| **NORMATIVO** | Comportamento garantido pela plataforma. É contrato. |
| **RECOMENDAÇÃO** | Boa prática de implementação. Não é obrigação. |
| **EXEMPLO** | Código ilustrativo. Identificadores e IDs são fictícios e **não devem ser reutilizados**. |
| **LIMITAÇÃO** | O que não é suportado. |

Salvo indicação em contrário, o conteúdo abaixo é **NORMATIVO**. Blocos de código
são **EXEMPLO**.

## Para agentes de IA

Esta é a referência canônica para agentes que criam, analisam ou modificam
configurações HageTag. Contratos determinísticos:

```text
ENTITY:                 HageTag
PURPOSE:                Instância rastreável de uma Categoria
IDENTIFIER:             tag.identificador
WORKFLOW_DEFINITION:    Categoria
STATE:                  tag._status
                        desvinculadas | vinculadas | andamento | atrasada
                        | concluidas | concl_atraso
STAGE_SNAPSHOT:         tag.checkpoints   etapas congeladas no vínculo
EXECUTION_RECORDS:      tag.info          registros realizados
CURRENT_STAGE:          DERIVADO — etapa da última ordem concluída.
                        NÃO existe propriedade armazenada com a etapa atual.
HISTORY:                tagFull.info      somente leitura
MUTABLE_RUNTIME:        tag, etapa, campo
READ_ONLY_CONTEXT:      tagFull, user
BLOCKING_HOOK:          etapa.blockSave
EVENT_HOOKS:            onFieldChange, onClick
```

**LIMITAÇÃO** — nunca invente identificadores de campo, etapa ou categoria. Eles são
específicos de cada instalação e precisam ser lidos da configuração real do cliente.

Prompt de sistema recomendado: [prompt.md](./prompt.md) · Índice: [llms.txt](./llms.txt)

---

## Modelo conceitual e glossário

Entender estes sete conceitos é suficiente para ler o resto do documento.

| Termo (UI) | Termo técnico | Definição |
|---|---|---|
| **Fluxo / Categoria** | `CategoriaCheckpoint` | O processo modelado. Agrupa etapas, define configurações globais (duplicidade, mapa, sequência, auto-início, publicação) e subcategorias. |
| **Etapa** | `TipoCheckpoint` | Uma fase do processo, com formulário próprio, prazo, notificações, automação e setores. Tem `ordem` (única na categoria). |
| **Tag** | `Tag` | A **ocorrência rastreável**: um lote, um transporte, um chamado, um plano de ação, uma visita. Percorre as etapas do fluxo. Tem `identificador` (chave de busca / código de barras) e, opcionalmente, `alias_identificador` (rótulo legível). |
| **Registro de etapa** | `CheckpointRecord` (`tag.info[]`) | A execução de uma etapa por um usuário: respostas (`adicionais`), arquivos, coordenada, endereço, data, usuário e leitura IoT do momento. É a unidade de evidência. |
| **Campo** | `CheckpointField` | Um item do formulário. Tem `id` imutável (chave de BI), `label`, `type` e flags. |
| **Base de Dados** | `BaseDados` / `Item` | Tabela de cadastro reutilizável (clientes, produtos, veículos, pacientes…) com layout definido pelos mesmos `CheckpointField`. |
| **Agenda** | `Agenda` / `AgendaEvent` | Calendário com regras de disponibilidade e campos próprios; um evento pode ser vinculado a uma etapa ou virar parada de rota. |

**Subcategoria** é um agrupamento de etapas dentro do fluxo (ex.: "Planejamento", "Execução",
"Qualidade"). Serve para filtrar o Kanban por frente de trabalho e para **restringir etapas por
setor** — uma etapa de uma subcategoria com setores só aparece para quem pertence a esses setores.

### Relação entre as entidades

```
Categoria (fluxo)
 ├─ config: duplicidade, mapa, sequência, auto-início, publicável, ícone/cor
 ├─ subcategorias[] ── setores[] → visibilidade por setor
 └─ Etapas[] (ordem)
 ├─ flags: início, fim, oculta, obrigatória, exige imagem, avulsa, repetível
 ├─ prazo padrão (duration, min) → prev_next_step da tag
 ├─ notificações: e-mail, push, WhatsApp, webhook
 ├─ automação: onFieldChange
 └─ Campos[] (29 tipos)
 ├─ database → Base de Dados (item)
 ├─ schedule → Agenda (evento)
 ├─ routing → Rota (origem, paradas, retorno)
 └─ table → sub-formulário repetível (colunas de qualquer tipo)

Tag (ocorrência)
 ├─ identificador / alias_identificador / descrição
 ├─ produto (equipamento IoT) · cliente · responsáveis · contatos
 ├─ prev_inicio · inicio · prev_conclusao · conclusao · prev_next_step
 ├─ checkpoints[] = snapshot das etapas no momento do vínculo
 └─ info[] = registros realizados (evidência)
```

Ponto de arquitetura importante para validação: **`tag.checkpoints` é um snapshot**. Ao alterar uma
etapa, as tags já em andamento continuam com a configuração antiga até que se execute
"Revincular tags" (oferecido automaticamente ao salvar a etapa). Isso preserva a integridade
histórica do que foi coletado e permite decidir explicitamente quando propagar a mudança.

---

## Ciclo de vida da tag

### Estados

| Estado (`_status`) | Significado |
|---|---|
| `desvinculadas` | Tag existe no pool mas não foi associada a um processo |
| `vinculadas` / `novo` | Associada a uma categoria, sem nenhuma etapa registrada ("Planejado") |
| `andamento` | Pelo menos uma etapa registrada, sem conclusão |
| `atrasada` | Em andamento com algum prazo estourado (`_algum_atraso`) |
| `concluidas` | Etapa marcada como `fim` registrada (ou encerramento forçado) |
| `concl_atraso` | Concluída após o prazo |

A **coluna atual no Kanban** é a etapa da última `ordem` concluída (`getEtapaAtual`), ignorando
etapas avulsas. Tags sem nenhum registro caem na coluna virtual "Planejado".

### Datas e SLA

| Campo | Papel |
|---|---|
| `prev_inicio` | Previsão de início do processo |
| `inicio` | Início real |
| `prev_conclusao` | Previsão de conclusão do processo (SLA macro) |
| `conclusao` | Conclusão real |
| `prev_next_step` | **Prazo da próxima etapa** (SLA micro) |

`prev_next_step` é a peça central do controle operacional. Ele é resolvido em cascata:

1. Valor definido por automação (`tag.prev_next_step` no script) — precedência máxima;
2. Caso contrário, `data do registro + config.duration` da etapa (em minutos);
3. Caso contrário, ausente — sem prazo (o campo é omitido do payload e a API decide).

O Kanban desenha o SLA **vivo** a partir disso, com texto padronizado por estado
("faltam 2h 15min", "atrasado 35min", "adiantou 12min", "no prazo", "atrasou 18min") e cor por
proximidade do vencimento. A contagem usa um relógio compartilhado com contagem de referência
(`ClockService`): colunas que só têm tags concluídas não assinam o relógio (SLA congelado).

### Identificador e apelido

O `identificador` é a **chave funcional**: é o que a busca e o leitor de código de barras usam.
Quando ele precisa ser um código pouco legível (ex.: chave de acesso de NF-e com 44 dígitos), a
automação pode gravar `alias_identificador` — um rótulo amigável ("NF 32298 · AC Labor") exibido
como título no app, sem substituir o identificador. Definir alias **só é possível por automação**.

Geradores de identificador disponíveis compartilhados por
Kanban, geração em lote, QR de início e tela de vínculo):

| Formato | Exemplo |
|---|---|
| Sem data | `COLETA` |
| Dia/mês | `COLETA 21/08` |
| Dia/mês/ano | `COLETA 21/08/2026` |
| Hora | `COLETA 21/08 - 14h` |
| Período | `COLETA 21/08 - tarde` |

Compõem-se como `prefixo + data + sufixo`, onde o sufixo costuma ser a descrição da categoria ou o
identificador do equipamento. Isso permite padronizar nomenclatura sem intervenção do operador — um
requisito recorrente de qualidade ("identificação unívoca e legível do registro").

---

### Configuração da etapa

Tela `gestao/etapa`, organizada em três abas: **Geral**, **Campos**, **Notificações**.

**Flags de comportamento**

| Flag | Efeito |
|---|---|
| `inicio` | Etapa de abertura do processo (mutuamente exclusiva com `fim` — a UI desabilita a outra) |
| `fim` | Etapa de fechamento: registrar conclui a tag |
| `hidden` | Etapa oculta em Kanban e listagens operacionais (só alcançável por automação/fluxo específico) |
| `stage_required` | Etapa obrigatória: com "restringir sequência" ativo, bloqueia as posteriores |
| `image_required` | Exige ao menos uma imagem para concluir o registro (validado no submit) |
| `allow_repeat` | Permite registrar a mesma etapa várias vezes (padrão: sim) |
| `skip_last_stage_update` | **Etapa avulsa**: fora do fluxo sequencial, registrável a qualquer momento; não vira coluna do Kanban nem avança a etapa atual — aparece na seção "Atividades" |
| `is_routing_completion` | Etapa de conclusão de parada de rota: só aparece no fluxo de roteirização |
| `public_visible` | Disponível para solicitação online — esta etapa pode ser preenchida por quem não tem login, via widget embutido no site do cliente ([Widget público de solicitações (embed no site do cliente)](#widget-publico-de-solicitacoes-embed-no-site-do-cliente)). Exige também a categoria marcada como publicável |
| `duration` (min) | Prazo padrão até a próxima etapa |
| `subcategoria` | Agrupamento/setor |
| `setores[]` | Restrição direta de setor |

O conceito de **etapa avulsa** é subestimado e vale destaque comercial: ele permite modelar
atividades que acontecem *em paralelo* ao fluxo principal e quantas vezes for necessário — uma
ocorrência, uma medição periódica, um registro de temperatura horária, um contato com o cliente —
sem sujar o Kanban nem alterar o estágio do processo, mas mantendo tudo na mesma trilha auditável da
tag.

**Notificações por etapa** (aba Notificações)

| Canal | Configuração | Observação |
|---|---|---|
| **E-mail** | lista nome → e-mail | Também controlável dinamicamente por automação (`etapa.emails`) — sem custo |
| **Push** | lista nome → destino, com `msg_title`/`msg_body` | Suporta variáveis |
| **WhatsApp** | lista nome → número | **Canal com custo por mensagem** — configurado só na tela, nunca por script; a documentação de IA obriga avisar sobre o custo e pedir confirmação |
| **Webhook** | host + headers customizados (chave/valor) | Integração com ERP/CRM/RPA ao concluir a etapa |

As mensagens aceitam **variáveis**: `{{identificador}}`, `{{etapa}}`, `{{cliente}}`, `{{produto}}`,
`{{usuario}}`, `{{atualizacao}}` e, dinamicamente, `{{<label de qualquer campo da etapa>}}` — a tela
lista as variáveis disponíveis e monta *placeholders* de exemplo com os campos reais da etapa.

**Notificação de etapa × alerta de IoT — são coisas distintas**

| — | Notificação de etapa | Alerta de IoT |
|---|---|---|
| Dispara quando | uma etapa é registrada | uma leitura viola a regra do equipamento |
| Depende de sensor | **não** | sim |
| Duração | pontual, no ato | contínua, enquanto o equipamento está em operação |
| Destinatário | definido por etapa, e ajustável por automação | grupos de alerta do monitoramento |
| Conteúdo | os campos que você escolher expor | a leitura que violou e o contexto do equipamento |

A notificação de etapa funciona **sem nenhum IoT**. É o canal do processo: avisa o cliente que a
coleta foi feita, o gestor que houve recusa de recebimento, o financeiro que a entrega foi concluída.

**Condicional**: como `etapa.emails` é mutável por automação, o disparo pode depender da resposta —
`etapa.emails = { Gestor: '...' }` notifica; `null` não notifica. É assim que se avisa só quando
houve exceção (recusa, avaria, divergência), em vez de a cada etapa concluída.

**Conteúdo selecionável**: as variáveis da mensagem determinam o que sai. Campo que não entra no
texto não é enviado — dado sensível pode ficar de fora da notificação e permanecer só no registro.

**Com IoT vinculado**, o relatório anexado ao encerramento traz as leituras contínuas do período.
Quando o processo passa por **mais de um equipamento ao longo do tempo** — um sensor na câmara
durante o armazenamento, outro no baú durante o transporte — as séries são mescladas na mesma linha
do tempo, com cor e legenda por equipamento e as faixas de limite de cada um. O gráfico e o
relatório são do **processo**, não do sensor.

**Validações de consistência no salvamento** (impedem publicar etapa inconsistente):

- campo Agenda sem agenda vinculada;
- Botão de ação sem script;
- campo com máscara sem tipo de máscara;
- campo Rota incompleto (sem origem válida, sem campo de endereço do destino, ou retorno
 "personalizado" sem ponto/base resolvida);
- script de automação com erro de sintaxe ou referência inexistente (validação obrigatória antes de
 salvar).

---

### Catálogo de tipos de campo

29 tipos publicados no editor, organizados em quatro grupos. A coluna "valor salvo" é essencial para
automação, BI, exportação e integração.

#### Entrada de dados

| Tipo | Rótulo na UI | Valor salvo | Notas de uso |
|---|---|---|---|
| `text` | Texto | string | — |
| `number` | Número | string numérica (converter com `Number`) | Vira medida no BI (soma/média/mediana) |
| `date` | Data | `AAAA-MM-DD` | — |
| `datetime-local` | Data/Hora | ISO 8601 | — |
| `email` | E-mail | string | — |
| `tel` | Telefone | string | — |
| `textarea` | Texto longo / Voz | string | **Ditado por voz embutido** (botão de microfone) — reduz digitação em campo. É também o tipo correto para automação gravar um texto calculado que deve **persistir** (`readonly: true`) |
| `note` | Texto e Avisos | string (texto ou HTML) | [Campo "Texto e Avisos" (note) e condições declarativas](#campo-note) — instrução ao operador, não é salvo por padrão |
| `masked` | Campo com máscara | string formatada (exceto moeda) | Máscaras: CPF, CNPJ, CPF/CNPJ, telefone, moeda e **padrão personalizado** com validador JS próprio |

**Máscara personalizada**: padrão com `9` = dígito, `A` = maiúscula, `a` = minúscula, `*` = qualquer
(ex.: `99999-999` para CEP, `AAA-9999` para placa, `99/99/9999`), mais um validador opcional em JS
que recebe o valor e devolve `null` (válido) ou a mensagem de erro. Cobre validação de documentos e
códigos internos sem desenvolvimento.

**Moeda é exceção deliberada**: exibe "R$ 1.234,56" e **grava número** (1234.56), para permitir
cálculo em automação, BI e backend.

#### Seleção

| Tipo | Rótulo | Valor salvo | Notas |
|---|---|---|---|
| `select` | Seletor | string | Opções podem ser reescritas por automação em runtime |
| `radio` | Escolha única | string | — |
| `checkbox` | Múltipla escolha | string[] | — |
| `switch` | Sim/Não | boolean | Vira Sim/Não no BI |
| `list` | Lista | string[] | Itens livres, **com leitura por QR/código de barras**; rejeita duplicados e sanitiza invisíveis |

#### Mídia e captura

| Tipo | Rótulo | Valor salvo | Notas |
|---|---|---|---|
| `photo` | Foto | base64 | Câmera **ou** galeria |
| `camera` | Câmera | base64 | Não é um tipo próprio: é `photo` + `cameraOnly` — **força captura ao vivo**, sem galeria. Diferença crítica para evidência (impede subir foto antiga) |
| `signature` | Assinatura | base64 | Canvas de assinatura, nunca câmera — aceite de entrega/serviço |
| `barcode` | QR/Barcode | string | Leitura nativa |
| `qrcode-ocr` | QR + OCR | string | Leitura por QR **ou** reconhecimento de texto |
| `ocr` | OCR | string | Reconhecimento de texto em imagem (número de série, laudo, etiqueta) |
| `file` | Arquivos | `[{file, name}]` | Anexo documental (PDF etc.), com visualizador de PDF no relatório |

#### Especiais (estruturados)

| Tipo | Rótulo | Valor salvo | O que habilita |
|---|---|---|---|
| `table` | Tabela | array de linhas (cada linha = conjunto de campos completos) | **Sub-formulário repetível dentro da etapa**, editado como planilha (edição em célula). Cada coluna é um campo de qualquer tipo, inclusive outra tabela. Tem automação própria por linha |
| `address` | Endereço | objeto (`formatted_address`, logradouro, bairro, cidade, estado, cep, lat, lng) | Endereço com autocomplete e geocodificação; base do cálculo de rota |
| `schedule` | Agenda | id do evento | Vincula a etapa a um evento de agenda (data/hora, participantes, local, lembretes) |
| `database` | Base de Dados | objeto do item (mapa label→valor + `_id`/`_label`) | Seleção de item de cadastro reutilizável; funciona **offline** |
| `routing` | Rota | objeto (`origin`, `items[]`, `ending`, previsões) | Roteiro de paradas com otimização — |
| `nfe` | Nota Fiscal | objeto (número, chave de acesso, emitente, destinatário, produtos, valor total) | Leitura de NF-e por chave/QR; **restrito por permissão de renderização** |
| `malote` | Malote | `{ id, data[] }` | **Embalagem secundária**: agrupa embalagens primárias lidas por código; recebe id próprio gerado localmente (alfabeto Code 128 sem caracteres ambíguos, ex.: `M-LX9K2A-4F7`) e oferece **impressão do código do malote** ao salvar |
| `action-button` | Botão de ação | string (o que o script gravar) | Botão que executa script **somente ao clique**: consultar API, preencher campos sob demanda, disparar fluxo pontual. Suporta rótulo próprio e confirmação prévia |

Tipos legados aceitos em cadastros (não criáveis no editor): `datetime`, `currency`, `url`.

#### Flags disponíveis em qualquer campo

| Flag | Efeito |
|---|---|
| `required` | Preenchimento obrigatório |
| `important` | **Painel**: o valor aparece no painel de monitoramento e no card |
| `hidden` | Oculto do operador — canal padrão para valores preenchidos por automação |
| `share_info` | Inclui o campo em relatórios e compartilhamentos externos |
| `readonly` | Operador vê mas não edita (usado por automação para exibir resultado calculado) |
| `unique_entry` | Só em Base de Dados: impede valor repetido entre itens |
| `cameraOnly` | Só em `photo`: força câmera |

**Coletado automaticamente em todo registro, sem configurar campo**: usuário que preencheu,
data/hora, coordenadas, endereço geocodificado a partir das coordenadas, leitura IoT do equipamento
vinculado no instante, e um campo "Observações" padrão ao final do formulário. A documentação de IA
proíbe explicitamente gerar campos duplicando esses dados.

---

### Automação: `onFieldChange` e `onClick`

O diferencial mais forte do módulo em termos de flexibilidade. Cada etapa aceita um script JS (corpo
de função) que roda:

- **ao abrir o formulário**, uma vez com `campo` vazio (inicializações) e uma vez por campo já
 preenchido (reaplicação de regras);
- **a cada alteração** de campo, com `campo` = o campo alterado.

Campos `action-button` têm um script separado (`onClick`) que roda **somente ao clique**.


**Capacidades que isso abre** (todas com exemplo pronto no editor):

1. **Obrigatoriedade e visibilidade condicionais** — exigir laudo só quando o resultado é "Reprovado".
2. **Cálculo de campos** — total = quantidade × preço, recalculado a cada alteração.
3. **Cálculo de prazos** — definir `prev_conclusao` ou `prev_next_step` conforme a resposta
 (ex.: prioridade "Urgente" → 2h; demais → 24h).
4. **Destaque no painel** — marcar `campo.important` dinamicamente quando o valor sai da faixa
 (ex.: temperatura fora de 2–8 °C) e alterar a descrição da tag para "ALERTA: 9,4 °C".
5. **Avisos visuais ao operador** — `chip`/`banner` em campo `note`, sem HTML manual.
6. **Reaproveitamento de etapas anteriores** — usar `tagFull.info` para montar a lista do que foi
 coletado numa etapa anterior e calcular o que **falta** (padrão clássico de conferência de
 recebimento: malotes coletados − malotes recebidos = faltantes).
7. **Consulta a API externa** — `fetch` (com e sem autenticação; o token do usuário está em
 `user.token`), com indicador de carregamento automático em scripts `async`.
8. **Integração com o próprio ecossistema IoT** — colocar o equipamento vinculado **em operação**
 (passa a notificar grupos de alerta sobre anormalidade) ao iniciar o transporte, e **fora de
 operação** ao coletar a assinatura do recebedor (evita alarme falso no retorno do veículo).
9. **E-mail condicional** — `etapa.emails = { Gestor: '...' }` dispara; `null` não dispara.
10. **Bloqueio de salvamento** — `etapa.blockSave = 'motivo'` impede o save e mostra o motivo ao
 operador; `null` libera. É diferente de `required`: cobre respostas **preenchidas porém
 incompatíveis** (data de coleta posterior à de entrega, valor fora de faixa, campos mutuamente
 obrigatórios). Requisito direto de "verificação de consistência de dados" em validação.
11. **Título legível** — gravar `alias_identificador` a partir da NF-e lida.
12. **Criação de campo em runtime** — o script pode inserir um campo somente leitura na etapa (ex.:
 uma linha única de histórico de integração que é sobrescrita a cada execução).

**Qualidade e segurança do script**

- **Validação obrigatória antes de salvar**: `validateAutomationScript` compila o script (com
 suporte a `async`) e faz *dry-run* com stubs universais, testando um cenário por label
 referenciado, para capturar erro de sintaxe e `ReferenceError`. A etapa não salva com script
 inválido.
- **Indicador de impacto**: `extractAutomationLabels` extrai por regex os labels tocados pelo
 script — como gatilho **ou** como alvo — e o editor de estrutura marca quais campos a automação
 manipula. É *best-effort* (não AST): referência por variável ou template string não é detectada.
- **Erro em runtime não bloqueia o operador**: exceção no script vira aviso, nunca impede o save
 (exceto o `blockSave` explícito).
- **Regras de estilo obrigatórias na documentação**: proteger cada regra pelo campo que a dispara
 (`if (campo.label === '...')`), comentar em português, organizar em blocos independentes para
 extensão futura. Isso existe porque o script inteiro roda a cada alteração de qualquer campo.
- **Cache de `tagFull` por tag** durante a sessão do formulário — uma única chamada de API,
 compartilhada entre o script da etapa e os scripts de linha de tabela.

**Assistência de IA embutida**: o editor de automação gera um *prompt* completo
(`buildAutomationDocs`) com o contexto do sistema, a estrutura real dos campos da etapa, o catálogo
de tipos, o formato de `value` por tipo, as variáveis, as agendas e bases de dados **reais** do
ambiente (com IDs), as regras de notificação e ~17 instruções de conduta. As instruções obrigam o
assistente a: fazer levantamento antes de codificar (com escolha prévia do nível de detalhamento —
simplificado/médio/detalhado), avaliar se a estrutura de campos está adequada antes de escrever
script, propor proativamente Base de Dados e Agenda quando detectar dados repetidos ou datas
futuras, validar o desenho com o usuário antes de entregar código, e nunca inventar e-mails ou IDs.

#### Contexto disponível no script

```text
VARIÁVEIS DISPONÍVEIS NO SCRIPT:

O script é o corpo de uma função executada nestes momentos:
 • Ao ABRIR o formulário: primeiro uma vez com a variável "campo" VAZIA (campo.label fica '' /
 indefinido) — use para inicializações (ex.: preencher opções a partir de etapas anteriores);
 depois uma vez para CADA campo do formulário, preenchido ou não (campo = aquele campo),
 reaplicando regras de obrigatório/oculto. Regras que dependem de um campo-gatilho ainda vazio
 também rodam — por isso proteja efeitos colaterais testando campo.value.
 • A cada ALTERAÇÃO de campo: com "campo" sendo o campo alterado.
Use campo.label para decidir o que executar. Para rodar algo SOMENTE na abertura, teste !campo.label.
Suporta async/await (um indicador de carregamento é exibido automaticamente).
Alterar campo.value dentro do script NÃO redispara o hook — não há recursão.

etapa (Object) — a etapa atual (leitura e escrita):.campos Array — todos os campos do formulário; cada item é mutável:.label string — nome do campo (use para localizar via find).value any — valor atual; altere para preencher automaticamente.type string — tipo do campo (ver lista acima).required boolean — true = obrigatório.hidden boolean — true = oculto.important boolean — true = destaque no dashboard.share_info boolean — true = exibido na linha do tempo.readonly boolean — true = somente leitura (exibe o valor mas impede edição).
 Para campos 'note': readonly:true (padrão) = não salva; readonly:false = salva..options any[] — opções disponíveis (select / radio / checkbox).identificador string — nome/descrição desta etapa.emails object — dispara e-mail SOMENTE nesta etapa: { NomeDestinatario: 'email@dominio.com',... }.blockSave string|boolean — IMPEDE o salvamento desta etapa (validação de respostas incompatíveis).
 Atribua uma STRING com o motivo para bloquear (o motivo é exibido ao operador ao tentar
 salvar); true bloqueia com mensagem genérica; null / false / '' liberam o salvamento.
 Combine com um campo do tipo 'note' para mostrar a explicação fixa dentro do formulário.
 REAVALIE SEMPRE: defina o motivo quando estiver incompatível e null quando resolvido,
 protegendo a regra pelo(s) campo(s) que a disparam (e !campo.label para checar na
 abertura). O bloqueio persiste até o script defini-lo como null — não é limpo sozinho.

campo (Object) — o campo que acabou de ser alterado (mesmas propriedades de etapa.campos[n])
 campo.label → qual campo disparou a execução (fica '' / indefinido na abertura do formulário)
 campo.value → novo valor
 Para executar lógica na abertura do formulário, teste !campo.label
 (ex.: if (!campo.label | — | campo.label === 'Recebimento malotes') {... }).

chip(texto, variante) (Function) — monta o HTML de um chip colorido (pill curta) para preencher
 um campo 'note'. variante: 'primary' (informativo) | 'success' | 'warning' | 'danger'. Não use
 hex/style à mão — o chip já usa as cores do tema (claro/escuro).
 Ex.: status.value = chip('✓ Dentro da faixa', 'success');

banner(variante, titulo, corpo) (Function) — monta o HTML de um aviso em destaque (título +
 descrição opcional) para preencher um campo 'note'. Mesmas variantes de chip; corpo é opcional
 (omita ou passe '' para um banner só com título). É o MESMO formato visual que o builder "Aviso"
 do editor de estrutura gera — automação e configuração manual ficam consistentes.
 Ex.: aviso.value = banner('warning', 'Ainda faltam malotes', '2 malote(s) pendente(s).');

tag (Object) — dados do registro atual em edição (leitura e escrita):.descricao string — descrição livre.identificador string — código identificador da tag. É a CHAVE de busca/leitura (código de
 barras / QR) e continua sendo o valor funcional — não o troque por um
 texto "bonito". Ex.: uma chave de acesso de NF-e (44 dígitos)..alias_identificador string — APELIDO legível do identificador, exibido como TÍTULO no app quando
 presente (o app renderiza alias_identificador | — | identificador). Use
 quando o identificador for um código difícil de ler (ex.: chave de NF-e)
 e você quiser mostrar algo amigável ("NF 32298 - AC Labor"). Defina SOMENTE
 por automação; atribua string vazia/null para limpar. NÃO substitui o
 identificador (que segue como código de barras/busca) — é só o rótulo visual.
 Ex.: tag.alias_identificador = 'NF ' + numero + ' - ' + cliente;.tipo_checkpoint number — ID da etapa (checkpoint) que está sendo registrada AGORA.
 Já vem preenchido com o ID da etapa atual; use para identificar a
 etapa dentro do script (ex.: comparar com o tipo_checkpoint de
 registros anteriores em tagFull.info). Somente leitura na prática —
 o sistema regrava com o ID correto ao salvar..produto object | null — equipamento/produto vinculado à tag (somente leitura). null
 quando a tag não tem equipamento vinculado. Campos úteis:.identificador string — nome/identificação do equipamento.imei string — IMEI do rastreador/sensor.num_serie string — número de série.modelo string — modelo do equipamento.temp_max number — temperatura máxima configurada.temp_min number — temperatura mínima configurada.controles object — controles do equipamento (relé, umidade, …).sck object — última leitura recebida ({ temperatura,
 lat, lng, voltagem, velocidade, local,
 eventtime, … }); use p/ ler a temperatura
 ou posição atual do equipamento.
 Ex.: if (Number(tag.produto?.sck?.temperatura) > tag.produto?.temp_max) {... }.date string — data/hora do registro (ISO 8601).inicio string — data de início (ISO 8601).conclusao string — data de conclusão (ISO 8601).prev_inicio string — previsão de início (ISO 8601).prev_conclusao string — previsão de conclusão (ISO 8601).prev_next_step string — limite (prazo) para a PRÓXIMA etapa (ISO 8601). Defina para
 sobrescrever o prazo (use sempre new Date para "agora", não tag.date).
 Defina null/'' para NÃO ter prazo. Se você não tocar neste campo, o sistema
 calcula automaticamente a partir do prazo padrão da etapa (em minutos):
 prev_next_step = data do registro + prazo.

user (Object) — dados do usuário logado (somente leitura):.sub number — ID do usuário.nome string — nome completo.nome_social string — nome social / apelido.empresa object — dados da empresa vinculada.estabelecimento object — estabelecimento ativo

tagFull (Object | null) — tag completa salva no dispositivo (somente leitura); pode ser null se a tag ainda não foi salva localmente:._id string — ID único da tag.descricao string — descrição.identificador string — código identificador.produto object — equipamento/produto vinculado (com.imei,.identificador,.controles, etc.).produto_id number — ID do equipamento vinculado.estabelecimento object — estabelecimento vinculado.cliente_id number — ID do cliente/estabelecimento.categoria object — categoria da tag.info array — histórico completo de registros de etapas; cada item:.tipo_checkpoint number — ID da etapa registrada.checkpoint object — dados do tipo_checkpoint (campos configurados com label, type, value, etc.).adicionais object — mapa label→valor dos campos preenchidos naquele registro.date string — data/hora do registro.usuario object — usuário que registrou.ultima_etapa object — último registro de etapa (mesmo formato de.info[n]).inicio string — data de início (ISO 8601).conclusao string — data de conclusão (ISO 8601).prev_inicio string — previsão de início (ISO 8601).prev_conclusao string — previsão de conclusão (ISO 8601).prev_next_step string — limite para a próxima etapa (ISO 8601)
 Uso típico: ler dados de etapas anteriores para pré-preencher campos da etapa atual.
 Exemplo simples: const lote = tagFull?.ultima_etapa?.adicionais?.['Número do Lote'];

routingItem (Object | undefined) — a PARADA DE ROTA vinculada a este registro, quando esta etapa foi
 aberta a partir de uma parada de um campo tipo "routing" (roteiro). Fica undefined fora desse
 fluxo (etapa aberta normalmente, sem rota) — sempre trate como opcional (routingItem?.algo).
 Identifica com precisão a parada ATUAL mesmo quando a etapa se repete (uma vez por parada): não
 infira a parada por contagem de registros anteriores nem por status desatualizado em tagFull —
 use routingItem diretamente..id string — id estável da parada (não muda com reordenação/otimização de rota).state string — 'pending' | 'going' | 'completed' | 'skipped' | 'cancelled'.sequence number — ordem da parada na rota.endereco string — endereço da parada.lat /.lng number — coordenadas da parada.adicionais object — mapa label→valor dos dados do evento/agendamento desta parada
 (ex.: routingItem.adicionais['Guia']).agendaEventId string — id do evento de agenda que originou a parada (quando houver).agendaId string — id da agenda de origem (quando houver).agendaNome string — nome da agenda de origem (quando houver).agendaEventDescription string — descrição do evento de agenda de origem (quando houver).scheduledStart/.scheduledEnd string — início/fim previsto do evento de agenda (ISO 8601)
 Exemplo: const paciente = routingItem?.adicionais?.Paciente?.['Nome completo'] ?? '—';

infoIndex (number | undefined) — índice, dentro de tagFull.info, do registro da etapa de rota
 (campo tipo "routing") que contém routingItem. Só é útil em conjunto com routingItem; não altera
 o item nem o índice diretamente — use apenas para referência/leitura, se precisar cruzar com
 tagFull.info[infoIndex].
```

#### Interfaces dos objetos

```text
CONTRATO DOS OBJETOS DO RUNTIME (TypeScript, para referência — o script em si é JavaScript):

type FieldType =
 | 'text' | 'number' | 'date' | 'datetime-local' | 'email' | 'tel'
 | 'textarea' | 'note' | 'masked'
 | 'select' | 'radio' | 'checkbox' | 'switch' | 'list'
 | 'photo' | 'signature' | 'file' | 'ocr' | 'barcode' | 'qrcode'
 | 'malote' | 'nfe' | 'schedule' | 'database' | 'routing' | 'table'
 | 'address' | 'action-button';

interface CampoRuntime {
 label: string; // nome do campo; chave de busca via etapa.campos.find
 value: unknown; // tipo depende de FieldType — 'number' chega como string
 type: FieldType;
 required: boolean;
 hidden: boolean;
 important: boolean; // destaque no painel e no card
 share_info: boolean; // aparece em relatório e compartilhamento externo
 readonly: boolean;
 options?: string[]; // apenas em select/radio/checkbox/list
}

interface EtapaRuntime {
 campos: CampoRuntime[];
 emails: Record<string, string> | null;
 blockSave: string | true | null; // string = motivo exibido; true = motivo genérico
}

interface TagRuntime {
 identificador: string;
 alias_identificador: string | null; // só definível por automação
 descricao: string;
 prev_inicio: string | null; // ISO 8601
 inicio: string | null;
 prev_conclusao: string | null; // SLA macro
 conclusao: string | null;
 prev_next_step: string | null; // SLA micro — prazo da próxima etapa
 produto?: {
 sck: unknown; // última leitura do sensor vinculado
 temp_min: number | null;
 temp_max: number | null;
 };
}

interface TagFullRuntime { // SOMENTE LEITURA
 info: Array<{
 checkpoint: string;
 adicionais: Array<{ label: string; value: unknown }>;
 }>;
}
// ATENÇÃO: tagFull é null em tag nova. Sempre verifique antes de acessar.info

interface UserRuntime { // SOMENTE LEITURA
 sub: string;
 token: string;
}
```

#### Mutabilidade e persistência

```text
MUTABILIDADE E PERSISTÊNCIA (o que sobrevive ao salvamento):

 Objeto Propriedade R/W Persistido? Observação
 --------------------------------------------------------------------------------------------
 campo value RW sim vira resposta da etapa
 campo required RW não (comportamento) validado no envio
 campo hidden RW não (comportamento) campo oculto não é exigido
 campo readonly RW não (comportamento) em 'note', false faz o valor persistir
 campo important RW sim (marcação) destaque no painel e no card
 campo share_info RW sim (marcação) entra em relatório externo
 campo options RW não (comportamento) só select/radio/checkbox/list
 campo label / type R — não altere: quebra a busca e o BI
 etapa campos RW via os campos mutação é sempre no item
 etapa emails RW sim destinatários da notificação
 etapa blockSave RW não (runtime) lido no envio; o script define E limpa
 tag prev_next_step RW sim precedência máxima sobre o padrão
 tag prev_conclusao RW sim SLA macro do processo
 tag descricao RW sim aparece no card e na busca
 tag alias_identificador RW sim nunca substitui o identificador
 tagFull info R — histórico; não modificar
 user sub / token R — use token em fetch autenticado

 Mutação NÃO é gravada no momento da execução: a persistência acontece no envio da etapa.
 Alterar campo.value dentro do script NÃO redispara o hook — não há recursão.
```

---

### Semântica de execução do runtime

Este é o contrato de execução para quem — pessoa ou agente — escreve automação sem poder testar
no ambiente real.

**Quando o script roda**

| Momento | `campo` recebido |
|---|---|
| Abertura do formulário | Uma vez com `{ label: '', value: '' }` — teste `!campo.label` para inicialização |
| Ainda na abertura | Uma vez para **cada campo do formulário**, preenchido ou não, na ordem da lista |
| Alteração | O campo alterado |
| Clique em `action-button` | O próprio campo, via `onClick` (script separado; `requireConfirm` pede confirmação antes) |

⚠️ O replay de abertura percorre **todos** os campos, não só os preenchidos — regras que inicializam
visibilidade ou obrigatoriedade de campo dependente precisam rodar com o gatilho ainda vazio. Proteja
efeito colateral testando `campo.value`.

**Reentrância** — alterar `campo.value` dentro do script **não redispara** o hook. Não há recursão nem
cascata: o script roda uma vez por interação e a view é reconciliada no fim.

**Persistência** — mutações do script vivem no modelo em memória. Nada é gravado no momento da
execução: a persistência acontece no envio da etapa, que monta `adicionais` a partir dos campos.
Campo `note` com `readonly` diferente de `false` **não é persistido** — é instrução ao operador.

**Erro, e por que `blockSave` é flag e não exceção**

Toda exceção do script é capturada pelo engine, registrada no console e exibida como aviso
(`Automação: <mensagem>`). **Ela não impede o salvamento.** `throw` dentro do script é engolido —
é exatamente por isso que o bloqueio é uma flag lida no envio, e não uma exceção.

O script é dono do ciclo da flag: precisa **definir e limpar** `etapa.blockSave`. Não há rollback das
mutações já feitas quando o bloqueio dispara; o operador corrige e o hook roda de novo.

| Valor de `etapa.blockSave` | Efeito no envio |
|---|---|
| string não vazia | Bloqueia e exibe a string como motivo |
| `true` | Bloqueia com motivo genérico |
| `null`, `false`, `''` | Libera |

**Ambiente de execução** — o script é o corpo de uma função criada com `new Function` e executada
no realm do navegador. **Não é sandbox**: os globais do navegador estão disponíveis (`fetch`, `console`,
`Date`, `JSON`, `localStorage`). Não há timeout — script que trava, trava o formulário.

O modo assíncrono é detectado por **presença textual da palavra `await`** no código-fonte do script
(`/\bawait\b/`). Em script assíncrono o app exibe "Processando automação..." automaticamente,
exceto durante o replay de abertura.

**`fetch` e operação offline** — `fetch` para API externa falha offline, a exceção é capturada, o aviso
aparece e **o salvamento continua permitido**. Automação que consulta serviço externo não deve ser a
única barreira de uma regra crítica: em campo, sem sinal, ela não roda.

**`tagFull`** — carregado sob demanda e **cacheado por tag durante a sessão do formulário**: não
reflete alterações feitas no formulário aberto. Em tag nova (`tagId === 'novo'`) vem **`null`** —
sempre proteja com verificação antes de acessar `tagFull.info`.

---

### Campo "Texto e Avisos" (`note`) e condições declarativas

O tipo `note` merece seção própria porque cobre três necessidades distintas:

1. **Instrução ao operador** (padrão, `readonly: true`): texto ou HTML exibido durante o
 preenchimento, **não persistido**. Serve para orientação de POP dentro do próprio formulário e
 pode ser recalculado por automação a cada abertura.
2. **Resultado calculado persistido** (`readonly: false`, definível só por automação): o valor É
 salvo e aparece no detalhe, no relatório e na impressão, mantendo o visual de nota.
3. **Aviso condicional sem programar** (modo Condição): regras "se / senão se" declarativas,
 avaliadas em runtime contra o valor vivo de outros campos da mesma etapa, com operadores
 filtrados pelo tipo do campo-gatilho. Permite que um analista de qualidade configure
 "se Temperatura > 8 então mostrar aviso vermelho 'Fora de faixa — abrir desvio'" sem escrever
 uma linha de código.

O builder visual oferece dois modos de composição (Texto e Aviso) com quatro variantes de cor
(informativo, sucesso, atenção, crítico) — o mesmo formato que os helpers `chip`/`banner` geram na
automação, garantindo consistência visual entre o que é configurado e o que é calculado.

---

### Cinco caminhos para criar um fluxo

O `ImportFlowModalComponent` unifica cinco modos de criação, o que reduz drasticamente o atrito de
implantação:

| Modo | Para quem | Como funciona |
|---|---|---|
| **Formulário** | Usuário comum | Formulário guiado passo a passo |
| **Templates** | Implantação rápida | 15 fluxos prontos (galeria com descrição e destaque) |
| **Código** | Consultor / TI | Editor JSON **ou** YAML com validação prévia, pré-visualização, download do template e "Copiar para IA" |
| **Etapas** | Desenho visual | Wizard para montar as etapas em sequência |
| **Criar com IA (Hagent)** | Qualquer um | Chat que faz o levantamento e monta o fluxo ao vivo, refletido na pré-visualização lateral |

**Templates disponíveis** já alinhados a processos reais de mercado:

| Template | Domínio |
|---|---|
| Fluxo Genérico | Ponto de partida |
| Plano de Ação 5W2H | Qualidade / melhoria contínua |
| Pipeline Comercial B2B · B2B SaaS + Hardware | Comercial (MEDDIC, discovery, proposta, handoff) |
| Implantação de Cliente | Projeto / go-live |
| Suporte Remoto · Visita Técnica | Serviço / field service |
| Manutenção Preventiva · Manutenção Corretiva | Manutenção |
| Calibração de Equipamento | Metrologia (alinhado a RDC 658, ISO 17025, PIC/S) |
| Expedição de Carga Térmica | Cadeia de frio (evidência para RDC 430/2020 — distribuição e transporte) |
| Recebimento de Materiais | Inspeção de remessa termossensível |
| Transporte de Amostras | Custódia de amostras biológicas |
| Processo de Compras | Suprimentos (solicitação → pagamento) |
| Rota de Entregas (Demo) | Logística com roteirização |

**Round-trip de edição**: "Editar com IA" carrega a categoria inteira (etapas + campos + automações
+ config, com IDs preservados) como JSON, deixa o usuário ou a IA ajustar, e aplica **tudo num único
`PUT` transacional**. As regras dessa transação são precisas e importam para integridade:

- etapa **com id** e sem `dt_ina` → atualização;
- etapa **sem id** → inserção (a API cria);
- etapa **com id + `dt_ina`** → remoção lógica (*soft delete*);
- etapa **omitida do array** → **não é tocada** (remoção é sempre explícita, nunca por ausência);
- campo omitido preserva o valor atual; campo presente sobrescreve (inclusive `""`/`[]`/`null` para
 limpar);
- `config` (categoria e etapa) é mesclado com o atual antes de enviar, porque a API substitui a
 coluna JSON inteira.

**Copiar fluxo entre clientes** (perfil admin): exporta o fluxo como JSON **removendo os vínculos
específicos do cliente** (base de dados, agenda) e avisa exatamente quais campos precisam ser
reconfigurados no destino. Isso viabiliza uma biblioteca interna de processos reutilizáveis.

---

### Auditoria e reversão de versão

Toda alteração de fluxo e de etapa entra na trilha de auditoria com transação, usuário e horário. O
módulo expõe isso como **histórico de versões** navegável (no dashboard, na página da categoria e na
página da etapa), com duas ações:

- **Ver alterações** — abre a trilha completa da transação (campo a campo, valor antigo → novo) em
 modal, sem sair da tela;
- **Reverter** — recompõe o mesmo `PUT` atômico que gerou a transação, gravando os valores
 anteriores de cada campo alterado. Campos JSON (`config`, `campos`, `emails`…) voltam completos,
 sem perda; `dt_alt` é ignorado. Reverter exige permissão de edição da categoria.

Do ponto de vista de validação de sistema, isso atende diretamente aos requisitos de
**controle de mudanças** e **recuperabilidade de configuração**: qualquer alteração de POP
eletrônico é atribuível, datada, inspecionável e reversível.

---

### Registrar uma etapa

O motor é o `StructureCheckpointComponent`, aberto como modal em qualquer tela (Kanban, painel,
relatório, ficha de item, Minhas Atividades). Ele:

- renderiza os 29 tipos de campo respeitando `hidden`, `readonly` e obrigatoriedade;
- executa a automação da etapa em todos os momentos descritos em [Automação: onFieldChange e onClick](#automacao);
- aplica *defaults* e carrega opções dinâmicas;
- gerencia captura de câmera/QR/OCR/assinatura de forma segura para Android (fecha editor e retira o
 foco antes de abrir a câmera — o sistema pode encerrar a *Activity* com teclado aberto);
- garante a **coordenada geográfica** do registro no momento do save;
- monta o payload e o entrega ao serviço de persistência, que grava local e sincroniza.

**Política de geolocalização** (detalhada em) — dois eixos independentes:

| Eixo | Opções | Efeito |
|---|---|---|
| Precisão | `strict` / `relaxed` | `strict` exige acurácia mínima (35 m no app nativo, 85 m no desktop); `relaxed` aceita qualquer fixo |
| Fallback | forçar GPS × preferir ponto manual | Define se pode cobrar GPS ou se usa o ponto manual salvo |

Regras de proteção que já estão implementadas e são invariantes: a posição é **aquecida em
background** enquanto o operador preenche (o save normalmente não abre carregamento nenhum); nunca
grava coordenada `(0,0)`; o seletor manual de mapa só aparece por ação do usuário (nunca no boot); e
existe válvula de escape "definir manualmente" mesmo no perfil rígido, para registrar evidência
quando o GPS genuinamente não converge (ambiente fechado, câmara fria, subsolo).

**Validações no salvamento**:

- campos obrigatórios vazios;
- `image_required` da etapa sem nenhuma imagem;
- `etapa.blockSave` definido pela automação (com o motivo exibido ao operador);
- listas e malotes são sanitizados; malote vazio é omitido do payload;
- moeda é convertida de string exibida para **número** persistido.

**Após salvar**: se a etapa gerou malotes, o app oferece **imprimir os códigos** (código de barras)
na hora — fechando o ciclo físico↔digital da embalagem secundária.

**Offline**: o registro é gravado no banco local e enfileirado; ao reconectar, é enviado. As telas
mostram pendências e oferecem "enviar pendentes". A tag ganha marca de pendência local. Cadastros
(Bases de Dados) são sincronizados no mesmo momento em que as tags, para que campos `database`
funcionem sem rede.

---

### Relatório da tag

O dossiê da ocorrência — e o principal artefato de evidência. Arquitetura em camadas descrita em.

| Bloco | Conteúdo |
|---|---|
| **Cabeçalho / metadados** | Identificador (ou apelido), descrição, categoria, cliente, equipamento, responsáveis, datas e previsões — editáveis conforme permissão |
| **Timeline de etapas** | Todas as etapas com status visual (concluída/atual/pulada/pendente), contagem de repetições, quem registrou e quando; cada registro expande as respostas |
| **Registro de etapa inline** | Cada etapa da timeline oferece registrar/repetir; paradas de rota oferecem registrar por parada e navegação (Waze / Google Maps) |
| **Renderização de valores** | Renderizador universal por tipo: imagens em visualizador, PDFs em leitor, tabelas em modal, endereços, rotas, agenda, NF-e, base de dados |
| **Mapa** | Marcadores dos checkpoints com posição real (ocultável por configuração da categoria) |
| **Gráfico IoT** | Curva contínua de temperatura/umidade por equipamento, com linhas de mínimo/máximo configuradas no produto e **marcadores dos checkpoints sobre a curva** — cruza a evidência do processo com a telemetria |
| **Tags vinculadas** | Relação entre ocorrências (ex.: um transporte que agrega várias coletas) |
| **Filtro destacado** | Ao chegar de uma consulta avançada, os registros que casaram com a busca são destacados e o *scroll* vai até o primeiro |
| **Impressão** | Relatório imprimível com renderização dedicada por tipo de campo (nada de `[object Object]`) |

---

## Bases de Dados (cadastros reutilizáveis)

Um subsistema completo dentro do módulo. Elimina o maior gerador de erro em coleta de campo: a
redigitação.

### Estrutura

Uma base tem nome, descrição, status e **layout** — uma lista de campos que usa exatamente o mesmo
esquema dos formulários de etapa (`CheckpointField` + `order`, `description`, `placeholder`).
Consequência direta: **todo tipo de campo criado para etapas está disponível em cadastros** (exceto
os que só fazem sentido em etapa: agenda, rota, NF-e, câmera, malote e botão de ação).

Cada item tem `_id`, os valores por label, metadados de criação/alteração, remoção lógica
(`deletedAt`) e um cálculo de **completude** (`_completeness`: completo, campos faltando,
percentual) — a base sabe dizer quanto do cadastro está preenchido.

### Operação

| Recurso | Descrição |
|---|---|
| **Grid tipo planilha** | Edição **em célula** com editores por tipo (inline, linha expansível para endereço/agenda/NF-e, toggle, miniatura de imagem, modal para tipos complexos) |
| **Linha de criação (draft)** | Cadastro rápido direto no grid |
| **Formulário completo do item** | Reusa o motor de checkpoint — mesma experiência do formulário de etapa, incluindo automação e geolocalização |
| **Campo único (`unique_entry`)** | Impede valor repetido entre itens (ex.: CPF, placa, número de série) |
| **Campo "importante"** | Define a coluna primária: título do item, placeholder de busca e coluna em negrito |
| **Filtro avançado** | O mesmo componente do Kanban, aplicado ao layout da base |
| **Busca normalizada** | Ignora acentos e pontuação — CPF/CNPJ casam com ou sem máscara |
| **Remoção lógica + restauração** | Item excluído pode ser restaurado |
| **Impressão** | Página de impressão do cadastro |
| **QR por item** | Cada item pode ter QR que abre sua ficha (`base-dados/link-tag`) |

### Import/Export Excel

Ferramenta dedicada, com política explícita:

- **Exportação inclui todas as colunas visíveis**, com representação plana e legível por tipo
 (endereço formatado, emitente + NF, resumo de rota, tabela achatada) — JSON apenas como último
 recurso. O objetivo declarado é: tipos simples saem **prontos para reimportar**; tipos complexos
 saem **legíveis para auditoria/backup**.
- **Importação é restrita aos tipos com ida-e-volta segura** (texto, número, moeda, e-mail,
 telefone, URL, datas, seleções, switch, máscara, códigos, endereço). A UI lista quais colunas são
 "somente exportação" e por quê.
- **Modelo para download** com as colunas importáveis.
- **Geocodificação na importação** de endereços.
- **Tratamento de erro por linha**, com opção de "importar as linhas válidas e reportar as
 inválidas".
- **Decisão consciente de integridade**: a exportação **não** oculta a coluna NF-e por permissão —
 porque a exportação alimenta a reimportação, e omitir uma coluna faria o ciclo apagar dados que o
 usuário não vê. Integridade do dado vence sigilo da coluna nesse ponto específico, e isso está
 documentado como limite conhecido.

### Ficha do item (`link-tag`) — o "QR no ativo"

Página de destino do QR de um item de cadastro, e um dos padrões de uso mais eficientes do módulo:

- ficha do item (campos do layout) e **bases relacionadas** (itens vinculados por campos
 `database` do próprio item, exibidos indentados e navegáveis);
- **tags em andamento** relacionadas ao item, agrupadas por categoria;
- **próxima etapa pronta para registrar**, com indicação de bloqueio por sequência;
- confirmação visual "etapa registrada" logo após o registro;
- **sinal ao vivo do equipamento** (telemetria via socket) quando o produto está na frota carregada;
- atalho para iniciar uma nova tag **já com o item pré-selecionado** no campo de base de dados da
 etapa (o contexto viaja por navegação e também por parâmetros de URL, como *fallback* para
 recarregamento de página, com controle para não vazar o contexto para a próxima tag);
- atalho para o painel filtrado por aquele item;
- edição do item conforme permissão.

> **Padrão de uso**: QR na câmara fria, no freezer, no veículo, no equipamento, na sala. O
> colaborador lê e vê a ficha do ativo, o processo aberto e o botão da próxima ação. Zero navegação.

### Arquitetura offline dos cadastros

Ponto técnico com efeito comercial direto (operação em campo sem sinal):

- as bases e **todos os seus itens** são baixados para o banco local no mesmo momento em que as
 tags, com TTL de 10 minutos e execução única concorrente;
- escrita atômica (metadados + itens na mesma transação) — nunca fica com base nova e itens antigos;
- busca e paginação locais com três modos: *offset* materializado (ordenação por campo dinâmico),
 *offset* sem estado (primeira página) e **keyset** (`O(pageSize)`) para *scroll* infinito;
- controle de **geração de cache** que invalida cursores em voo quando o cache é reescrito — a lista
 recarrega do topo em vez de pular/repetir itens;
- invariante: item removido logicamente **nunca** é persistido no cache local (mais uma limpeza
 única, memoizada, para caches antigos) — o que a tela conta e o que ela mostra saem do mesmo
 universo.

---

## Agenda

Subsistema de calendário integrado ao fluxo.

### Configuração da agenda

| Item | Descrição |
|---|---|
| Identidade | Nome, descrição, cor, grupo, fuso horário, status |
| **Horário de trabalho** | Faixas por grupo de dias da semana |
| **Períodos disponíveis** | Janelas específicas por data |
| **Duração de slot** | Granularidade do agendamento |
| **Buffers antes/depois** | Folga entre compromissos (deslocamento, preparação) |
| **Sobreposição** | Permitir ou não eventos concorrentes |
| Duração mínima / máxima / padrão, hora padrão de início | Regras de duração |
| **Tipos de evento** | Vocabulário próprio do cliente (ex.: Coleta, Entrega, Visita, Manutenção) |
| **Usuários permitidos / padrão** | Quem pode usar e quem já vem pré-selecionado |
| **Endereço** | Habilitar / exigir endereço no evento |
| **Link de reunião** | Habilitar campo de reunião online |
| **Campos adicionais** | Layout de campos próprios do evento — **o mesmo esquema de `CheckpointField`** |
| **Script na criação de evento** | Automação `onEventCreate(event, user, agenda)` |

### Eventos

Um evento tem título, início/fim, usuários, tipo, descrição, status (rascunho, pendente, confirmado,
cancelado, concluído), participantes com e-mail, link de reunião, endereço, vínculo com tag, e os
**campos adicionais** com um **snapshot do layout** gravado junto — para que qualquer renderizador
resolva o tipo de cada campo sem consultar a definição da agenda (importante para histórico correto
quando o layout muda).

### Como a agenda se conecta ao processo

Três integrações, e é a combinação delas que gera valor:

1. **Campo `schedule`** — a etapa fica vinculada a um evento: a data futura deixa de ser um texto e
 passa a ter lembrete, participantes, local, status e visão de calendário.
2. **Origem de paradas de rota** — os eventos de um período viram destinos da rota, mantendo o
 vínculo (id do evento, horário combinado) para comparar previsto × realizado.
3. **Busca por campos do evento** — o filtro avançado consulta os campos adicionais dos eventos,
 inclusive subcampos (ex.: `Paciente.Nome completo`).

A tela de calendário é um calendário completo (dia/semana/mês/lista, criação por arraste conforme
permissão, múltiplas agendas visíveis simultaneamente com mini-calendário de navegação, layout
master-detail no desktop e otimizado no mobile). Títulos e descrições de evento são renderizados
como texto puro (nunca HTML), por serem dados de usuário.

---

## Integrações e extensibilidade

| Mecanismo | Direção | Quando usar |
|---|---|---|
| **Webhook por etapa** | Saída | Notificar ERP/CRM/RPA a cada conclusão de etapa; suporta host + headers (autenticação por token) |
| **E-mail** | Saída | Fixo na etapa ou dinâmico por resposta (`etapa.emails`) — sem custo |
| **Push** | Saída | Com título/corpo e variáveis do processo |
| **WhatsApp** | Saída | Canal com custo, configurado na etapa |
| **`fetch` na automação** | Entrada e saída | Consultar ou alimentar qualquer API (pública ou autenticada com o token do usuário) durante o preenchimento |
| **Botão de ação** | Entrada e saída | Integração **sob demanda**, disparada pelo operador (consultar protocolo, validar documento, reservar recurso) |
| **Widget público embutível** | Entrada | *Opcional, uso minoritário, em PoC*: o cliente final abre a solicitação no site do cliente e ela entra no fluxo — [Widget público de solicitações (embed no site do cliente)](#widget-publico-de-solicitacoes-embed-no-site-do-cliente) |
| **QR / código de barras** | Entrada | Início e continuação de processo, identificação de item, leitura de embalagem |
| **NF-e** | Entrada | Leitura estruturada da nota (chave, emitente, destinatário, produtos, valor) |
| **OCR** | Entrada | Digitalização de número de série, laudo, etiqueta |
| **Import/Export Excel** | Ambas | Carga inicial e manutenção de cadastros; extração para auditoria |
| **Telemetria IoT** | Entrada | Leitura do sensor no instante de cada registro + curva contínua no relatório; automação pode ligar/desligar o monitoramento do equipamento |
| **Socket** | Entrada | Atualização em tempo real de Kanban, painel e dashboard |

### Widget público de solicitações (embed no site do cliente)

> **Recurso OPCIONAL e de uso minoritário.** É mais uma forma de entrada de dados, ao lado das
> quatro principais (a–d), e só faz sentido para o subconjunto de clientes que quer receber
> solicitações pelo próprio site. **Não é premissa de nenhum fluxo** — a esmagadora maioria das
> implantações nunca o habilita.
>

#### O que é

Uma **linha de HTML no site do cliente** que renderiza o formulário de abertura de uma etapa pública
do fluxo. Quem preenche é o **cliente final do nosso cliente** — sem login, sem app, sem cadastro. A
solicitação entra como **tag no fluxo**, exatamente igual a uma aberta internamente: mesmo Kanban,
mesmo SLA, mesmas automações, mesmas notificações, mesmo relatório.

Em termos de arquitetura é apenas **um quinto canal de entrada**, ao lado da tela de vínculo, do
início automático, do QR e da geração em lote. Nada a jusante muda: quem consome a tag não sabe (nem
precisa saber) por onde ela entrou.

#### Como se habilita (duas chaves, ambas já existentes)

O widget não é configurado no widget — é configurado **no próprio fluxo**, com as duas flags já
descritas em e [Configuração da etapa](#configuracao-da-etapa):

| Onde | Flag | Efeito |
|---|---|---|
| Categoria | **Publicável** (`config.publicavel`) | Marca o fluxo como elegível à solicitação online |
| Etapa | **Disponível para solicitação online** (`config.public_visible`) | Marca *quais* etapas podem ser preenchidas publicamente |

As duas são independentes e cumulativas: marcar a categoria não publica nenhuma etapa. Isso permite
publicar **apenas a etapa de abertura** de um fluxo cujas demais etapas são internas.

Com as flags ativas, a tela da categoria e a tela da etapa passam a exibir o botão **"copiar
código"**, que gera o *snippet* pronto (`buildTicketsEmbedSnippet`), já com o `cliente-id` do
estabelecimento logado:

```html
<script type="module" src="https://hagelab.com.br/tickets/main.js"></script>
<hage-tickets-widget categoria-id="228" etapa-id="1142" cliente-id="5"></hage-tickets-widget>
```

Sem `etapa-id`, o widget abre a etapa pública marcada como **início**. Com `etapa-id`, vai direto ao
formulário daquela etapa.

#### Parametrização visual (atributos do elemento)

O widget se adapta à identidade visual do site hospedeiro sem CSS externo:

| Atributo | Função |
|---|---|
| `categoria-id` | Fluxo alvo (obrigatório) |
| `etapa-id` | Etapa alvo; ausente = etapa de início pública |
| `cliente-id` | Estabelecimento — necessário para listar etapas de categorias **por tenant**; sem ele, só categorias globais |
| `primary-color` | Cor primária em hex — o widget **recalcula sozinho** tom, matiz e cor de contraste |
| `background-color` | Fundo |
| `radius` | Raio de borda em px |
| `compact` | Layout compacto (booleano) |
| `hide-header` | Oculta o cabeçalho (booleano) |
| `submit-label` | Rótulo do botão final — porque nem todo fluxo é "solicitação" (pode ser "Agendar coleta", "Abrir chamado", "Enviar amostra") |
| `auth-token` | Token injetado **em runtime**, nunca compilado no bundle |

#### Fluxo multi-etapa público

Um fluxo pode ter mais de uma etapa pública (ex.: abertura pelo solicitante → confirmação de dados
pelo mesmo solicitante). A regra de avanço é deliberadamente conservadora: depois de salvar, o widget
só avança quando existe uma etapa pública cuja `ordem` seja **exatamente a atual mais um**. Uma etapa
pública posterior **nunca** é usada para pular uma etapa interna — o solicitante não consegue
atravessar o processo.

#### Reuso dos recursos do módulo dentro do widget

O formulário público não é um formulário separado: ele renderiza os campos configurados na etapa, e
dois tipos recebem tratamento próprio para não expor dado interno:

| Tipo | Comportamento no widget |
|---|---|
| `database` | Consulta pontual à Base de Dados para **identificar o solicitante** (no protótipo, por CNPJ) e pré-preencher empresa/unidade. Se o CNPJ não é localizado, os campos ficam abertos para preenchimento manual — nunca trava a solicitação |
| `schedule` | A agenda interna **não é exposta**. O campo é adaptado para data/hora; ao enviar, o widget cria o evento na agenda com duração provisória de 60 min e substitui o valor pela identificação retornada. Se o envio da etapa falhar, o evento é cancelado como compensação |

#### Quando faz sentido oferecer (e quando não)

**Faz sentido** no caso específico de o cliente ter um público externo que hoje solicita por e-mail,
telefone ou WhatsApp e precisa ser redigitado: portal de coleta (o laboratório pede a coleta no site
da transportadora), abertura de chamado pelo cliente final, agendamento de serviço. O ganho é a
solicitação chegar **estruturada e já no fluxo**, em vez de virar texto a ser transcrito — inclusive
disparando as automações de e-mail/webhook da etapa de abertura.

**Não faz sentido** — e é o caso da maioria — quando a entrada é sempre interna: operação de campo,
bancada, conferência, transporte, manutenção programada. Nesses fluxos os canais de a–d cobrem
tudo, e habilitar o widget só adiciona superfície pública sem uso.

Do lado do cliente o custo de adoção é baixo (uma tag `<script>` e um elemento, sem projeto de TI, com
cores e rótulos parametrizáveis), mas isso não muda o fato de que é um recurso de nicho.

---

## Playbooks de uso (como combinar as peças)

Esta seção é a mais útil para exploração comercial: mostra que o valor não está em nenhum recurso
isolado, mas na composição.

### Cadeia de frio — expedição e transporte

**Peças**: categoria com sequência restrita · etapas de preparação, pré-cooling, carregamento,
expedição, entrega · campos `number` (temperatura), `photo` **somente câmera**, `signature`,
`address`, `nfe`, `malote` · equipamento IoT vinculado · automação.

**Como a automação eleva o processo**:
- destaca a temperatura no painel e altera a descrição da tag quando sai de 2–8 °C;
- monta um aviso colorido no formulário orientando o operador ("Fora da faixa — registrar desvio");
- **bloqueia o salvamento** se a data de coleta for posterior à de entrega;
- coloca o equipamento **em operação** ao iniciar o transporte (passando a notificar os grupos de
 alerta sobre qualquer anormalidade) e **fora de operação** ao capturar a assinatura do recebedor,
 evitando alarme falso na volta com refrigeração desligada;
- dispara e-mail ao responsável apenas quando houver desvio.

**Resultado**: relatório com timeline, foto e assinatura por ponto, curva contínua de temperatura com
os marcadores dos checkpoints sobrepostos, e limites de mínimo/máximo do equipamento desenhados —
evidência única para auditoria de RDC 430/2020.

### Conferência de recebimento com embalagem secundária

**Peças**: etapa de coleta com campo `list` (lista de malotes lidos por código) · etapa de
recebimento com campo `malote` e `checkbox` · automação usando `tagFull`.

**Mecânica**: na etapa de recebimento, o script varre o histórico da tag, monta a lista completa do
que foi coletado, subtrai o que já foi informado como recebido e **calcula o que falta** — populando
o campo de faltantes e exibindo um banner de atenção. Se sobrar item pendente, define o destinatário
de e-mail; se não sobrar nada, define `null` e não dispara.

**Resultado**: conferência sem planilha paralela, com prova de qual embalagem chegou e qual não, e
alerta automático apenas quando há problema.

### Coleta e transporte de amostras com rota

**Peças**: agenda de coletas com campos adicionais (paciente, guia, tipo de exame) · etapa com campo
`routing` cuja fonte de destinos é a agenda · etapa "conclusão de rota" com foto, assinatura e
temperatura · horários padrão de início por dia da semana.

**Mecânica**: o roteirizador importa os eventos do dia como paradas, sugere a previsão de início pelo
horário padrão, otimiza por tempo ou distância (ou mantém a ordem, quando há hora marcada), calcula
ETA por parada e previsão de conclusão. Em cada destino, o motorista registra a etapa de conclusão
com o formulário completo; o script identifica a parada exata por `routingItem` e pode ler os dados
do agendamento (`routingItem.adicionais['Paciente']`). Navegação abre no Waze/Maps.

**Resultado**: roteiro otimizado, comprovação por parada, e comparação **ETA da rota × horário
combinado com o paciente** — base para indicador de pontualidade.

### Calibração / metrologia

**Peças**: base de dados de equipamentos (com número de série `unique_entry`) · etapas de
solicitação, retirada, execução, laudo, aprovação · campos `file` (laudo PDF), `ocr` (número de
certificado), `masked` com validador personalizado, `date` (validade) · etapa de aprovação com
setor restrito à Qualidade.

**Extras**: prazo padrão por etapa gerando SLA de próxima etapa; e-mail à Qualidade ao emitir laudo;
webhook ao aprovar, alimentando o ERP; consulta avançada por "validade de calibração até"; BI com
tempo médio por etapa e taxa de reprovação por campo de resultado.

### Field service (visita técnica e manutenção)

**Peças**: agenda com buffers de deslocamento · campo `schedule` na etapa de agendamento · rota para
o dia · etapas de saída, chegada, diagnóstico, resolução, aceite do cliente · assinatura do cliente ·
etapas avulsas para ocorrências no caminho.

**Extras**: Minhas Atividades como fila do técnico; QR no equipamento levando à ficha e à próxima
etapa; botão de ação consultando o histórico do equipamento na API do cliente; push ao solicitante
em cada mudança de etapa.

### Recebimento fiscal e conciliação

**Peças**: campo `nfe` (leitura por chave/QR) · automação gravando a chave como identificador e um
apelido legível como título · campo `table` para conferência item a item · `masked` (moeda) para
divergência de valor · webhook para o ERP.

**Resultado**: a tag passa a ser localizável pelo código de barras da própria nota, mas exibida como
"NF 32298 · Fornecedor X" na interface.

### Processos administrativos (compras, plano de ação, comercial)

O módulo não é só de campo. Os templates de **Processo de Compras** (solicitação → cotação →
aprovação → pedido → recebimento → conferência fiscal → pagamento), **Plano de Ação 5W2H**
(planejamento → execução → verificação de eficácia → encerramento) e **Pipeline Comercial B2B**
(prospecção → qualificação → apresentação → proposta → negociação → fechamento) demonstram que
qualquer processo com etapas, responsáveis, prazos, aprovações e evidência documental cabe no mesmo
motor — com o mesmo Kanban, o mesmo SLA e o mesmo BI.

### Etiqueta QR como interface única

Combinação transversal que vale destacar isoladamente, por ser a que mais reduz treinamento:

| QR colado em… | Aponta para… | O operador vê |
|---|---|---|
| Ativo / sala / veículo | `checar-andamento/:duracao/:categoria` | O processo em andamento ou um novo já criado |
| Item de cadastro | `base-dados/link-tag` | Ficha do ativo, tags relacionadas e próxima etapa |
| Malote impresso | código de barras do malote | Conferência da embalagem secundária |
| Equipamento rastreador | `track` | Ficha do equipamento e envio de posição |
| Tag gerada em lote | fluxo de início com prefixo/produto/descrição | Processo pré-configurado pronto para iniciar |

---

## Offline-first e sincronização

Requisito de operação de campo, e um diferencial que raramente sobrevive à prova real:

- **Vínculo de tag offline**: enfileira a requisição e grava a tag localmente já com categoria e
 snapshot de etapas (buscando a configuração de uma tag existente da mesma categoria, ou dos tipos
 em cache), para que o operador possa **continuar registrando etapas** imediatamente.
- **Registro de etapa offline**: gravado local e enviado ao reconectar; as telas mostram pendências
 e oferecem envio manual.
- **Cadastros offline**: bases e itens completos no banco local ([Arquitetura offline dos cadastros](#arquitetura-offline-dos-cadastros)), com busca e paginação
 locais — campos `database` funcionam sem rede.
- **Consulta offline**: painel e listagens servem os status operacionais do banco local; os status
 que exigem agregação do servidor são escondidos quando não há conexão (em vez de mostrar número
 errado).
- **Leitura indexada**: as tags de uma categoria são lidas por índice composto no banco local
 (`O(log n)`), não por varredura.
- **Recuperação**: envio de pendentes, limpeza de dados locais e recriação do banco local
 disponíveis na interface (com confirmação, já que pendências seriam perdidas).

---

## Subsídios para validação de sistema (ERU/URS)

Esta seção mapeia as capacidades do módulo aos requisitos típicos de especificação de requisitos do
usuário em ambiente regulado. Cada linha é redigida como um requisito verificável.

### Rastreabilidade e integridade do registro

| # | Requisito | Como o módulo atende | Referência |
|---|---|---|---|
| R-01 | Todo registro deve identificar **quem** executou, **quando** e **onde** | Usuário, data/hora, coordenadas e endereço geocodificado são gravados automaticamente em todo registro de etapa | [Catálogo de tipos de campo](#tipos-de-campo), [Registrar uma etapa](#registrar-etapa) |
| R-02 | A coordenada não pode ser nula, zerada ou arbitrária | Invariante explícita: nunca grava `(0,0)`; acurácia mínima configurável (35 m nativo / 85 m desktop); ponto manual é autoritativo e definido por ação do usuário | — |
| R-03 | O histórico de execução não pode ser alterado pela mudança de configuração | `tag.checkpoints` é snapshot no vínculo; a propagação da nova configuração é ação explícita ("Revincular tags") | [Relação entre as entidades](#relacao-entre-as-entidades) |
| R-04 | Cada campo deve ter identidade estável independente do rótulo | `CheckpointField.id` é UUID imutável, preservado no *rename*, e é a chave de dimensão do BI | — |
| R-05 | O sistema deve suportar evidência objetiva (imagem, assinatura, documento) | Foto (com modo **somente câmera**, sem galeria), assinatura em canvas, arquivo, OCR, NF-e, leitura de código | [Catálogo de tipos de campo](#tipos-de-campo) |
| R-06 | Evidência de imagem pode ser obrigatória por etapa | Flag `image_required`, validada no salvamento | [Configuração da etapa](#configuracao-da-etapa), [Registrar uma etapa](#registrar-etapa) |
| R-07 | Registro sem conexão não pode ser perdido | Fila offline com envio ao reconectar e indicação de pendência | [Offline-first e sincronização](#offline) |

### Controle de mudanças e configuração

| # | Requisito | Como o módulo atende | Referência |
|---|---|---|---|
| R-10 | Toda alteração de configuração de processo deve ser atribuível e datada | Trilha de auditoria por transação, com usuário e horário, navegável na interface | [Auditoria e reversão de versão](#auditoria-de-configuracao) |
| R-11 | Deve ser possível inspecionar o antes/depois de uma alteração | "Ver alterações" abre a trilha campo a campo da transação | [Auditoria e reversão de versão](#auditoria-de-configuracao) |
| R-12 | Deve ser possível reverter uma alteração de configuração | Reversão recompõe o mesmo `PUT` atômico com os valores anteriores (campos JSON completos, sem perda) | [Auditoria e reversão de versão](#auditoria-de-configuracao) |
| R-13 | Remoção de etapa não pode destruir histórico | Remoção é **lógica** (`dt_ina`); tags paradas na etapa removida ganham coluna virtual somente leitura no Kanban | [Cinco caminhos para criar um fluxo](#criar-um-fluxo) |
| R-14 | Alteração de configuração parcial não pode apagar configurações irmãs | Todas as escritas de `config` fazem *merge* local antes de enviar | [Cinco caminhos para criar um fluxo](#criar-um-fluxo) |
| R-15 | Configuração inconsistente não deve ser publicável | Validações de salvamento por tipo de campo (agenda, botão, máscara, rota) e validação obrigatória do script | [Configuração da etapa](#configuracao-da-etapa) |

### Controle de acesso

| # | Requisito | Como o módulo atende | Referência |
|---|---|---|---|
| R-20 | Acesso por função, com granularidade de operação | 12 códigos de permissão × 4 ações (consultar/inserir/alterar/excluir) | — |
| R-21 | Segregação entre quem desenha o processo e quem o executa | Códigos distintos para categorias/etapas (design) e Kanban/Minhas Atividades (execução) | — |
| R-22 | Restrição de visibilidade por área/setor | Setores por categoria e por subcategoria; opção de restringir até o histórico | — |
| R-23 | Ação destrutiva/excepcional deve exigir privilégio elevado e justificativa | Encerramento forçado exige as quatro ações do Kanban e justificativa obrigatória persistida | — |
| R-24 | Permissão deve ser reavaliada, não presumida | `guardPage` reavalia a cada entrada na tela; leituras críticas de permissão são reativas (concessão/revogação refletem sem recarregar) | — |
| R-25 | Dado de outro cliente/estabelecimento não deve ser editável | Verificação de estabelecimento no painel torna a tag somente leitura | — |
| R-26 | Informação fiscal pode ser restrita por perfil | NF-e é o único tipo com guarda **de renderização** — não aparece para quem não tem permissão, em nenhuma superfície de exibição | — |

### Consistência e qualidade do dado

| # | Requisito | Como o módulo atende | Referência |
|---|---|---|---|
| R-30 | Campos obrigatórios devem impedir a conclusão do registro | Validação de obrigatoriedade no salvamento, com marcação visual | [Registrar uma etapa](#registrar-etapa) |
| R-31 | Deve haver validação cruzada entre respostas | `etapa.blockSave` bloqueia o salvamento com motivo exibido; recomputado na abertura e a cada alteração | [Automação: onFieldChange e onClick](#automacao) |
| R-32 | Formato de dados estruturados (documentos, códigos) deve ser validado | Máscaras nativas (CPF/CNPJ/telefone/moeda) + máscara personalizada com validador JS | [Catálogo de tipos de campo](#tipos-de-campo) |
| R-33 | Duplicidade de identificação deve ser evitável | `proibir_duplicidade` na categoria; `unique_entry` em cadastros | [Operação](#operacao) |
| R-34 | Dados repetidos devem vir de fonte única | Bases de Dados com seleção de item (funcionando offline) em vez de digitação | [Bases de Dados (cadastros reutilizáveis)](#bases-de-dados) |
| R-35 | Ordem de execução pode ser mandatória | `stage_required` + `restringir_sequencia`, com bloqueio visível e explicado ao operador | [Configuração da etapa](#configuracao-da-etapa) |
| R-36 | Valores monetários devem permitir cálculo | Moeda exibida formatada e persistida como número | [Catálogo de tipos de campo](#tipos-de-campo) |

### Relato, extração e retenção

| # | Requisito | Como o módulo atende | Referência |
|---|---|---|---|
| R-40 | Deve existir relatório por ocorrência, imprimível | Relatório da tag com timeline, evidências, mapa, curva IoT e impressão dedicada por tipo de campo | [Relatório da tag](#relatorio) |
| R-41 | Qualquer dado coletado deve ser pesquisável | Filtro avançado sobre todos os campos de todas as etapas, com estratégia por tipo e subcampos | — |
| R-42 | Qualquer dado coletado deve ser mensurável | Dimensões dinâmicas de BI por campo, com chave imutável | — |
| R-43 | Deve ser possível extrair dados para análise externa | Exportação CSV do BI e Excel dos cadastros, com representação legível de tipos complexos | [Import/Export Excel](#import-export-excel) |
| R-44 | Limitações de amostragem devem ser explícitas | Aviso de truncamento da lista bruta no BI, distinguindo o que vale para o período inteiro | — |

### Limites e decisões conhecidas (declarar na validação)

Declarar limites é parte de uma boa especificação. Os seguintes pontos são **decisões documentadas**,
não defeitos:

1. **Exportação de cadastro não aplica a restrição de NF-e** — porque a exportação alimenta a
 reimportação e omitir coluna causaria perda de dado no ciclo. Integridade prevalece sobre sigilo
 nesse ponto.
2. **HTML de notas confia na origem** — o HTML de campo `note` é renderizado sem sanitização de
 estilo, porque o visual do aviso vem de estilos inline. O HTML é produzido pelo builder visual ou
 por script de desenvolvedor. Script que monte nota concatenando resposta de API externa deve
 sanitizar na origem.
3. **Coluna obrigatória em tabela não é validada** — a obrigatoriedade da tabela é avaliada no
 conjunto, não célula a célula.
4. **Flags dinâmicas em tabela valem por coluna, não por linha** — `readonly`/`hidden`/`required`
 mutados por script afetam o template da coluna; efeito por linha é suportado para valor e opções.
5. **Opções carregadas por API não são genéricas** — hoje só um caso específico é populado
 automaticamente; carregar opções dinamicamente se faz via automação (`fetch` + `campo.options`).
6. **Detecção de campos tocados pela automação é por expressão regular**, não análise sintática:
 referência por variável não é detectada (falso negativo aceito em indicador de interface).
7. **Filtro de "Análise de Atrasos" no BI não recorta** — não existe parâmetro equivalente na API
 para suas fatias, e as categorias de atraso se sobrepõem; a regra do "no prazo" precisa ser
 definida onde os agregados são calculados.
8. **`alias_identificador` só pode ser definido por automação** — é decisão de produto, para o
 identificador funcional nunca ser substituído por um rótulo.
9. **Alguns canais têm custo** — WhatsApp é cobrado por mensagem e só é configurável na tela da
 etapa (nunca por script), com aviso obrigatório de custo.

---

## Changelog da documentação

| Versão | Data | Mudança |
|---|---|---|
| `2026.08` | 27/08/2026 | Primeira publicação como referência versionada. Prompt de agente separado em `prompt.md`; referências cruzadas passam a apontar apenas para conteúdo público. |

*Gerado a partir das fontes do produto. Não editar à mão — a correção se faz na fonte.*
