Skip to content

feat(core,cli): terminologia pública + CNES record labeler (PRE-200) - #6

Merged
rlueder merged 3 commits into
mainfrom
feat/pre-200-terminology
Apr 22, 2026
Merged

feat(core,cli): terminologia pública + CNES record labeler (PRE-200)#6
rlueder merged 3 commits into
mainfrom
feat/pre-200-terminology

Conversation

@rlueder

@rlueder rlueder commented Apr 22, 2026

Copy link
Copy Markdown
Member

Sumário

Fecha os dois gaps que faltavam pra /platform consumir o datasus-brasil direto, conforme PRE-200:

  • Terminologia pública — LOINC ↔ TUSS ↔ SIGTAP como API tipada (dados já existiam em v0.1, faltava a superfície programática)
  • cnes.labelEstabelecimento — projeta os 150+ campos DATASUS crus num objeto legível em pt-BR
  • Smoke test e2e — rodado contra FTP DATASUS real

1. Terminologia pública

Novo módulo packages/core/src/terminology/:

```ts
import { listBiomarkers, loincToSigtap, lookupSigtap, lookupTuss } from '@precisa-saude/datasus';

const m = loincToSigtap('2085-9'); // Colesterol HDL
// → { loinc: '2085-9', biomarker: { code: 'HDL', display: 'Colesterol HDL' },
// sigtap: '0202010279', tuss: '40301583', confidence: 'high',
// source: 'llm-refined', reasoning: '...', noMatchReason: null }

lookupSigtap(m!.sigtap!); // → { code: '0202010279', name: 'DOSAGEM DE COLESTEROL HDL' }
lookupTuss(m!.tuss!); // → { code, name, sigtapEquivalents: [...] }
listBiomarkers(); // → 164 biomarcadores
```

Reorganização dos dados: runtime JSONs movidos pra src/terminology/data/ (vão pro bundle); audit (loinc-tuss-sigtap.llm.json completo com `candidates_shown`, `.report.md`, `.fsh`, `.diff.md`) fica em `data/`. `loinc-biomarkers.json` slimmed de 465KB → 98KB removendo campos audit-only.

Scripts geradores em `packages/core/scripts/` + `scripts/` atualizados pra emitir pros dois destinos (audit + runtime slim).

Critério de aceite: /platform consegue `import { terminology } from '@precisa-saude/datasus'` e mapear todos os 164 biomarcadores. ✅

2. CNES record labeler

`cnes.labelEstabelecimento(record)` projeta um registro CNES-ST cru em `LabeledEstabelecimento`:

  • Identificação: cnes, cnpj, fantasia, razão, município (via `findMunicipio`), geo, competência ISO (`"2024-01"`)
  • Classificação (`{ codigo, rotulo }`): tipo, natureza jurídica, gestão, esfera, vínculo SUS, clientela, turno, atividade de ensino, tipo prestador, nível de atenção ambulatorial/hospitalar, nível de dependência, pessoa
  • Capacidade agregada: `instalacoes` (37 slots QTINST decodificados), `leitos` (QTLEIT + LEITHOSP com breakdown por especialidade)
  • Serviços & convênios: `servicosApoio` (SERAP próprio/terceirizado/ambos), `atendimentos` (flags booleanas ativas), `matrizAtividadeConvenio` (7×7)

8 tabelas modulares em `packages/core/src/datasets/cnes/tabelas/`. CLI ganha flag `--labeled` (mutuamente exclusiva com `--raw`).

Critério de aceite: `datasus-brasil cnes --uf AC --year 2024 --month 1 --labeled --limit 3` emite estabelecimentos com todos os códigos decodificados em pt-BR, sem campos QTINST15/SERAP03P/AP02CV01 crus no output. ✅

3. Smoke test end-to-end

`examples/cnes-smoke-test.sh` roda 3 cenários contra FTP DATASUS real:

  1. Golden path — top-5 tipos de unidade
  2. Labeled — 3 estabelecimentos em pt-BR
  3. Streaming JSONL → jq — contagem por gestão

Rodado contra AC/2024/01, 1374 estabelecimentos, todos verdes.

Também adicionado `examples/cnes-labeled.ts` como exemplo TS usando a nova API.

Bonus: ESLint + READMEs

  • ESLint ganha bloco pra `scripts/**/*.ts` desabilitando type-aware parsing e `no-console` (são utilitários, não runtime)
  • README root reescrito com exemplos de todos os comandos (saídas reais capturadas do CLI ao vivo) e subseções pra cada modo de uso
  • Disclaimer "em desenvolvimento" removido dos 3 READMEs
  • DBC README ganha seção API real documentando as 3 camadas (readDbcRecords, dbcToDbf, implodeDecompress)
  • core/README ganha seção "Terminologia LOINC ↔ TUSS ↔ SIGTAP" com exemplo de uso

Saúde do código

  • `pnpm turbo run typecheck test lint build` verde: 12/12 tarefas
  • 120 testes passando (5 dbc + 50 core + 65 CLI) — +22 novos (15 terminologia + 7 labeler)
  • Bundle core: 2.55MB (JSONs de terminologia embutidos, slim aplicado)
  • pt-BR correto em docs e commits

Plano de teste

  • CI verde após merge
  • Smoke test local: `examples/cnes-smoke-test.sh AC 2024 1`
  • `/platform` importa `loincToSigtap` e confirma mapping pros biomarcadores do `@precisa-saude/fhir`

PRE-200. Fecha os dois gaps que faltavam pra /platform consumir direto:

1. Terminologia LOINC ↔ TUSS ↔ SIGTAP como API pública

   Novo módulo `packages/core/src/terminology/` com `loincToSigtap`,
   `lookupSigtap`, `lookupTuss` e `listBiomarkers`. Os dados já existiam
   em v0.1 (164 biomarcadores LOINC fuzzy + refinados por Gemini, 6919
   linhas ANS, 4982 procedimentos SIGTAP) mas ficavam em `data/` sem
   acesso programático. Agora o /platform importa a função tipada.

   Reorganização: runtime JSONs em `src/terminology/data/`, audit em
   `data/`. Slim do `loinc-biomarkers.json` (465KB → 98KB removendo
   `candidates_shown` que era audit-only). Scripts geradores apontam
   pros dois destinos; `llm-refine-mapping` emite full + slim.

2. `cnes.labelEstabelecimento` — registros CNES-ST legíveis

   Projeta os 150+ campos DATASUS num objeto pt-BR com 8 tabelas de
   código (gestão, clientela, natureza jurídica, nível de atenção,
   etc.), instalações agregadas (37 slots QTINST), leitos (QTLEIT +
   LEITHOSP), serviços de apoio (SERAP P/T), matriz atividade×convênio
   (7×7) e competência ISO. CLI ganha `--labeled` (mutuamente exclusivo
   com `--raw`).

3. Smoke test end-to-end

   `examples/cnes-smoke-test.sh` exercita os 3 modos (default, labeled,
   raw + jq) contra FTP DATASUS real — rodado contra AC/2024/01 com 3
   cenários verdes. `examples/cnes-labeled.ts` exemplo TS da API.

Bonus: ESLint ganha bloco pra `scripts/**` (desabilita type-aware
parsing e no-console; são utilitários, não runtime). README root
reescrito com exemplos de TODOS os comandos (saídas reais capturadas
do CLI ao vivo) e subseções pra cada modo. Disclaimer "em desenvolvimento"
removido dos 3 READMEs; DBC README ganha seção API real.

Totais: 120 testes (5 dbc + 50 core + 65 CLI), bundle core 2.55MB.
@github-actions

Copy link
Copy Markdown

Claude Review - Round 1

Summary

This PR adds CNES establishment labeling (labelEstabelecimento), a terminology module (LOINC↔TUSS↔SIGTAP), a --labeled CLI flag, and restructures data files from packages/core/data/ to packages/core/src/terminology/data/. It includes new lookup tables for CNES fields (gestão, clientela, natureza jurídica, instalações, leitos, serviços de apoio, atividade×convênio), CLI streaming improvements, and comprehensive README updates.

Changes

  • New labelEstabelecimento function that projects raw CNES-ST records into human-readable pt-BR objects
  • New --labeled CLI flag (mutually exclusive with --raw) with JSONL default format
  • New terminology module with loincToSigtap, lookupSigtap, lookupTuss, listBiomarkers exports
  • Moved ans-tuss-sigtap data file into src/terminology/data/ directory
  • Added large loinc-biomarkers.json (2148 lines) with LLM-refined mappings
  • New CNES lookup tables: gestão, clientela, natureza jurídica, instalações, leitos, serviços de apoio, atividade×convênio
  • Updated CLI usage text and README documentation extensively

🔍 Found 8 suggestions (see inline comments)

Automated review by Claude Opus 4.6 - Round 1 of 2 | 56,985 in / 1,492 out | $0.3222

Comment thread packages/core/src/datasets/cnes/tabelas/leitos.ts
Comment thread packages/core/src/datasets/cnes/tabelas/natureza-juridica.ts
Comment thread packages/core/src/datasets/cnes/tabelas/natureza-juridica.ts
Comment thread packages/core/src/datasets/cnes/label-estabelecimento.ts
Comment thread packages/cli/src/commands/cnes.ts
Comment thread packages/core/src/datasets/cnes/tabelas/instalacoes.ts
Comment thread packages/core/src/terminology/data/loinc-biomarkers.json
Comment thread packages/cli/src/commands/cnes.ts
- natureza-juridica: remover entry dead-code `3069.1` (dot inválido,
  nunca bate com NAT_JUR real de 4 dígitos)
- instalacoes: remover QTINST25/QTINST26 da tabela de labels (semântica
  ambígua/duplicada contra QTINST08/QTINST23 sem fonte autoritativa).
  `labelInstalacoes` agora itera todos os `QTINST\d+` do record em vez
  da tabela, emitindo `rotulo: null` pra slots desconhecidos — preserva
  o código cru no output em vez de silenciar dados.
- labelEstabelecimento: validar CNES obrigatório — passar registro sem
  CNES (ex: CNES-PF em vez de CNES-ST) agora lança erro em vez de
  produzir objeto silenciosamente vazio. Tipo `cnes: string` (era
  `string | null`).
- loinc-biomarkers.json (runtime slim): stripar `_source` pra eliminar
  churn no checksum do pacote causado por `extracted_at` timestamp —
  procedência completa permanece no audit file em `data/`.
- +2 testes cobrindo validação CNES e passthrough de slot QTINST
  desconhecido.
@github-actions

Copy link
Copy Markdown

Claude Review - Round 2 (Final)

Summary

This PR makes several improvements: adds a fast-fail validation for missing CNES field in labelEstabelecimento, changes labelInstalacoes to iterate over record keys instead of only known labels (preserving unknown slots with rotulo: null), removes potentially incorrect entries from lookup tables (QTINST25/26 and natureza-juridica 3069.1), strips _source metadata from loinc-biomarkers.json, and updates tests accordingly.

Changes

  • Added early validation in labelEstabelecimento that throws if CNES field is missing
  • Changed cnes field type from null | string to string in LabeledEstabelecimento interface
  • Refactored labelInstalacoes to iterate over record keys matching QTINST pattern instead of only known labels, emitting rotulo: null for unknown slots
  • Removed QTINST25 and QTINST26 from INSTALACOES_LABELS as their semantics are uncertain
  • Removed natureza-juridica entry '3069.1' (sub-code variant)
  • Removed _source metadata from loinc-biomarkers.json and slim output in llm-refine-mapping.ts
  • Added tests for CNES-absent failure and unknown QTINST slot preservation

🔍 Found 3 suggestions (see inline comments)

Automated review by Claude Opus 4.6 - Round 2 (Final) - No further reviews will be performed | 4,415 in / 762 out | $0.0411

Comment thread packages/core/src/datasets/cnes/tabelas/instalacoes.ts Outdated
Comment thread packages/core/src/datasets/cnes/tabelas/instalacoes.ts Outdated
Comment thread packages/core/src/datasets/cnes/tabelas/instalacoes.ts
Round 2 review da PR #6: iterar `Object.keys(record)` fazia a ordem
das instalações depender da ordem de inserção dos campos (não-deter-
minístico) e varria as ~150 colunas do CNES-ST só pra filtrar 37
slots `QTINST`. Substituído por loop `01..99` com `padStart(2, "0")` —
ordem numérica fixa e custo constante independente do tamanho do
record.
@rlueder
rlueder merged commit b940743 into main Apr 22, 2026
3 checks passed
@rlueder
rlueder deleted the feat/pre-200-terminology branch April 22, 2026 16:22
precisa-saude-release-bot Bot pushed a commit that referenced this pull request Apr 22, 2026
## 1.0.0 (2026-04-22)

### Features

* **ci:** adotar workflows canônicos split + doctor + publish-tag ([ea8194d](ea8194d))
* consumir @precisa-saude/agent-instructions + worktree-cli ([9e91ee0](9e91ee0))
* **core,cli:** terminologia pública + CNES record labeler (PRE-200) ([#6](#6)) ([b940743](b940743))
* datasus-brasil v0.1 — decoder DBC, CNES, CLI e mapeamento LOINC↔TUSS↔SIGTAP ([#1](#1)) ([27cd027](27cd027))

### Bug Fixes

* **ci:** concede contents: write no caller para _release.yml poder pedir ([08d5611](08d5611)), closes [#16](#16)
* pre-push fallback — typecheck/test topológicos, só lint paralelo ([6b86449](6b86449))

### Tests

* **dbc:** cobrir caminhos de erro e decoders por tipo ([5bcae1d](5bcae1d))
* excluir scripts de build e arquivos types.ts da cobertura ([5287766](5287766))

### CI/CD

* drop --offline from pre-push pnpm install ([2834a16](2834a16))
* normalizar workflows e templates de PR/issue ([8eabc49](8eabc49))

### Chores

* alinhar hooks husky e turbo.json ao template compartilhado ([acd971a](acd971a))
* aplicar drift safe-only do precisa sync ([3ada96c](3ada96c))
* aplicar fixes do template husky (turbo detection + regex var) ([98ac50f](98ac50f))
* **config:** scaffold inicial do monorepo datasus-brasil ([04dc154](04dc154))
* **deps:** add renovate config ([808bd94](808bd94))
* **deps:** adotar configs compartilhadas [@Precisa-Saude](https://github.com/precisa-saude) ([a42c894](a42c894))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant