test(qa): add exhaustive 38-scenario test suite covering happy, sad, borderline, LLM fallback, CLI contracts, and multilingual matrix

This commit is contained in:
2026-08-21 11:11:35 -03:00
parent a874b98dac
commit 2cdd3547b2
17 changed files with 6111 additions and 1287 deletions
+69 -59
View File
@@ -1,16 +1,16 @@
# Graph Report - TextNLPClassifierApp (2026-08-21)
## Corpus Check
- 202 files · ~112,197 words
- 203 files · ~114,894 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 1579 nodes · 2030 edges · 168 communities (120 shown, 48 thin omitted)
- Extraction: 96% EXTRACTED · 4% INFERRED · 0% AMBIGUOUS · INFERRED: 73 edges (avg confidence: 0.95)
- 1651 nodes · 2220 edges · 170 communities (122 shown, 48 thin omitted)
- Extraction: 94% EXTRACTED · 6% INFERRED · 0% AMBIGUOUS · INFERRED: 127 edges (avg confidence: 0.95)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `bae14405`
- Built from commit: `a874b98d`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
@@ -58,7 +58,7 @@
- 2. Standard Streams & Exit Codes
- ClassificationResult
- test_adversarial.py
- classifier.py
- LLMFallbackAdapter
- test_convert_article_to_markdown.py
- content_northvolt_de.md
- content_presal_pt.md
@@ -111,7 +111,7 @@
- Extraction Pipeline Checklist: Article Content Multi-Engine Extractor
- parametrize
- main
- ECPSnapshot
- classifier.py
- Feature Specification: Multilingual NLP Entity Inherence Classifier (POC)
- 4. Requisitos Funcionais (FR)
- Tasks: Article Content Multi-Engine Extractor
@@ -130,7 +130,7 @@
- Tasks: Deterministic Article Content Selection
- select_article_extractor
- process_batch
- detect_language
- test_models.py
- test_select_article_extractor.py
- Feature Specification: Deterministic Content Selection
- 2. Entity Descriptions & Fields
@@ -157,10 +157,10 @@
- Specification Quality Checklist: Convert Article JSON to Markdown
- CLI Contract: `convert_article_to_markdown.py`
- 9. Interface CLI
- sample_rss_xml
- get_hl_gl_ceid
- 13. Estratégia de testes
- 6. Contrato de entrada
- LLMFallbackAdapter
- ECPSnapshot
- convert_html_to_markdown
- JSON Schema Contract: Deterministic Article Content Selection
- 5. Escopo
@@ -176,35 +176,37 @@
- InherenceClassifier
- remove_duplicate_initial_h1
- test_normalize_scalar_whitespace_collapsing
- .disambiguate
- test_funnel_cli_subprocess_end_to_end
## God Nodes (most connected - your core abstractions)
1. `ECPSnapshot` - 49 edges
2. `InherenceClassifier` - 36 edges
3. `DecisionCategory` - 35 edges
4. `LLMFallbackAdapter` - 32 edges
5. `ClassificationResult` - 26 edges
1. `ECPSnapshot` - 78 edges
2. `InherenceClassifier` - 62 edges
3. `DecisionCategory` - 62 edges
4. `LLMFallbackAdapter` - 48 edges
5. `ClassificationResult` - 29 edges
6. `select_article_extractor()` - 23 edges
7. `ExtractorName` - 21 edges
8. `PRD — Conversão de artigo JSON para Markdown` - 16 edges
9. `process_batch()` - 15 edges
10. `8. Regras funcionais` - 15 edges
8. `main()` - 20 edges
9. `PRD — Conversão de artigo JSON para Markdown` - 16 edges
10. `process_batch()` - 15 edges
## Surprising Connections (you probably didn't know these)
- `main()` --uses--> `ECPSnapshot` [INFERRED]
classify.py → src/models.py
- `test_extract_google_news_orchestration_mocked()` --uses--> `ExtractionResult` [INFERRED]
- `main()` --uses--> `ErrorCode` [INFERRED]
classify.py → src/models.py
- `test_e2e_extract_google_news_live_pipeline()` --uses--> `ExtractionResult` [INFERRED]
tests/test_extract_google_news.py → scripts/extract_google_news.py
- `test_llm_adapter_interface()` --calls--> `LLMFallbackAdapter` [EXTRACTED]
tests/test_adapters.py → src/adapters/llm.py
- `classifier()` --uses--> `InherenceClassifier` [INFERRED]
tests/test_benchmark_24.py → src/classifier.py
- `test_adversarial_apple_fruit_recipe()` --uses--> `DecisionCategory` [INFERRED]
tests/test_adversarial.py → src/models.py
## Import Cycles
- None detected.
## Communities (168 total, 48 thin omitted)
## Communities (170 total, 48 thin omitted)
### Community 0 - "Task Planning"
Cohesion: 0.07
@@ -348,15 +350,15 @@ Nodes (6): 1.1 Arguments & Options, 1. Command Line Interface, 2.1 Exit Codes, 2
### Community 45 - "ClassificationResult"
Cohesion: 0.10
Nodes (20): ABC, BaseNLPAdapter, Base abstract adapter interface for optional Tier 2 / Tier 3 NLP enhancers., Abstract interface for pluggable NLP classification adapters., Return True if the underlying provider or model is installed and configured., Compute semantic similarity score between text and a set of candidate terms., Optionally refine an ambiguous classification result., LocalEmbeddingsAdapter (+12 more)
Nodes (19): ABC, BaseNLPAdapter, Base abstract adapter interface for optional Tier 2 / Tier 3 NLP enhancers., Abstract interface for pluggable NLP classification adapters., Return True if the underlying provider or model is installed and configured., Compute semantic similarity score between text and a set of candidate terms., Optionally refine an ambiguous classification result., LocalEmbeddingsAdapter (+11 more)
### Community 46 - "test_adversarial.py"
Cohesion: 0.11
Nodes (19): RelatedEntity, Adversarial and robustness test suite for Multilingual NLP Entity Inherence…, Run CLI via subprocess without --output and verify stdout is pure parseable…, Run CLI via subprocess with empty content and verify error code and exit code., Run CLI via subprocess with missing target_name and verify error payload., Run CLI via subprocess with corrupted JSON and verify error payload., Content about apple fruit/culinary recipe against Apple Inc. tech entity., High-weight related entity mentioned in passing without required domain anchors. (+11 more)
Cohesion: 0.10
Nodes (21): RelatedEntity, Adversarial and robustness test suite for Multilingual NLP Entity Inherence…, Run CLI via subprocess without --output and verify stdout is pure parseable…, Run CLI via subprocess with empty content and verify error code and exit code., Content about city/state governance of São Paulo against ECP for São Paulo FC., Run CLI via subprocess with missing target_name and verify error payload., Run CLI via subprocess with corrupted JSON and verify error payload., High-weight related entity mentioned in passing without required domain anchors. (+13 more)
### Community 47 - "classifier.py"
Cohesion: 0.16
Nodes (15): count_phrase_occurrences(), match_phrase_in_text(), Core deterministic classification engine (Tier 1 core)., Check if a normalized phrase appears in normalized text with word boundary…, Count occurrences of a phrase in text., Classify inherence of content against an ECP snapshot., extract_evidence_snippets(), extract_sentences() (+7 more)
### Community 47 - "LLMFallbackAdapter"
Cohesion: 0.06
Nodes (43): LLMFallbackAdapter, Optional adapter for LLM fallback boundary disambiguation., Any, parametrize, Suíte de Testes Exaustiva para o Classificador de Inerência (classify.py e…, Cenário 4.1: Caso ambíguo elevado para DIRECT_INHERENT pelo LLM., Cenário 4.2: Caso ambíguo elevado para CONTEXTUAL_INHERENT pelo LLM., Cenário 4.3: LLM confirma categoricamente que a menção é periférica /… (+35 more)
### Community 48 - "test_convert_article_to_markdown.py"
Cohesion: 0.08
@@ -371,16 +373,16 @@ Cohesion: 0.08
Nodes (24): 1. Visão geral (arquitetura), 2.1 DTO de entrada (`googlenews_etl/application/dtos/extract_news_dto.py`), 2.2 Value Object de validação (`googlenews_etl/domain/entities/search_query.py`), 2. Entrada, 3.1 O caso de uso (`googlenews_etl/application/use_cases/extract_news_use_case.py`), 3.2 A porta (`googlenews_etl/domain/ports/news_extractor_port.py`), 3.3.1 Inicialização: sessão HTTP com impersonação de browser, 3.3.2 Mapeamento idioma → parâmetros `hl`/`gl` (`_get_hl_gl`) (+16 more)
### Community 82 - "extract_google_news.py"
Cohesion: 0.15
Nodes (18): extract_google_news(), _fetch_rss_content(), get_hl_gl_ceid(), NewsArticle, _normalize_text_for_comparison(), parse_google_news_rss(), Mapeia idioma e locale para os parâmetros hl, gl e ceid do Google News., Remove pontuação e espaços extras para comparação de redundância. (+10 more)
Cohesion: 0.20
Nodes (14): extract_google_news(), _fetch_rss_content(), NewsArticle, _normalize_text_for_comparison(), parse_google_news_rss(), Remove pontuação e espaços extras para comparação de redundância., Parseia o XML do RSS do Google News e extrai os itens estruturados., Resolve em paralelo as URLs intermediárias do Google News para os links finais… (+6 more)
### Community 83 - "ExtractionResult"
Cohesion: 0.29
Nodes (5): ExtractionResult, Any, Resultado consolidado da extração., Valida E2E o fluxo completo de busca, parsing e resolução de URLs reais ao vivo., test_e2e_extract_google_news_live_pipeline()
Nodes (5): ExtractionResult, Any, Resultado consolidado da extração., Valida a consolidação do ExtractionResult a partir da busca mockada com URLs…, test_extract_google_news_orchestration_mocked()
### Community 84 - "test_extract_google_news.py"
Cohesion: 0.15
Nodes (15): Resolve a URL intermediária do Google News para a URL real do veículo., resolve_article_url(), Testes unitários e de integração para o Extrator de Manchetes do Google News.…, Valida fallback gracioso de URL quando não é link do Google News ou em erro., Valida resolução bem-sucedida de URL do Google News para o portal destino., Valida E2E que o decodificador resolve uma URL real do Google News para o…, Valida o mapeamento padrão de idiomas para pares (hl, gl, ceid)., Valida a sobrescrita geográfica quando o argumento locale é especificado. (+7 more)
Cohesion: 0.16
Nodes (14): Resolve a URL intermediária do Google News para a URL real do veículo., resolve_article_url(), fixture, Testes unitários e de integração para o Extrator de Manchetes do Google News.…, Valida o parsing do feed RSS, higienização de tags HTML e deduplicação., Valida fallback gracioso de URL quando não é link do Google News ou em erro., Valida resolução bem-sucedida de URL do Google News para o portal destino., Valida E2E que o decodificador resolve uma URL real do Google News para o… (+6 more)
### Community 85 - "Implementation Tasks: Google News Headlines Extractor"
Cohesion: 0.14
@@ -400,7 +402,7 @@ Nodes (7): Architecture & Pipeline, Documentation (this feature), Implementation
### Community 90 - "SearchQuery"
Cohesion: 0.20
Nodes (6): Value Object com parâmetros de busca validados., SearchQuery, Valida a consolidação do ExtractionResult a partir da busca mockada com URLs…, Valida as regras de negócio e limites de SearchQuery., test_extract_google_news_orchestration_mocked(), test_search_query_validation()
Nodes (6): Value Object com parâmetros de busca validados., SearchQuery, Valida E2E o fluxo completo de busca, parsing e resolução de URLs reais ao vivo., Valida as regras de negócio e limites de SearchQuery., test_e2e_extract_google_news_live_pipeline(), test_search_query_validation()
### Community 91 - "1. Technical Decisions & Tradeoffs"
Cohesion: 0.25
@@ -435,12 +437,12 @@ Cohesion: 0.22
Nodes (9): parametrize, Garante aceitação de URLs absolutas com esquema HTTP e HTTPS válidos., Garante rejeição de esquemas não permitidos, URLs relativas e strings vazias., Garante que a ausência de corpo no extrator selecionado NUNCA faça fallback…, Garante que todos os placeholders documentados no PRD sejam descartados…, test_normalize_scalar_placeholders_discarded(), test_resolve_article_body_strict_isolation_all_extractors(), test_validate_url_invalid_schemes() (+1 more)
### Community 100 - "main"
Cohesion: 0.17
Nodes (15): emit_error(), main(), parse_args(), Namespace, ClassificationError, ErrorCode, Enum, str (+7 more)
Cohesion: 0.14
Nodes (20): main(), Path, Cenário 6.1: Caminho de ECP inexistente -> Exit Code 1, error_code:…, Cenário 6.2: Arquivo ECP com sintaxe JSON corrompida., Cenário 6.3: Valida erro para falta de cada um dos campos obrigatórios do ECP., Cenário 6.4: Caminho de arquivo Markdown inexistente., Cenário 6.5: Arquivo Markdown vazio ou contendo apenas espaços em branco., Cenário 6.6: A flag -o / --output cria diretórios aninhados automaticamente. (+12 more)
### Community 101 - "ECPSnapshot"
Cohesion: 0.18
Nodes (12): ECPSnapshot, Any, classifier(), fixture, parametrize, Controlled 24-case benchmark suite for Multilingual NLP Entity Inherence…, test_benchmark_case(), Unit tests for ECP models, schema validation, and structured error handling. (+4 more)
### Community 101 - "classifier.py"
Cohesion: 0.17
Nodes (12): emit_error(), parse_args(), Namespace, Core deterministic classification engine (Tier 1 core)., ErrorCode, MatchedGraphEntity, Enum, str (+4 more)
### Community 102 - "Feature Specification: Multilingual NLP Entity Inherence Classifier (POC)"
Cohesion: 0.14
@@ -514,13 +516,13 @@ Nodes (35): CandidateStatus, extract_candidate_data(), ExtractorName, Any, Enum,
Cohesion: 0.11
Nodes (24): atomic_save_json(), process_batch(), Path, Salva dados em JSON de forma atômica utilizando arquivo temporário e rename., Lê o JSON de entrada, valida a estrutura, processa todos os artigos e grava o…, Path, CT-012: A entrada já contém selected_extractor -> Recalcular e substituir…, CT-013: articles está vazio -> Gerar saída válida com articles vazio. (+16 more)
### Community 121 - "detect_language"
Cohesion: 0.19
Nodes (16): detect_language(), extract_words(), normalize_text(), Lightweight multilingual language detection and text normalization., Normalize text by converting to lowercase and stripping combining diacritical…, Tokenize text into lowercase alphanumeric words., Detect the ISO-639-1 language code of text among supported languages (pt, en,…, Unit tests for language detection and text normalization. (+8 more)
### Community 121 - "test_models.py"
Cohesion: 0.07
Nodes (37): count_phrase_occurrences(), match_phrase_in_text(), Check if a normalized phrase appears in normalized text with word boundary…, Count occurrences of a phrase in text., Classify inherence of content against an ECP snapshot., detect_language(), extract_words(), normalize_text() (+29 more)
### Community 122 - "test_select_article_extractor.py"
Cohesion: 0.18
Nodes (16): generate_shingles(), normalize_text(), Executa a normalização determinística para comparação: 1. Decodificar entidades…, Gera conjunto de shingles ordenados de tamanho window_size (padrão 5). - Se…, Suíte de Testes Automatizados para o Seletor Determinístico de Extrator. Cobre…, Garante que marcação de imagem Markdown ![alt](url) seja descartada e link…, E2E: Executa CLI com arquivo inexistente e valida código 1 e mensagem no stderr., test_e2e_cli_subprocess_missing_file() (+8 more)
Nodes (16): generate_shingles(), normalize_text(), Executa a normalização determinística para comparação: 1. Decodificar entidades…, Gera conjunto de shingles ordenados de tamanho window_size (padrão 5). - Se…, Suíte de Testes Automatizados para o Seletor Determinístico de Extrator. Cobre…, Garante que marcação de imagem Markdown ![alt](url) seja descartada e link…, E2E: Executa scripts/select_article_extractor.py como subprocesso real na linha…, test_e2e_cli_subprocess_real_execution() (+8 more)
### Community 123 - "Feature Specification: Deterministic Content Selection"
Cohesion: 0.17
@@ -622,9 +624,9 @@ Nodes (5): 1. Script Signature, 2. Command-Line Arguments, 3. Exit Codes, 4. Sta
Cohesion: 0.40
Nodes (5): 9.1 Script, 9.2 Argumentos, 9.3 Exemplos, 9.4 Saída do processo, 9. Interface CLI
### Community 149 - "sample_rss_xml"
Cohesion: 0.67
Nodes (3): fixture, Fixture que fornece o conteúdo do XML de exemplo para testes offline., sample_rss_xml()
### Community 149 - "get_hl_gl_ceid"
Cohesion: 0.25
Nodes (8): get_hl_gl_ceid(), Mapeia idioma e locale para os parâmetros hl, gl e ceid do Google News., Valida o mapeamento padrão de idiomas para pares (hl, gl, ceid)., Valida a sobrescrita geográfica quando o argumento locale é especificado., Valida fallback dinâmico para idiomas regionais não listados explicitamente., test_get_hl_gl_ceid_default_mappings(), test_get_hl_gl_ceid_dynamic_fallback(), test_get_hl_gl_ceid_with_custom_locale()
### Community 150 - "13. Estratégia de testes"
Cohesion: 0.50
@@ -634,9 +636,9 @@ Nodes (4): 13.1 Testes unitários, 13.2 Testes de integração do CLI, 13.3 Caso
Cohesion: 0.50
Nodes (4): 6.1 Formato, 6.2 Valores aceitos para `selected_extractor`, 6.3 Campos obrigatórios após a resolução, 6. Contrato de entrada
### Community 152 - "LLMFallbackAdapter"
Cohesion: 0.08
Nodes (26): LLMFallbackAdapter, Executes LLM fallback for ambiguous boundary cases. Returns a refined…, Parses and validates structured JSON response from LLM., Optional adapter for LLM fallback boundary disambiguation., Returns True if an API key or custom provider function is configured., Constructs an expert-engineered prompt for multilingual entity inherence…, Any, Suíte de Testes para o Adaptador de Fallback para LLM (Tier 3) do Classificador… (+18 more)
### Community 152 - "ECPSnapshot"
Cohesion: 0.13
Nodes (20): ECPSnapshot, parametrize, test_benchmark_case(), Suíte de Testes para o Adaptador de Fallback para LLM (Tier 3) do Classificador…, Valida extração de JSON quando a resposta do LLM vem formatada em bloco…, Valida que respostas corrompidas ou JSONs sem campos obrigatórios retornem None…, Garante que o classificador dispare o Tier 3 LLM para casos ambíguos…, Garante que casos claros (alta confiança e alta densidade de âncoras) NÃO… (+12 more)
### Community 153 - "convert_html_to_markdown"
Cohesion: 0.33
@@ -651,13 +653,21 @@ Cohesion: 0.67
Nodes (3): 5.1 Incluído, 5.2 Fora do escopo, 5. Escopo
### Community 165 - "InherenceClassifier"
Cohesion: 0.11
Nodes (30): InherenceClassifier, Tier 1 Deterministic NLP Entity Inherence Classifier with optional Tier 2 /…, DecisionCategory, Content about city/state governance of São Paulo against ECP for São Paulo FC., test_adversarial_sao_paulo_city_vs_fc(), Unit tests for deterministic classification decision logic., test_contextual_inherent(), test_direct_inherent() (+22 more)
Cohesion: 0.07
Nodes (45): InherenceClassifier, Tier 1 Deterministic NLP Entity Inherence Classifier with optional Tier 2 /…, DecisionCategory, Content about apple fruit/culinary recipe against Apple Inc. tech entity., test_adversarial_apple_fruit_recipe(), Unit tests for deterministic classification decision logic., test_contextual_inherent(), test_direct_inherent() (+37 more)
### Community 166 - "remove_duplicate_initial_h1"
Cohesion: 0.50
Nodes (4): Remove o primeiro título H1 do corpo somente quando ele for igual ao título…, remove_duplicate_initial_h1(), Testa remoção de H1 inicial coincidente com título com variações de espaços e…, test_remove_duplicate_initial_h1_exact_and_variations()
### Community 168 - ".disambiguate"
Cohesion: 0.25
Nodes (4): Executes LLM fallback for ambiguous boundary cases. Returns a refined…, Parses and validates structured JSON response from LLM., Returns True if an API key or custom provider function is configured., Constructs an expert-engineered prompt for multilingual entity inherence…
### Community 169 - "test_funnel_cli_subprocess_end_to_end"
Cohesion: 0.67
Nodes (3): Path, Valida o contrato CLI completo classify.py com saída em arquivo JSON e flags…, test_funnel_cli_subprocess_end_to_end()
## Knowledge Gaps
- **696 isolated node(s):** `text-nlp-classifier`, `MatchedGraphEntity`, `graphify`, `Usage`, `What graphify is for` (+691 more)
These have ≤1 connection - possible missing edges or undocumented components.
@@ -666,17 +676,17 @@ Nodes (4): Remove o primeiro título H1 do corpo somente quando ele for igual ao
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `ECPSnapshot` connect `ECPSnapshot` to `main`, `classifier.py`, `InherenceClassifier`, `.disambiguate`, `ClassificationResult`, `test_adversarial.py`, `LLMFallbackAdapter`, `test_models.py`?**
_High betweenness centrality (0.009) - this node is a cross-community bridge._
- **Why does `PRD — Conversão de artigo JSON para Markdown` connect `PRD — Conversão de artigo JSON para Markdown` to `8. Regras funcionais`, `12. Critérios de aceite`, `11. Requisitos não funcionais`, `9. Interface CLI`, `13. Estratégia de testes`, `6. Contrato de entrada`, `5. Escopo`?**
_High betweenness centrality (0.006) - this node is a cross-community bridge._
- **Why does `Tasks: Convert Article JSON to Markdown` connect `Tasks: Convert Article JSON to Markdown` to `005-convert-json-markdown/plan.md`?**
_High betweenness centrality (0.004) - this node is a cross-community bridge._
- **Why does `ECPSnapshot` connect `ECPSnapshot` to `main`, `InherenceClassifier`, `ClassificationResult`, `test_adversarial.py`, `classifier.py`, `LLMFallbackAdapter`?**
- **Why does `InherenceClassifier` connect `InherenceClassifier` to `main`, `classifier.py`, `ClassificationResult`, `test_adversarial.py`, `LLMFallbackAdapter`, `ECPSnapshot`, `test_models.py`?**
_High betweenness centrality (0.004) - this node is a cross-community bridge._
- **Are the 16 inferred relationships involving `ECPSnapshot` (e.g. with `main()` and `BaseNLPAdapter`) actually correct?**
_`ECPSnapshot` has 16 INFERRED edges - model-reasoned connections that need verification._
- **Are the 7 inferred relationships involving `InherenceClassifier` (e.g. with `LocalEmbeddingsAdapter` and `LLMFallbackAdapter`) actually correct?**
_`InherenceClassifier` has 7 INFERRED edges - model-reasoned connections that need verification._
- **Are the 24 inferred relationships involving `DecisionCategory` (e.g. with `LLMFallbackAdapter` and `InherenceClassifier`) actually correct?**
_`DecisionCategory` has 24 INFERRED edges - model-reasoned connections that need verification._
- **Are the 42 inferred relationships involving `ECPSnapshot` (e.g. with `main()` and `BaseNLPAdapter`) actually correct?**
_`ECPSnapshot` has 42 INFERRED edges - model-reasoned connections that need verification._
- **Are the 8 inferred relationships involving `InherenceClassifier` (e.g. with `LocalEmbeddingsAdapter` and `LLMFallbackAdapter`) actually correct?**
_`InherenceClassifier` has 8 INFERRED edges - model-reasoned connections that need verification._
- **Are the 50 inferred relationships involving `DecisionCategory` (e.g. with `LLMFallbackAdapter` and `InherenceClassifier`) actually correct?**
_`DecisionCategory` has 50 INFERRED edges - model-reasoned connections that need verification._
- **Are the 4 inferred relationships involving `LLMFallbackAdapter` (e.g. with `ClassificationResult` and `DecisionCategory`) actually correct?**
_`LLMFallbackAdapter` has 4 INFERRED edges - model-reasoned connections that need verification._