# Feature Specification: Classificação e Roteamento de Notícias Predominantemente de Mídia **Feature Branch**: `007-media-article-routing` **Created**: 2026-08-24 **Status**: Draft **Input**: User description: "Classificação e Roteamento de Notícias Predominantemente de Mídia no script scripts/extract_article_contents.py conforme docs/prd_extrator_artigo_media (prd.md, adr_001.md, adr_002.md)" --- ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Roteamento Exclusivo de Notícias Predominantemente de Mídia (Priority: P1) Como operador do pipeline de notícias, quero que publicações jornalísticas cujo conteúdo informativo principal seja uma mídia (vídeo, imagem única, coleção/múltiplas imagens, post incorporado/embed ou combinação mista de mídias) e cujo texto atue apenas como introdução, legenda, contextualização ou breve descrição dessa mídia sejam identificadas logo após o crawl e salvas em um arquivo JSON próprio (`*_media.json`), sem passar pelo pipeline multimotor textual (Trafilatura, Newspaper4k e Readability). **Why this priority**: É o objetivo central da funcionalidade: evitar processamento desnecessário de páginas não textuais pelo multimotor e separar fisicamente as publicações de mídia dos artigos textuais. **Independent Test**: Pode ser testado de forma isolada submetendo URLs cujas páginas possuam mídia predominante e texto meramente descritivo/introdutório. O sistema deve gerar o arquivo `*_media.json` com os registros correspondentes e seus metadados mínimos de rastreabilidade, sem disparar qualquer chamada aos três extratores textuais. **Acceptance Scenarios**: 1. **Cenário B (Vídeo)**: **Given** uma página carregada contendo um vídeo e texto curto apenas introdutório/contextual, **When** a detecção estrutural e o classificador processam a publicação, **Then** o classificador retorna `content_type = "media"` e `media_type = "video"`, o multimotor não é executado e o artigo é gravado em `*_media.json`. 2. **Cenário C (Imagem Única)**: **Given** uma página carregada contendo uma única imagem e texto curto descritivo, **When** o classificador processa a publicação, **Then** o classificador retorna `content_type = "media"` e `media_type = "image"`, o multimotor não é executado e o artigo é gravado em `*_media.json`. 3. **Cenário D (Múltiplas Imagens)**: **Given** uma página carregada contendo múltiplas imagens (em galeria, carousel, slideshow ou sequência vertical) e texto curto, **When** o classificador processa a publicação, **Then** o classificador retorna `content_type = "media"` e `media_type = "images"`, o multimotor não é executado e o artigo é gravado em `*_media.json`. 4. **Cenário E (Conteúdo Incorporado / Embed)**: **Given** uma página carregada contendo um post incorporado (ex: Instagram, TikTok, X) e breve contextualização textual, **When** o classificador processa a publicação, **Then** o classificador retorna `content_type = "media"` e `media_type = "embed"`, o multimotor não é executado e o artigo é gravado em `*_media.json`. 5. **Cenário F (Mídia Mista)**: **Given** uma página carregada contendo mais de uma categoria relevante de mídia (ex: vídeo e imagens) com texto meramente introdutório, **When** o classificador processa a publicação, **Then** o classificador retorna `content_type = "media"` e `media_type = "mixed"`, o multimotor não é executado e o artigo é gravado em `*_media.json`. --- ### User Story 2 - Roteamento Direto e Preservação de Notícias Textuais (Priority: P2) Como operador do pipeline textual, quero que notícias cujo conteúdo principal seja texto jornalístico substancial (mesmo que acompanhadas de fotos ilustrativas, vídeos ou infográficos) ou páginas sem qualquer elemento estrutural de mídia continuem sendo processadas normalmente pelos três motores de extração (`extract_all_engines`) e salvas no arquivo de saída textual (`*_extracted.json` ou caminho informado em `-o/--output`), preservando integralmente o formato de saída atual e a semântica de seus contadores. **Why this priority**: Garante que o pipeline textual original não sofra quebra de contrato, regressão ou perda de dados em matérias jornalísticas informativas normais. **Independent Test**: Pode ser testado submetendo: (a) páginas de texto puro sem mídia, e (b) matérias jornalísticas substanciais de múltiplos parágrafos contendo fotos ou vídeos editoriais. Ambos devem ser encaminhados ao multimotor e consolidados no arquivo de saída textual. **Acceptance Scenarios**: 1. **Cenário A (Texto sem Mídia)**: **Given** uma página carregada sem elementos estruturais candidatos a mídia na DOM, **When** a detecção estrutural prévia é executada, **Then** o classificador LLM não é chamado e o artigo é encaminhado diretamente ao multimotor textual. 2. **Cenário G (Artigo Textual Longo com Imagem)**: **Given** uma notícia com texto jornalístico substancial contendo uma ou mais imagens ilustrativas, **When** o classificador semântico avalia a publicação, **Then** retorna `content_type = "text"` com `media_type = null`, o artigo é processado pelo multimotor e salvo na saída textual. 3. **Cenário H (Artigo Textual Longo com Vídeo)**: **Given** uma notícia com texto jornalístico substancial contendo um vídeo incorporado, **When** o classificador semântico avalia a publicação, **Then** retorna `content_type = "text"` com `media_type = null`, o artigo é processado pelo multimotor e salvo na saída textual. --- ### User Story 3 - Resiliência com Fallback Operacional Sequencial e Registro de Falhas (Priority: P3) Como operador do sistema, quero que falhas puramente operacionais/técnicas no provedor primário local (Ollama) acionem sequencialmente o primeiro fallback (Groq) e, se este também falhar operacionalmente, o segundo fallback (OmniRoute), e que em caso de indisponibilidade de todos os provedores, o erro seja registrado explicitamente no JSON principal de processamento sem interromper o lote. **Why this priority**: Assegura resiliência de produção em execuções de lote sem intervenção manual, mantendo rastreabilidade estrita e isolamento de falhas. **Independent Test**: Pode ser testado simulando deterministicamente falhas operacionais e de contrato (timeout, recusa de conexão, erro HTTP, schema inválido) nos provedores intermediários e validando a transição sequencial e o comportamento de falha total, sem necessidade de chamadas a provedores reais durante a suíte normal de CI. **Acceptance Scenarios**: 1. **Cenário I (Falha no Ollama)**: **Given** o provedor primário (Qwen3.5 2B / Ollama) apresentando falha operacional (timeout, erro HTTP ou conexão recusada), **When** um artigo com mídia candidata é classificado, **Then** o sistema aciona o provedor GPT-OSS 20B / Groq e utiliza sua classificação válida para rotear o artigo. 2. **Cenário J (Falha no Ollama e Groq)**: **Given** Ollama e Groq apresentando falha operacional, **When** o artigo é classificado, **Then** o sistema aciona o provedor `cgpt-web/gpt-5.5` / OmniRoute e utiliza sua classificação válida para rotear o artigo. 3. **Cenário K (Falha Total de Todos os Provedores)**: **Given** Ollama, Groq e OmniRoute apresentando falhas operacionais consecutivas, **When** o artigo é processado, **Then** o sistema atribui `classification_status = "failed"` e mensagem de erro diagnóstica, não assume presunção arbitrária de `text` nem de `media`, não executa o multimotor, não envia o registro para `*_media.json`, registra a falha no arquivo JSON principal de processamento e continua processando os demais artigos do lote. 4. **Cenário L (Resposta fora do Schema)**: **Given** um provedor retornando resposta ilegível, truncada ou incompatível com o contrato estruturado obrigatório, **When** a validação de contrato é executada, **Then** a tentativa é tratada como falha operacional do provedor atual e o sistema avança imediatamente para o próximo provedor na cadeia de fallback. 5. **Cenário M (Suporte Multilíngue)**: **Given** páginas de notícias redigidas em qualquer um dos até 10 idiomas suportados pelo sistema (ex: espanhol, inglês, português, francês, alemão, italiano, etc.), **When** a detecção e a classificação são executadas, **Then** o sistema classifica corretamente textos curtos com mídia como `media` e textos substanciais com mídia como `text`, sem utilizar regras ou prompts específicos por idioma. --- ### Edge Cases - **Texto extremamente curto com uma única imagem (notícia curta com imagem)**: Deve ser deliberadamente classificado como `content_type = "media"` e `media_type = "image"`, pois texto com volume insuficiente não é útil para o pipeline textual. - **Avaliação de brevidade textual ("3 a 5 linhas")**: A referência de 3 a 5 linhas de texto é exclusivamente conceitual. O modelo deve avaliar semanticamente se o texto possui conteúdo jornalístico substancial por si próprio ou se funciona apenas como introdução/descrição da mídia, sem depender de contagem literal de linhas renderizadas, viewport, resolução, CSS, número de palavras ou caracteres. - **Coleções de imagens (galerias / carousels / slideshows / sequência vertical)**: O sistema não deve tentar interagir com o carousel, clicar em botões, avançar slides ou extrair URLs das imagens. Deve apenas identificar estruturalmente a presença de múltiplas imagens e classificar como `media_type = "images"`. - **Conteúdo misto sem precedência artificial**: Quando houver mais de um tipo relevante de mídia atuando como elemento informativo principal (ex: vídeo e galeria de fotos), o sistema deve classificar como `media_type = "mixed"`, sem impor regras artificiais de precedência como `video > image`. - **Falha isolada por artigo**: A falha na classificação ou no processamento de um artigo específico não pode interromper nem abortar a execução do lote. --- ## Requirements *(mandatory)* ### Functional Requirements #### Detecção Estrutural Prévia - **FR-001**: O sistema MUST analisar estruturalmente a DOM carregada pelo crawler antes de qualquer chamada aos motores Trafilatura, Newspaper4k e Readability. - **FR-002**: A análise estrutural da DOM MUST ser realizada exclusivamente via parser HTML/DOM (navegação por nós, tags, atributos e contagem de elementos), sendo estritamente proibido o uso de regex (`re`), listas de palavras-chave ou heurísticas semânticas por idioma em toda a nova implementação. - **FR-003**: Quando nenhuma mídia candidata estiver presente como elemento estruturalmente relevante ao conteúdo da publicação na DOM carregada (sem imagem, vídeo, iframe/embed, object ou estruturas DOM equivalentes associadas à publicação), o artigo MUST seguir diretamente para o pipeline multimotor textual sem chamada ao classificador LLM. - **FR-004**: Quando houver mídia candidata presente e estruturalmente relevante ao conteúdo da publicação na DOM carregada (como imagem, vídeo, iframe/embed, object ou estruturas equivalentes associadas à publicação), o sistema MUST submeter uma representação compacta da publicação ao classificador de conteúdo. A mera presença de mídia em outras regiões da página não associadas ao conteúdo da publicação não deve, isoladamente, tornar o artigo candidato. #### Entrada e Escopo do Classificador - **FR-005**: O classificador MUST receber apenas dados textuais e estruturais compactos já disponíveis na página carregada (título, blocos textuais relevantes, informação estrutural de mídias presentes e quantidade/tipos encontrados), evitando o envio do HTML completo quando a representação compacta contiver a mesma informação. - **FR-006**: O sistema MUST NOT baixar imagens, baixar vídeos, executar OCR, executar visão computacional, assistir vídeos, transcrever áudios, navegar em carousels/slideshows nem chamar APIs externas de plataformas de mídia. #### Contrato Estruturado do Classificador - **FR-007**: A saída emitida pelo classificador LLM MUST conter exclusivamente os campos `content_type` e `media_type` em formato JSON estruturado (sem campos como `provider_used`, `status`, `error_message`, `confidence`, `rationale`, `summary`, `keywords`, `evidence` ou `tradução`). - **FR-008**: O campo `content_type` emitido pelo modelo MUST aceitar exclusivamente os valores `"text"` ou `"media"`. - **FR-009**: O campo `media_type` emitido pelo modelo MUST aceitar exclusivamente os valores `"video"`, `"image"`, `"images"`, `"embed"`, `"mixed"` quando `content_type = "media"`, e MUST ser obrigatoriamente `null` quando `content_type = "text"`. - **FR-010**: O prompt enviado ao classificador MUST ser único, conciso, comum a todos os idiomas e solicitar exclusivamente a classificação necessária (`content_type` e `media_type`). O prompt MUST NOT solicitar reasoning, confidence, rationale, summary, keywords, evidence ou tradução. Reasoning deliberativo adicional não deve ser habilitado quando não for necessário para produzir o contrato estruturado da classificação, e novos campos não devem ser adicionados à resposta. - **FR-011**: Sempre que suportado pelo provedor, a requisição ao modelo MUST utilizar structured output nativo (JSON Schema / response_format). Toda resposta MUST ser estritamente validada contra o contrato antes de ser aceita. #### Roteamento, Persistência e Regras de Arquivos - **FR-012**: Artigos classificados como `content_type = "text"` MUST seguir normalmente pelo fluxo textual existente, executando os três motores de extração (`extract_all_engines`) e sendo salvos no arquivo JSON de saída textual. - **FR-013**: Artigos classificados como `content_type = "media"` MUST NOT executar os motores Trafilatura, Newspaper4k ou Readability. - **FR-014**: Artigos classificados como `content_type = "media"` MUST ser gravados em arquivo JSON de mídia dedicado com sufixo `_media.json`, obedecendo às seguintes regras de nomenclatura e diretório: - **Sem `-o/--output`**: para uma entrada `/.json` (ex: `out/river_plate.json`), a saída textual padrão é `/_extracted.json` (`out/river_plate_extracted.json`) e a saída de mídia é `/_media.json` (`out/river_plate_media.json`). - **Com `-o/--output` customizado**: para uma saída textual informada como `/.json` (ex: `-o out/processados/brasil_completo.json`), a saída de mídia MUST ser gravada em `/_media.json` (`out/processados/brasil_completo_media.json`), utilizando o mesmo diretório e stem do output textual com sufixo `_media` e extensão `.json`. - O arquivo `*_media.json` MUST ser gerado em toda execução bem-sucedida do lote. Quando nenhum artigo for classificado como `content_type = "media"`, o arquivo MUST conter exatamente `{"articles": []}`. O sistema MUST NOT adotar comportamento condicional de omissão do arquivo. - A interface CLI existente MUST permanecer inalterada, sem adição de parâmetros como `--media-output`. - **FR-015**: O arquivo `*_media.json` MUST utilizar um envelope físico mínimo contendo exclusivamente a chave `"articles"`, onde cada elemento representa um registro de `MediaArticle` com seus campos mínimos obrigatórios de identificação e rastreabilidade: ```json { "articles": [ { "input_meta": { "titulo": "...", "url": "..." }, "crawled_url": "...", "page_title": "...", "http_status": 200, "content_type": "media", "media_type": "video" } ] } ``` Quando nenhum artigo de mídia for identificado no lote, o envelope MUST ser emitido exatamente como `{"articles": []}`. `input_meta` preserva os metadados recebidos da entrada (onde `titulo` e `url` são obrigatórios, e `subtitulo`, `quando_publicado`, `pagina` e demais metadados existentes continuam opcionais conforme a entrada). O envelope de mídia MUST NOT conter `total_articles`, `successful_articles`, `failed_articles`, contadores por `media_type`, status agregados, modelo de relatório `MediaBatchReport` ou métricas duplicadas (as métricas operacionais são expostas exclusivamente via logging/resumo de execução conforme FR-028). - **FR-016**: O sistema MUST NOT adicionar em `*_media.json` campos de extração aprofundada de mídia como: `image_urls`, `video_urls`, `embed_urls`, `thumbnails`, `duration`, `captions`, `transcripts`, `OCR`, `alt-text gerado`, `descrição da imagem`, `provider da mídia`, `metadata da mídia`, `summary`, `keywords` ou `sentiment`. - **FR-017**: O fluxo textual existente (`*_extracted.json` ou arquivo informado em `-o/--output`) MUST preservar o envelope de saída existente (`source_file`, `processed_at`, `total_articles`, `successful_articles`, `failed_articles`, `articles`). Como os artigos de mídia são desviados para `*_media.json` e não permanecem no array `articles` do JSON principal, a semântica dos contadores MUST representar estritamente os registros presentes no arquivo JSON principal: - `total_articles`: quantidade total de registros presentes no array `articles` do JSON principal; - `successful_articles`: quantidade de artigos textuais processados com sucesso pelo multimotor e presentes no JSON principal; - `failed_articles`: quantidade de registros de falha presentes no JSON principal (incluindo falhas do crawl ou com `classification_status = "failed"`). O JSON principal MUST NOT conter campos adicionais como `media_articles`, contadores de artigos desviados, referências ao `*_media.json` ou novos campos agregados de relatório. #### Cadeia Sequencial de Provedores e Fallback Operacional - **FR-018**: O sistema MUST utilizar Qwen3.5 2B executado localmente via Ollama como classificador primário determinístico. - **FR-019**: O sistema MUST acionar o primeiro fallback operacional (GPT-OSS 20B via Groq) estritamente em caso de falha operacional do Ollama (timeout, erro HTTP, indisponibilidade ou resposta fora do schema). - **FR-020**: O sistema MUST acionar o segundo fallback operacional (`cgpt-web/gpt-5.5` via OmniRoute) estritamente em caso de falha operacional cumulativa do Ollama e do Groq. - **FR-021**: Os provedores de classificação MUST ser executados de forma estritamente sequencial (uma resposta válida encerra imediatamente a cadeia). É expressamente proibida a execução paralela, votação entre modelos, consenso, segunda opinião ou fallback condicionado a valor de classificação ou nível de confiança. - **FR-022**: Em caso de falha operacional dos três provedores (Ollama, Groq e OmniRoute), o artigo MUST receber `classification_status = "failed"` e mensagem diagnóstica de erro sem exposição de credenciais, MUST permanecer registrado no JSON principal de processamento, MUST NOT ser enviado ao arquivo de mídia, MUST NOT executar o multimotor e MUST NOT ter classificação presumida como `text` ou `media`. A falha não deve interromper os demais artigos do lote. - **FR-023**: O sistema MUST NOT criar arquivos físicos adicionais exclusivos para falhas (ex: `*_failed.json`), filas de mensagens, DLQ ou serviços assíncronos de recuperação. - **FR-024**: A cadeia de provedores de classificação MUST ser testável de forma determinística sem depender de chamadas reais aos provedores externos durante a suíte normal automatizada (CI). Devem ser simuláveis/testáveis, no mínimo: (1) Ollama success; (2) Ollama fail → Groq success; (3) Ollama fail → Groq fail → OmniRoute success; (4) todos falham; e (5) provedor retorna resposta fora do schema. Não é exigida presença de Ollama, Groq ou OmniRoute reais na execução regular da suíte de testes. #### Segurança, Configuração Externa e Observabilidade - **FR-025**: Endpoints, nomes de modelos, credenciais de API e timeouts dos provedores MUST ser configuráveis externamente ao código-fonte, não sendo permitidos segredos hardcoded. - **FR-026**: Logs, saídas JSON e mensagens de erro persistidas MUST NOT registrar API keys, tokens de autenticação ou credenciais. - **FR-027**: O sistema MUST registrar informações operacionais estruturadas nos logs existentes para identificar: provedor utilizado, acionamento do fallback Ollama → Groq, acionamento do fallback Groq → OmniRoute, classificação final obtida e eventual falha total da cadeia, sem registrar o payload textual integral do artigo por padrão. - **FR-028**: O sistema MUST contabilizar e expor nos mecanismos de logging/resumo de execução existentes as seguintes métricas operacionais obrigatórias: - total de artigos avaliados; - total roteado para `text`; - total roteado para `media`; - total `media/video`; - total `media/image`; - total `media/images`; - total `media/embed`; - total `media/mixed`; - quantidade de acionamentos do fallback Groq; - quantidade de acionamentos do fallback OmniRoute; - total de falhas de classificação. - **FR-029**: O classificador e a detecção estrutural MUST funcionar de forma independente de idioma, cobrindo com precisão os até 10 idiomas processados pelo sistema. - **FR-030**: O script `scripts/extract_article_contents.py` MUST manter compatibilidade com sua interface de linha de comando (`-i/--input`, `-o/--output`, `-l/--limit`, `--lang/--language`, `-t/--timeout`, `-s/--silent`). --- ### Key Entities - **InputArticle**: Metadados originais da notícia de entrada carregados do arquivo de busca (`titulo` e `url` obrigatórios; `subtitulo`, `quando_publicado`, `pagina` e eventuais campos extras preservados conforme a entrada). - **MediaArticle**: Registro consolidado de publicação predominantemente de mídia gravado no array `articles` de `*_media.json`, contendo os campos mínimos obrigatórios de rastreabilidade (`input_meta`, `crawled_url`, `page_title`, `http_status`, `content_type` e `media_type`). - **ExtractedArticle**: Registro consolidado de artigo textual efetivamente processado pelos motores de extração multimotor (`extract_all_engines`) e gravado no array `articles` do JSON principal de saída textual, contendo `input_meta`, `extraction_status`, `error_message`, `crawled_url`, `page_title`, `http_status`, `trafilatura`, `newspaper4k` e `readability`. - **ExtractionBatchReport**: Relatório consolidado do lote de extração textual gravado no arquivo JSON principal de saída contendo os contadores restritos aos itens presentes nesse arquivo (`total_articles`, `successful_articles`, `failed_articles`) e a lista `articles`. --- ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001**: 100% das publicações identificadas como predominantemente de mídia (`video`, `image`, `images`, `embed`, `mixed`) são desviadas antes da execução dos motores Trafilatura, Newspaper4k e Readability. - **SC-002**: 100% das publicações de mídia identificadas são gravadas no arquivo `*_media.json` dentro do envelope `articles` contendo os campos mínimos obrigatórios definidos no contrato de rastreabilidade (`input_meta`, `crawled_url`, `page_title`, `http_status`, `content_type`, `media_type`). - **SC-003**: 0% de alteração de contrato ou degradação de campos nos arquivos JSON de saída textual para artigos normais processados com sucesso, com contadores refletindo estritamente os itens presentes no arquivo. - **SC-004**: 100% das falhas operacionais intermediárias em provedores LLM transitam de forma determinística e sequencial pela cadeia (Ollama → Groq → OmniRoute) sem interrupção manual ou falha espúria. - **SC-005**: 100% das tentativas com falha cumulativa dos três provedores resultam em registro de erro explícito (`classification_status = "failed"`) no arquivo JSON principal de processamento sem parar o processamento dos demais itens do lote. - **SC-006**: 100% dos cenários mínimos e requisitos funcionais obrigatórios possuem cobertura em suíte de testes automatizados (unitários e de integração), contemplando páginas sem mídia, mídias individuais, mídias mistas, fallbacks operacionais simulados deterministicamente e artigos em todos os idiomas suportados pelo sistema. - **SC-007**: 0% de uso de bibliotecas ou chamadas de expressões regulares (`re`) em toda a nova lógica de detecção estrutural e classificação de mídia. - **SC-008**: 0 ocorrências de credenciais, API keys ou tokens expostos em logs, mensagens de erro ou saídas JSON. --- ## Assumptions - **Configuração de Provedores**: O ambiente de produção deve possuir configuração para o runtime local do Ollama (Qwen3.5 2B) e credenciais para os provedores de fallback Groq (GPT-OSS 20B) e OmniRoute (`cgpt-web/gpt-5.5`). A indisponibilidade operacional de um provedor durante a execução aciona sequencialmente o fallback definido. - **Isolamento de Responsabilidade da Mídia**: O download, visão computacional, OCR, transcrição e indexação da mídia são responsabilidades exclusivas de sistemas ou etapas downstream, não fazendo parte desta funcionalidade. - **Decisão Semântica de Conteúdo**: A distinção entre texto substancial e texto curto introdutório/contextual é uma avaliação semântica realizada pelo modelo de linguagem compacto, não sendo calculada por contagem de linhas visuais renderizadas ou regras lexicais. - **Isolamento de Falha**: A falha operacional na classificação de uma notícia isolada não invalida nem interrompe a execução dos demais artigos presentes no mesmo lote. --- ## Out of Scope *(Explicitamente Fora de Escopo)* Para evitar overengineering e manter a solução estritamente aderente aos princípios obrigatórios, os seguintes itens estão **explicitamente fora de escopo**: - Download de imagens, vídeos ou arquivos binários de mídia; - Extração de URLs internas de imagens, vídeos ou embeds; - OCR (Reconhecimento Óptico de Caracteres) ou visão computacional; - Transcrição de áudio ou vídeo; - Identificação de objetos, fotografias, infográficos ou ilustrações; - Geração de descrições, resumos, palavras-chave ou análise de sentimento; - Interação com carousels, slideshows ou navegação em galerias; - Chamadas a APIs externas dos provedores das mídias (Instagram, TikTok, X, etc.); - Detecção, classificação ou filtragem de publicidade / anúncios; - Armazenamento em banco de dados nesta feature (nenhum banco); - Criação de qualquer worker novo (nenhum worker novo); - Criação de pipeline adicional de NLP (nenhum pipeline NLP adicional); - Criação ou introdução de nova plataforma de observabilidade (nenhuma nova plataforma de observabilidade); - Criação de novos serviços, microserviços ou processos autônomos; - Introdução de filas de mensagens, DLQ ou mecanismos adicionais de retry em fila; - Uso de frameworks de agentes, LangChain, LangGraph ou LLM-as-a-judge; - Votação entre modelos, scoring complexo ou consenso de LLMs; - Classificação baseada em regex ou dicionários de palavras-chave por idioma; - Criação de novos modelos físicos de relatório como `MediaBatchReport`; - Criação de novas entidades de domínio ou DTOs para falhas (ex: `FailedArticle`, `ClassificationFailedArticle`); - Criação de arquivos físicos adicionais como `*_failed.json`; - Criação de novos parâmetros de linha de comando como `--media-output`; - Alterar o comportamento de carregamento/crawl existente do Foxcape/Camoufox, ou a lógica interna de Trafilatura, Newspaper4k e Readability. A nova funcionalidade consome o HTML já retornado pelo crawler e realiza a decisão de roteamento antes da execução de `extract_all_engines()`.