Ir para o conteúdo

Plugin system para mappings

Diátaxis: explanation. RFC base para v1.2. Para o como usar, veja docs/reference/cli.md seção mappings list.

Problema

Cada secretaria de saúde brasileira customiza seu e-SUS, SISREG e SEI. O Anotae já entrega mappings built-in (vocabularies/mappings/) para o piloto APS/DF. Conforme adoção cresce:

  • UBSs em outros estados querem mappings próprios sem PR no repo principal (workflow lento para customização local).
  • Mantenedores querem que mappings comunitários convivam com os built-in sem sobrescrever acidentalmente.
  • Multi-tenant (mais de um SEI no mesmo PC, ex.: SEI/DF + SEI/SES-SP) precisa de prioridade explícita.

Modelo

Três fontes de mappings, em ordem de prioridade crescente:

BUILTIN (vocabularies/mappings/)        ← release-signed
  ↓ pode ser sobrescrito por
USER    (~/.anotae/mappings/)           ← opt-in do usuário
  ↓ pode ser sobrescrito por
CUSTOM  (ANOTAE_MAPPING_PATHS env var)  ← multi-tenant / dev

A unidade de identidade é o campo id no JSON. Conflitos são resolvidos elegendo a fonte de maior prioridade. O id perdedor fica registrado em DiscoveryReport.overridden para auditoria.

Decisões

Plugins são apenas dados

Os mappings são JSON — sem código executável, sem eval. A extensão de navegador interpreta target_selectors (CSS / id / name / aria-label / label); a engine Python só carrega e valida.

Trade-off: - Pro: zero risco de execução arbitrária via mapping malicioso. - Contra: limita expressividade. Para transforms complexas (ex.: formatar data antes de injetar) precisamos estender o JSON com enum de transforms — não com expressões livres.

Validação obrigatória

Mappings inválidos são pulados com warning, não derrubam o app. DiscoveryReport.skipped lista cada arquivo + motivo. CLI anotae mappings list mostra os pulados ao final.

Sem hot-reload por agora

Mudanças em ~/.anotae/mappings/ exigem restart do app. Hot-reload é planejado para v1.2+ se demanda surgir — adiciona complexidade (invalidação de caches, sinalização para extensão) sem ganho imediato no piloto.

Sem versionamento semântico forçado

O JSON tem version (string livre). Não há check de compatibilidade entre versões — confia no autor do mapping para gerenciar quebras. Vamos formalizar quando alguma operação destrutiva entre versões aparecer (esperamos que nunca: mappings são aditivos por natureza).

Quando promover USER → BUILTIN

Critérios para um mapping comunitário virar built-in:

  1. Cobertura clara: target_system + target_version + (se aplicável) target_instance bem definidos.
  2. Validado em pelo menos 2 UBSs distintas (relato + screenshot sem PII).
  3. Mantenedor identificado dispostos a manter o mapping em release futuras.
  4. PR aprovado + entrada em THIRD_PARTY_NOTICES.md se o mapping reusou trabalho existente (ex.: SEI Pro AGPL-3.0).

Como criar um mapping novo

Para uso pessoal/local (sem PR):

mkdir -p ~/.anotae/mappings
cp vocabularies/mappings/sei-relatorio-ses-df.json \
   ~/.anotae/mappings/sei-relatorio-meu-orgao.json
# editar id, name, target_instance e selectors específicos
anotae mappings list   # confere que carregou como source=user

Para teste em dev sem alterar ~/.anotae/mappings/:

export ANOTAE_MAPPING_PATHS=/tmp/mappings-dev
mkdir -p /tmp/mappings-dev
# colocar JSONs lá
anotae mappings list   # source=custom

Quando reabrir esta decisão

  • Demanda por mappings com lógica condicional → estender JSON com enum de transforms ou avaliar embed de Lua/Starlark sandbox.
  • Distribuição via package manager (npm, pip de mapping bundles) → adicionar entry_points em vez de pastas só de filesystem.
  • Hot-reload vira requisito → adicionar watchdog em ~/.anotae/mappings/ + sinal Qt para recarregar combo de Integrações.