# Plano de testes e evals — Runtime de consolidação de artigos **Versão:** 1.0 **Data:** 23 de agosto de 2026 **Escopo:** runtime; self-healing excluído **Regra mestre:** comprovar integralmente os requisitos de produção com a menor suíte suficiente, sem testes duplicados, frágeis ou sem efeito em gates. ## 1. Objetivo Demonstrar que o runtime atende aos contratos, preserva grounding, aplica o ECP, suporta falhas e processa 100 artigos por hora sem perda ou duplicação. ## 2. Princípios - Qualidade agregada não pode esconder falhas por idioma, domínio ou extrator. - Invariantes críticas exigem zero falhas. - Grounding é validado por código, não por LLM-as-a-judge. - Nenhum teste textual ou assertion usa regex. - Nenhuma decisão esperada depende de lista manual de palavras-chave. - Prompts reais são usados no Promptfoo. - Providers são simulados nos testes de integração e reais apenas nos evals autorizados. - Casos de correção textual distinguem reparo de reescrita. - Self-healing, otimizador e judge não fazem parte desta suíte. ## 3. Camadas de teste | Camada | Finalidade | Execução | | --- | --- | --- | | Unitário | Funções determinísticas, parsers, IDs, estado e renderer | Todo PR | | Contrato | Schemas de artigo, ECP, LLM e manifesto | Todo PR | | Integração simulada | Fluxo completo com respostas controladas dos providers | Todo PR | | Promptfoo reduzido | Regressão crítica de prompts e modelos baratos | Mudança em prompt, schema, contexto ou modelo | | Golden set completo | Qualidade por artigo, idioma, domínio e extrator | Antes de promoção | | Carga | 100 artigos/hora | Staging antes do go-live e mudança relevante | | Fault injection | Provider, disco, SQLite e Langfuse | Staging e releases relevantes | | Segurança | Prompt injection, secrets e conteúdo hostil | Todo release | | Reprocessamento | Idempotência, retomada e duplicidade | Todo release | ## 4. Dados de teste ### 4.1 Fixture inicial Os 20 artigos do JSON de referência são a regressão inicial obrigatória. Cada item deve ser convertido para a unidade de entrada do runtime e associado a um ECP válido. ### 4.2 Golden set de produção O conjunto deve ser revisado manualmente e estratificado por: - idioma; - domínio de origem; - extrator selecionado; - tamanho e estrutura; - concordância e divergência entre extratores; - ECP direto, contextual, tangencial e não relacionado; - ruídos editoriais; - pequenos defeitos textuais. O corpus deve ser suficiente para cobrir os idiomas, domínios, extratores, estruturas editoriais e tipos de ruído suportados. Seu tamanho deve ser justificado pela estabilidade observada dos resultados e pelos intervalos de confiança dos gates medidos. ### 4.3 Holdout Parte da golden set deve permanecer fora da elaboração dos prompts. Resultados do holdout são usados no gate final e não podem orientar exemplos few-shot diretamente. ### 4.4 Verdade de referência Cada caso deve possuir: - entrada integral; - ECP e versão; - status esperado; - título, subtítulo, autor e data esperados ou conjunto de candidatos aceitáveis; - blocos mantidos e removidos; - links e imagens mantidos; - reparos permitidos e proibidos; - classificação ECP esperada; - sentimento esperado; - tags aceitas ou rubrica fechada; - Markdown ou estrutura esperada; - motivo de falha/descarte, quando aplicável. ## 5. Métricas de avaliação - pass rate integral por artigo; - precisão, recall e F1 de blocos mantidos; - acurácia de metadados; - taxa de reparos corretos; - taxa de reparos indevidos; - grounding textual; - grounding de URL e imagem; - validade de schema; - classificação ECP; - sentimento; - tags; - custo e latência. Resultados devem ser segmentados por idioma, domínio, extrator e versão de prompt/modelo. ## 6. Gates críticos Uma configuração não pode ser promovida se ocorrer qualquer um dos seguintes: - texto inventado; - URL ou imagem inventada; - fato, número, nome, data, placar ou citação alterado indevidamente; - reparo usado como reescrita; - ID inexistente aceito; - duplicidade de saída; - secret em log, trace ou arquivo; - regex no pipeline textual ou assertion de conteúdo; - modelo potente configurado no runtime; - Promptfoo chamado online. ## 7. Gates quantitativos - pass rate integral textual: pelo menos 95%; - grounding textual, URLs e imagens: 100%; - schema válido em todas as respostas aceitas: 100%; - duplicidade: 0; - perda no teste de carga: 0; - trace enviado ou telemetria preservada: 100%. Custo e latência devem ser medidos no staging e receber limites aprovados antes do go-live. ## 8. Testes de contrato de entrada | ID | Cenário | Resultado esperado | | --- | --- | --- | | IN-001 | Artigo válido, ECP válido e extrator disponível | Segue para fingerprint | | IN-002 | JSON do artigo corrompido | `INVALID_ARTICLE_SCHEMA`; nenhum LLM | | IN-003 | ECP ausente | `INVALID_ECP_SCHEMA`; nenhum LLM | | IN-004 | ECP corrompido | `INVALID_ECP_SCHEMA`; nenhum LLM | | IN-005 | ECP em versão incompatível | Falha de contrato; nenhum LLM | | IN-006 | `selected_extractor` ausente | `MISSING_SELECTED_EXTRACTOR` | | IN-007 | `selected_extractor` desconhecido | `INVALID_SELECTED_EXTRACTOR` | | IN-008 | Extrator selecionado sem dados utilizáveis | `SELECTED_EXTRACTOR_UNAVAILABLE` | | IN-009 | Outro extrator utilizável, mas selecionado inválido | Falhar; não substituir silenciosamente | | IN-010 | URL ausente em todas as fontes | `MISSING_SOURCE_URL` | | IN-011 | Título ausente em todas as fontes | `MISSING_TITLE_CANDIDATE` | | IN-012 | Sem conteúdo textual | `MISSING_CONTENT` | | IN-013 | Campos extras desconhecidos | Preservar entrada registrada e ignorar no fluxo | | IN-014 | Wrapper com array `articles` usado como artigo | Falha de schema da unidade | | IN-015 | Conteúdo contém instrução para o modelo | Tratar como dado não confiável | ## 9. Testes de fingerprint e idempotência | ID | Cenário | Resultado esperado | | --- | --- | --- | | ID-001 | Mesma entrada e mesmas versões | Mesmo fingerprint | | ID-002 | Mudança apenas de timestamp operacional | Mesmo fingerprint | | ID-003 | Mudança de prompt | Fingerprint diferente | | ID-004 | Mudança de modelo funcional | Fingerprint diferente | | ID-005 | Mudança do ECP | Fingerprint diferente | | ID-006 | Execução já concluída | Retornar saída existente sem novo LLM | | ID-007 | Queda antes da primeira escrita | Reexecução limpa | | ID-008 | Queda durante escrita temporária | Nenhum arquivo final parcial | | ID-009 | Queda após Markdown e antes do estado final | Reconciliação por hash sem duplicidade | | ID-010 | Duas execuções concorrentes idênticas | Uma conclusão efetiva, nenhuma duplicidade | ## 10. Testes de parsing sem regex | ID | Cenário | Resultado esperado | | --- | --- | --- | | PAR-001 | HTML válido com elementos aninhados | DOM preserva relações | | PAR-002 | HTML malformado tolerado pelo parser | Estrutura segura ou falha controlada | | PAR-003 | Markdown com heading, lista, citação e ênfase | AST identifica tipos | | PAR-004 | URL com query e fragmento | Parser preserva componentes válidos | | PAR-005 | Unicode composto e decomposto | Normalização consistente | | PAR-006 | Texto em idiomas sem separação simples por espaços | Tokenizador apropriado não quebra o fluxo | | PAR-007 | JSON-LD com `Article` ou `NewsArticle` | Metadados estruturais registrados | | PAR-008 | JSON-LD inválido | Ignorar fonte inválida e registrar warning | | PAR-009 | Código importa módulo de regex no pipeline textual | CI falha por análise AST | | PAR-010 | Configuração Promptfoo contém assertion regex de conteúdo | CI falha | ## 11. Testes de candidatos e proveniência | ID | Cenário | Resultado esperado | | --- | --- | --- | | CAN-001 | Mesmo bloco em três extratores | Equivalência registrada, IDs preservados | | CAN-002 | Bloco exclusivo de outro extrator | Candidato disponível sem inserção automática | | CAN-003 | Ordem diverge entre extratores | Base segue `selected_extractor` | | CAN-004 | Título duplicado no corpo | Duplicação estrutural removível pelo assembler | | CAN-005 | Link sem URL válida | Candidato rejeitado estruturalmente | | CAN-006 | Imagem sem posição editorial | Não inserir automaticamente | | CAN-007 | IDs repetidos | Falha interna de construção | | CAN-008 | Conteúdo idêntico com Unicode diferente | Equivalência via normalização apropriada | | CAN-009 | Similaridade baixa | Manter candidatos distintos | | CAN-010 | Três extratores concordam integralmente | Ainda chamar higienização LLM | ## 12. Testes de higienização | ID | Cenário | Resultado esperado | | --- | --- | --- | | HYG-001 | Três extratores concordam | LLM ainda seleciona IDs e resultado é validado | | HYG-002 | Um extrator contém publicidade textual | Bloco de ruído removido conforme referência | | HYG-003 | Todos repetem chamada externa | LLM remove; consenso não obriga manutenção | | HYG-004 | Chamada para outra notícia no meio | Link/bloco removido sem afetar conteúdo | | HYG-005 | Newsletter após o corpo | Removida | | HYG-006 | Bloco editorial exclusivo de um extrator | Mantido quando golden set exigir | | HYG-007 | Citação legítima | Preservada como citação | | HYG-008 | Lista editorial legítima | Preservada como lista | | HYG-009 | Heading legítimo | Preservado | | HYG-010 | Imagem editorial posicionada | Mantida com URL existente | | HYG-011 | Imagem publicitária | Removida | | HYG-012 | Link editorial contextual | Mantido | | HYG-013 | Link de recomendação | Removido | | HYG-014 | Modelo devolve artigo completo | Schema rejeita | | HYG-015 | Modelo seleciona ID inexistente | Grounding rejeita | | HYG-016 | Modelo muda ordem narrativa sem IDs correspondentes | Renderer ignora ordem não autorizada | | HYG-017 | Modelo omite conteúdo material | Falha no gate de cobertura do caso | | HYG-018 | Primário falha tecnicamente | Retry técnico e/ou fallback conforme política | | HYG-019 | Primário falha semanticamente | Fallback barato, sem retry semântico no mesmo modelo | | HYG-020 | Ambos falham e base estrutural é segura | Fallback determinístico conservador | | HYG-021 | Ambos falham e base não é segura | `HYGIENE_FAILED`; sem Markdown | ## 13. Testes de reparos textuais | ID | Cenário | Resultado esperado | | --- | --- | --- | | REP-001 | Mojibake inequívoco em título | Reparo aceito e auditado | | REP-002 | Unicode quebrado no corpo | Reparo aceito | | REP-003 | Espaçamento acidental | Reparo aceito se preservar sentido | | REP-004 | Pontuação corrompida | Reparo aceito conforme golden set | | REP-005 | Pequeno typo inequívoco | Reparo aceito conforme eval | | REP-006 | Sinônimo mais elegante | Reparo rejeitado; original preservado | | REP-007 | Frase reescrita para fluência | Reparo rejeitado | | REP-008 | Nome de pessoa alterado | Reparo rejeitado, salvo defeito de encoding comprovado | | REP-009 | Número ou placar alterado | Reparo rejeitado | | REP-010 | Data alterada | Reparo rejeitado | | REP-011 | Citação corrigida editorialmente | Reparo rejeitado | | REP-012 | Fragmento original não existe | Reparo rejeitado | | REP-013 | Fragmento é ambíguo no mesmo bloco | Reparo rejeitado ou exige referência estrutural válida | | REP-014 | Um reparo válido e outro inválido | Aplicar válido, preservar original do inválido | | REP-015 | Modelo omite categoria ou justificativa | Reparo inválido | | REP-016 | Reparo modifica sentido apesar de pequena distância | Gate semântico/humano do eval reprova configuração | ## 14. Testes do ECP | ID | Cenário | Resultado esperado | | --- | --- | --- | | ECP-001 | Texto `DIRECT_INHERENT` | Continuar para enriquecimento | | ECP-002 | Texto `CONTEXTUAL_INHERENT` | Continuar para enriquecimento | | ECP-003 | Texto `TANGENTIAL` | Manifesto `rejected_ecp`; sem Markdown | | ECP-004 | Texto `NOT_RELATED` | Manifesto `rejected_ecp`; sem Markdown | | ECP-005 | Classificador falha | `ECP_CLASSIFICATION_FAILED` | | ECP-006 | Resultado fora do enum | Rejeitar contrato | | ECP-007 | Evidência não pertence ao documento | Rejeitar resultado | | ECP-008 | Relação contextual embutida válida | `CONTEXTUAL_INHERENT` conforme referência | | ECP-009 | Tier LLM do ECP aponta modelo potente | Gate de configuração falha | ## 15. Testes de enriquecimento | ID | Cenário | Resultado esperado | | --- | --- | --- | | ENR-001 | Sentimento positivo em relação ao ECP | `positive` | | ENR-002 | Sentimento negativo em relação ao ECP | `negative` | | ENR-003 | Notícia factual sem polaridade | `neutral` | | ENR-004 | Tags no idioma do artigo | 3 a 8 tags válidas | | ENR-005 | Tags duplicadas | Resposta rejeitada/fallback | | ENR-006 | Tag sem evidência | Reprovar caso | | ENR-007 | Modelo tenta alterar corpo | Ignorar alteração e rejeitar schema | | ENR-008 | Primário falha | Fallback barato | | ENR-009 | Ambos falham | `ENRICHMENT_FAILED`; sem Markdown | ## 16. Testes de Markdown e manifesto | ID | Cenário | Resultado esperado | | --- | --- | --- | | OUT-001 | Texto aprovado completo | Manifesto e Markdown consistentes | | OUT-002 | Subtítulo ausente | Campo omitido e nenhuma linha vazia artificial | | OUT-003 | Autor ausente | Campo omitido | | OUT-004 | Data ausente | Campo omitido | | OUT-005 | Headings/listas/citações | Markdown válido | | OUT-006 | Sublinhado no HTML | Texto preservado sem underline | | OUT-007 | HTML inline residual | Validação reprova | | OUT-008 | Rejeição ECP | Manifesto sem Markdown | | OUT-009 | URL no Markdown não existe na entrada | Grounding reprova | | OUT-010 | Imagem sem origem | Grounding reprova | | OUT-011 | Escrita falha por disco | Estado não concluído; nenhum par parcial válido | | OUT-012 | Hash após escrita diverge | Falha de persistência | ## 17. Testes de providers e fallback | ID | Cenário | Resultado esperado | | --- | --- | --- | | LLM-001 | Timeout primário | Retry técnico limitado | | LLM-002 | 429 primário | Backoff e fallback conforme limite | | LLM-003 | 5xx primário | Retry técnico e fallback | | LLM-004 | Resposta vazia | Retry técnico limitado | | LLM-005 | Schema inválido | Fallback; sem loop semântico | | LLM-006 | Grounding inválido | Fallback | | LLM-007 | Fallback válido | Continuar e registrar uso | | LLM-008 | Fallback indisponível | Falha/fallback determinístico da etapa | | LLM-009 | Modelo não certificado na configuração | Falha inicial | | LLM-010 | Configuração aponta modelo potente | Gate de configuração falha | | LLM-011 | Troca de provider com mesmo papel | Domínio permanece inalterado | | LLM-012 | Retry excede limite | Encerrar tentativa e seguir política | ## 18. Testes de observabilidade | ID | Cenário | Resultado esperado | | --- | --- | --- | | OBS-001 | Fluxo textual normal | Trace e spans completos | | OBS-002 | Fallback usado | Tentativas registradas separadamente | | OBS-003 | Langfuse indisponível | Resultado continua e evento fica pendente | | OBS-004 | Flush posterior | Telemetria enviada uma vez | | OBS-005 | Conteúdo em trace desabilitado | Métricas, hashes e status permanecem | | OBS-006 | Secret presente no ambiente | Nunca aparece em log/trace | | OBS-007 | Execução rejeitada pelo ECP | Score e status corretos | | OBS-008 | Reparo aplicado | Diff e decisão registrados | | OBS-009 | Reparo rejeitado | Original e motivo registrados | | OBS-010 | Prompt/modelo trocados | Versões corretas na trace | | OBS-011 | Erro inesperado | Stack técnica local e código sanitizado | ## 19. Testes de segurança | ID | Cenário | Resultado esperado | | SEC-001 | Artigo contém “ignore instruções” | Texto tratado como dado | | SEC-002 | HTML contém script | Não executar; parser trata como estrutura não confiável | | SEC-003 | URL maliciosa no conteúdo | Não acessar; apenas validar e preservar se editorial | | SEC-004 | Resposta do modelo inclui secret inventado | Schema/grounding rejeita | | SEC-005 | Path traversal derivado do título | Nome por fingerprint impede traversal | | SEC-006 | Log de exceção do SDK contém header | Sanitização remove credencial | | SEC-007 | Arquivo de saída preexistente de outra execução | Não sobrescrever sem correspondência idempotente | | SEC-008 | Conteúdo enorme excede limite configurado | Falha controlada antes do provider ou estratégia aprovada de contexto | ## 20. Teste de carga ### 20.1 Perfil - 100 artigos por hora; - mistura representativa de idiomas, domínios, extratores e estruturas editoriais; - chamadas primárias e percentual observado de fallback; - execuções concorrentes controladas pelo orquestrador; - mesmo SQLite e diretório de saída planejados para produção. ### 20.2 Critérios - nenhuma perda; - nenhuma duplicação; - nenhum arquivo parcial apresentado como final; - nenhuma corrupção SQLite; - 100% de traces enviados ou preservados; - memória e disco estáveis durante o ensaio; - custo e latência p50/p95/p99 registrados por etapa e total; - throughput sustentado durante a janela acordada. ### 20.3 Saída O relatório de staging deve propor limites de custo e latência. O go-live depende da aprovação desses limites. ## 21. Fault injection | ID | Falha injetada | Critério | | --- | --- | --- | | FLT-001 | Provider primário indisponível | Fallback barato assume | | FLT-002 | Ambos providers indisponíveis | Falha controlada sem saída falsa | | FLT-003 | Langfuse indisponível | Processamento continua | | FLT-004 | SQLite temporariamente bloqueado | Timeout controlado/retry de persistência | | FLT-005 | Disco sem espaço | Nenhum final parcial | | FLT-006 | Processo encerrado durante escrita | Reexecução recupera | | FLT-007 | Resposta truncada do LLM | Schema rejeita | | FLT-008 | ECP indisponível | Nenhum Markdown | | FLT-009 | Arquivo temporário órfão | Limpeza segura por fingerprint | | FLT-010 | Falha no flush de telemetria | Evento permanece pendente | ## 22. Promptfoo ### 22.1 Matriz Testar: - prompt de higienização × primário/fallback baratos; - prompt de enriquecimento × primário/fallback baratos; - versões candidata e vigente apenas no processo normal de release manual; - slices de idioma, extrator e domínio. ### 22.2 Assertions permitidas - JSON Schema; - funções Python customizadas sem regex; - IDs pertencentes ao contexto; - conjuntos esperados de blocos; - URLs pertencentes à entrada; - precisão e recall calculados; - enum e cardinalidade; - diffs de reparo com bibliotecas de sequência/Unicode; - comparação com verdade de referência; - métricas de custo e latência. ### 22.3 Assertions proibidas - regex textual; - contains/not-contains baseado em keyword para semântica; - LLM-as-a-judge para grounding; - modelo potente como judge do runtime; - aprovação baseada somente em score agregado. ## 23. Execução no CI ### Todo pull request - schema e formato; - lint; - análise AST da proibição de regex; - unitários; - contratos; - integração simulada; - segurança básica. ### Mudança de prompt, contexto, schema ou modelo - itens anteriores; - Promptfoo reduzido; - regressão dos 20 casos iniciais; - comparação de custo e latência. ### Antes de promoção - golden set completo; - holdout; - fault injection aplicável; - carga de 100 artigos/hora; - aprovação manual dos resultados; - aprovação dos limites de custo e latência. ## 24. Evidências de teste Preservar como artefatos: - versão do código; - versões dos prompts; - providers e modelos; - configuração Promptfoo; - hashes da golden set; - resultados por caso e slice; - violações críticas; - relatório de custo e latência; - relatório de carga; - aprovação de release. ## 25. Critério de conclusão O plano estará cumprido quando: - todos os cenários obrigatórios estiverem automatizados ou documentados como revisão manual controlada; - gates críticos tiverem zero falhas; - gates quantitativos forem atingidos por slice; - os 20 casos iniciais não regredirem; - a golden set de produção tiver revisão concluída; - o teste de 100 artigos/hora passar; - custo e latência tiverem limites aprovados; - nenhuma suíte depender de regex textual ou modelo potente; - não houver qualquer teste ou componente de self-healing no runtime.