feat(core,cli): terminologia pública + CNES record labeler (PRE-200) - #6
Conversation
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.
Claude Review - Round 1SummaryThis 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
🔍 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 |
- 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.
Claude Review - Round 2 (Final)SummaryThis PR makes several improvements: adds a fast-fail validation for missing CNES field in Changes
🔍 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 |
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.
## 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))
Sumário
Fecha os dois gaps que faltavam pra
/platformconsumir odatasus-brasildireto, conforme PRE-200:cnes.labelEstabelecimento— projeta os 150+ campos DATASUS crus num objeto legível em pt-BR1. 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.jsoncompleto 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:
/platformconsegue `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`:
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:
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
Saúde do código
Plano de teste