# Validação da reconstrução ## Resultado - Sintaxe Python (`compileall`): **PASS** - YAML de `config/`: **PASS** - Testes de domínio mock: **4 PASS** - Smoke MCP: **PASS** para faturas, invoice explanation, VAS, histórico, cancelamento e contestação - Diretório do pacote anterior presente: **NÃO** - Imports do namespace anterior em `app/`, `mcp/`, `config/`: **0** - `.env`: **preservado byte a byte** ## Limitação do ambiente de construção O runtime usado para montar o pacote não possui `langgraph` instalado globalmente. Por isso o teste de import/execução do `StateGraph` completo não foi executado aqui. O projeto declara `langgraph` em `pyproject.toml`; rode `uv sync` antes de subir o backend. ## Aceite recomendado em ambiente do projeto ```bash uv sync pytest -q tests/migration uv run uvicorn contas_mcp.servers.contas_mcp_server.main:app --port 8400 uv run uvicorn app.main:app --port 8000 ``` Depois execute os cenários descritos em `MANUAL_AGENT_CONTAS_MIGRADO.md`. ## Atualização de paridade - 2026-08-18 Gate local atual: **338 passed / 3 skipped** em `tests/migration`. Os skips dependem de bibliotecas não instaladas no runtime de construção (principalmente LangGraph/jellyfish) e permanecem habilitados para execução após `uv sync`. Contratos adicionais corrigidos nesta rodada: - VAS History: `GET ?msisdn=...`, `clientId`, `messageId` e `Authorization`. - Contract Information: `clientId` (não `client_id`) e Basic Auth. - Profile/Line Info: header `ClientID` conforme contrato original. - Cancelamento VAS: `messageId` sempre não vazio, além de `channel=AIAGENTCR` e `interactionProtocol`. Gates estruturais mantidos: - zero imports de `agente_contas_tim` em `app/`, `mcp/` e no código-fonte do framework; - zero imports diretos de `langgraph.graph` no domínio Contas; - workflows do domínio executados por `agent_framework.workflows.WorkflowRuntime`; - `FrameworkStateGraph` usado para composição do grafo principal; - `.env` preservado como arquivo principal de configuração. ## Atualização de paridade - continuação 2026-08-18 Gate local atual: **400 passed / 2 skipped** em `tests/migration`. Skips restantes: - `test_original_item_matcher_transcription.py`: requer `jellyfish`, dependência declarada no projeto e instalada por `uv sync`. - `test_original_workflow_cases.py`: requer `langgraph`, dependência declarada no projeto e instalada por `uv sync`. Novas coberturas e correções comprovadas nesta rodada: - cenário real `cy0001` voltou a integrar a regressão de `vas_variation`; o skip causado por caminho incorreto do harness foi removido; - replay pós-finalização preserva `terminal_status` e possui fallback seguro quando a sessão é restaurada sem a última fala; - transformação de transcrição `Fim/Mim -> Sim` foi validada contra a matriz original de fronteira de fala inteira; - `processing_interruption` interrompível voltou a usar classificador LLM leve do **framework**; sem classificador/erro/resultado negativo o comportamento é replay fail-safe; - os dois templates oficiais do framework receberam o mesmo fluxo de classificação de interrupção; - SMS recuperou o default canônico `senderName=TIM Brasil`; - Service Request Status prioriza `messageId`/`ura_call_id` antes de `session_id`; - o compositor determinístico de mensagem de cancelamento VAS foi portado e os 19 testes originais passam; - `cancelar_vas_avulso` encadeia `cancelamento_vas_avulso -> contestacao_tool` usando **dois WorkflowRuntime do framework**; - o MCP inicializa WorkflowRuntime/checkpointer/idempotência de forma lazy, evitando abrir Oracle durante import/health/tools-list; - o `IdempotencyStore` selecionado pelo framework é agora realmente injetado nos actions do Contas, eliminando o fallback local involuntário para memória; - cancelamento usa VAS History como fallback e bloqueia recancelamento quando `canCancel=false`; - resultado em lote expõe `cancelados`, `nao_encontrados`, `nao_cancelados` e `itens_para_contestacao`, preservando sucesso parcial; - finalização normaliza status/aliases e summary conforme o original; - finalização informacional cria protocolo fechado somente quando necessário e evita duplicidade quando já existe protocolo; - combinações canônicas de notas `VAS Bundle`, `VAS Estratégico` e `VAS Avulso` foram portadas e testadas. ### Gates estruturais desta versão ```text agente_contas_tim em app/ 0 agente_contas_tim em mcp/ 0 agente_contas_tim em agent_framework/src 0 langgraph.graph em app/ 0 langgraph.graph em mcp/ 0 ``` O LangGraph continua interno ao `agent_framework_oci` por `FrameworkStateGraph` e `WorkflowRuntime`. ## Atualização de paridade - continuação 2026-08-18 (baseline 420) Gate local atual: **420 passed / 2 skipped** em `tests/migration`. Novas correções comprovadas desde a baseline 400: - plano família mantém o MSISDN do titular na contestação e o MSISDN real do dependente no cancelamento; - `invoice_detail` corrige deterministicamente a linha do item antes do side effect; - `CVAL` encerra `contestacao_tool` imediatamente quando bloqueia a operação, sem seguir para SMS/contrato/SR; - o MCP propaga `success=false`, mensagem e estado sistêmico quando a contestação é bloqueada; - finalização de `invoice_explanation` cria protocolo informacional apenas quando o workflow realmente executou, não por mero prefetch; - protocolo VEB já fechado é reutilizado sem nova abertura/fechamento; `force_rt15_finalization_protocol` força um RT-15 novo quando solicitado; - `suppress_cvn_protocol_ic` preserva a semântica de VAS estratégico deferido; - `WorkflowRuntime` preserva snapshot parcial, nodes e trace quando uma action posterior falha; - Billing Analysis agora carrega metadata de tentativas e gera RCT.079-084 por tentativa; - corrigido bug de `msisdn` duplicado em `preparar_invoice_explanation`; - corrigido bug de inicialização de `AgentWorkflow`: router/agentes/grafo estavam em código inalcançável após `return`; - `InvoiceContextService` foi reconstruído sobre `agent_framework.cache.Cache`, com isolamento por sessão, TTL, prefetch e reaproveitamento de CompleteInvoices/Billing Analysis/detalhe. Os dois skips continuam sendo exclusivamente dependências deste runtime de construção: - `jellyfish` para regressão fonética completa; - `langgraph` para os 18 casos reais de WorkflowRuntime. ## Baseline 435 testes — continuação de paridade Nesta baseline foram adicionadas as seguintes garantias: - `InvoiceContextService` com single-flight por sessão/fatura, cache incompleto sensível a `include_detail`, CVN.002/CVN.006/CVN.007 como `business_events` deduplicados por sessão e sem republicação em cache hit. - Metadados de prefetch: `fetch_elapsed_ms`, `task_timings`, `cache_age_ms` e erros por subconsulta. - Latch genérico `business_workflows_executed` no `AgentRuntimeMixin`; workflows em `PAUSED` já contam como executados e o latch é persistido no patch transacional. - Cancelamento em lote com concorrência máxima 5 (configurável por `TIM_CANCELAMENTO_BATCH_CONCURRENCY`) e protocolo por linha antes do side effect; falha de protocolo bloqueia o cancelamento daquela linha. - `WorkflowRunResult.error_details` preserva fatos estruturados de exceções externas sem acoplamento do framework a TIM. - Contestação FAILED mapeia mensagem do provider/protocolo parcial quando disponíveis e diferencia erro de negócio de falha sistêmica. Resultado local: **435 passed / 2 skipped** em `tests/migration`. Os dois skips continuam dependentes de `langgraph`/`jellyfish` indisponíveis neste runtime de construção. ## Baseline 440 testes - continuação - 440 testes de migração passando; 2 skipped por dependências indisponíveis neste runtime (`langgraph`/`jellyfish`). - Cancelamento VAS: falha de bloqueio/cancelamento pode permanecer candidata à contestação sem mascarar sucesso. - Resposta composta preserva protocolos de cancelamento por linha + protocolo de contestação e sinaliza finalização sugerida. - Plano família: protocolo de cancelamento em dependente resolve `socialSecNo` via LineInfo da própria linha. - Registro de protocolo aceita aliases de resposta `interactionProtocol`, `protocolNumber`, `protocol` e `protocolo`. - Finalização informacional recalcula Bundle/Estratégico/Avulso pela evidência de `invoice_detail`/Billing Analysis quando disponível. ## Baseline 533 - dependências de regressão e finalização - `tests/migration`: **533 passed / 4 xfailed / 0 skipped**. - Os quatro `xfail` são limitações históricas documentadas do ranking fonético (`gueimiloft`, `tim miusic`, `apou`, `agebeo max`), não testes ignorados por dependência. - `jellyfish` deixou de ser requisito obrigatório: o domínio possui fallback puro-Python para Jaro-Winkler, Levenshtein e chave fonética. A biblioteca externa pode ser usada como aceleração, mas o projeto e a regressão não dependem dela. - Os 18 casos históricos de workflow não usam mais `importorskip(langgraph)`. Em builders offline executam pelo backend determinístico **explicitamente opt-in** do `WorkflowRuntime`; em produção o backend padrão continua sendo LangGraph e a ausência de `langgraph` continua sendo erro de configuração. - Finalização validada adicionalmente para: CVN.008/CVN.009, MPI.006/MPI.005, RCT.085/RCT.086, CVN.010/CVN.011, SAD.001 e árvore SAD opcional; protocolo informacional canônico de invoice explanation; supressão em handoff; ausência de aceite informacional após workflows transacionais; classificação de VAS estratégico/avulso sem invoice detail. ### Lockfile O `uv.lock` herdado de snapshots anteriores foi removido porque ainda descrevia o pacote legado (`agente-contas-tim`) e dependências que já não pertencem ao projeto (`jellyfish` obrigatório, NeMoGuardrails/LangChain extras, entre outras). O primeiro `uv sync` deve regenerar o lock a partir do `pyproject.toml` atual. ## Baseline 550 - finalização e matcher sem skips/xfails Validação consolidada desta etapa: ```text 550 passed 0 skipped 0 xfailed ``` Coberturas adicionadas nesta etapa: - finalização conversacional diferencia encerramento genérico de contexto real de fatura; - prefetch/invoice context pode garantir CVN.002/CVN.006 e RT-15 conforme semântica histórica; - `conversation_unresolved_transition_emitted` impede reemissão indevida de CVN/MPI positivos; - VEB terminal com protocolo fechado não cria RT-15 duplicado nem reemite CVN/MPI; - evidência da fatura prevalece sobre tipo informacional salvo em match total; - match parcial mescla inferência da fatura com tipo salvo ainda não representado; - sem match na fatura, tipos salvos conflitantes são descartados; - protocolo já existente impede RT-15 duplicado; - alias `cpf` é propagado como `socialSecNo` no protocolo informacional; - matcher de transcrição resolveu os quatro casos históricos antes marcados como xfail. ### Matcher sem dívida conhecida no catálogo de regressão O `SimilarityItemMatcher` passou a combinar: - similaridade de frase; - similaridade fonética; - alinhamento token-a-token; - dupla evidência grafia + fonética por token. Isso corrigiu explicitamente: - `gueimiloft` -> `Gameloft`; - `tim miusic` -> `TIM Music`; - `apou` -> `Apple Music`; - `agebeo max` -> `HBO Max`. ## Baseline 556 — paridade VAA (2026-08-18) A regressão de migração passou a executar 556 testes sem skip/xfail. Nesta etapa os testes históricos do projeto original foram usados diretamente como catálogo para restaurar a família VAA sem trazer o publisher legado: - cancelamento VAS: VAA.001/VAA.002/VAA.003 no caminho feliz e VAA.004 em falha operacional; - contestação: VAA.005 + VAA.006/VAA.007 e VAA.008/VAA.009 conforme sucesso e elegibilidade do código de barras; - SMS: VAA.012/VAA.014 em sucesso e VAA.013/VAA.015 em falha, mantendo o workflow ativo; - atualização/fechamento de SR: VAA.016/VAA.017. Os events são retornados como `business_events`; transporte, sequence e fan-out permanecem responsabilidade exclusiva do `AgentObserver`/analytics do agent_framework_oci. ## Baseline 560 testes - metadata corporativa TIM - 560 testes de migração passando, sem skips/xfails. - Business events preservam contexto corporativo: `agentProtocolId`, `adjustedProtocol`, `billingId`, `uraCallId`, `channelId`, `sessionId`, `messageId` e `agentSpecificData`. - Contestação VAA.005-009 preserva itens/valores ajustados e protocolo. - Finalização CVN.010/011 preserva protocolo TIM/URA e billing context. - RCTs derivados de retry HTTP carregam `apiUrl`, `apiStatusCode`, `apiResponsePayload` e `latencyMs`. - SMS e Service Request Status carregam metadata de transporte e contexto de protocolo. - `tim_payload_mapper` aceita o formato histórico `agentSpecificData` JSON-string e o converte para o objeto canônico TIM no payload final. ## Baseline 565 — metadata MPI/VEB/SAD e VAS estratégico - Regressão: **565 passed / 0 skipped / 0 xfailed**. - `VEB.*`, `MPI.*`, `CVN.*` e `SAD.*` passam a carregar metadata conversacional TIM via `_event_context`: `customerMessage`, `llmResponse`, `messageId`, `sessionId`, `channelId`, `uraCallId`, `billingId` e `sessionEndAt` quando aplicável. - `SAD.001` normaliza `session_end_at` ISO-8601 para epoch milliseconds. - VAS Estratégico recuperou a semântica histórica: `NAO` estratégico -> `VEB.004 -> VEB.006 -> VEB.007`; Bundle + `NAO` -> `VEB.004 -> VEB.005 -> VEB.007` e protocolo deferido para finalização; `SIM` -> `VEB.003` e registro de atendimento. - Invoice Explanation (`MPI.005/006`), Pró-Rata (`MPI.010`) e cancelamento (`MPI.011`) usam o mesmo enriquecimento de contexto. ## Baseline 570 testes - `pytest -q`: **570 passed**, 0 skipped, 0 xfailed. - `pyproject.toml` limita o gate padrão a `tests/`, evitando coleta acidental dos scripts de teste duplicados existentes dentro dos templates do framework. - `cancelar_vas_avulso` redireciona automaticamente para `vas_estrategico` quando o `InvoiceResolver` classifica a cobrança como estratégico/bundle. - `invoice_explanation` recuperou `recomenda_finalizacao/status_finalizacao_sugerido` por branch do `WorkflowRuntime`. - `pro_rata` recuperou recomendação de finalização nos branches `registrar_aceitou` e `registrar_nao_controle`. - VAS Estratégico recuperou a busca de orientação do parceiro sem RAG próprio: a action declara `requires_rag/rag_queries`, e o `AgentRuntimeMixin` executa `RagService` do framework. ## Baseline 578 - wrapper cancelamento + LLM composition - 593 testes passando; zero skipped/xfail. - Cancelamento preserva `auto_finalize_on_failure` em falha técnica de contestação. - Item já contestado não mascara cancelamento concluído como falha sistêmica. - `cancelamento_vas_protocol` e todos os protocolos de resposta são preservados. - Plano família contesta titular + dependentes em uma única `contestacao_tool`. - Lote com muitos no-match continua contestando exatamente os candidatos elegíveis. - Pró-rata usa `requires_llm_composition` do framework em vez de gateway LLM de domínio. ## Continuação — paridade explícita dos wrappers (baseline 593) - `contestacao_tool` volta a garantir `tipo_atendimento=contestacao` e preserva o contexto do turno (`message_id`, `customer_message`, `ura_call_id`, `channel_id`, `ani`, `invoice_id`). - Falhas de contestação são diferenciadas entre erro técnico (`erro_falha_sistema`) e conflito/item já contestado com mensagem/protocolo do provider. - Cancelamento composto aceita tanto `contestation_candidates` quanto o alias histórico `itens_para_contestacao`. - Resultado agregado em `cancelados/nao_cancelados` é aceito mesmo quando `results[]` não repete a flag `success`. - `cpf` volta a ser alias de `social_sec_no` e é normalizado para dígitos antes de protocolo/contestação. - Em fluxo misto Bundle + Estratégico no branch NÃO, o protocolo segue deferido para finalização, porém as `rag_queries` dos serviços estratégicos são preservadas. - Falha parcial com candidato continua encadeando `cancelamento_vas_avulso -> contestacao_tool`; falha de SMS não transforma o cancelamento em falha. - `pytest -q`: **593 passed, 0 skipped, 0 xfailed**. ## Baseline 599 testes — paridade explícita de wrappers Validação consolidada: `599 passed`, `0 skipped`, `0 xfailed`. A rodada adicionou regressões 1:1 baseadas nos wrappers históricos para: - `itens_ja_contestados` preservados desde a action de contestação até o compositor de resposta; - classificação de `contested_items`, `not_contested_items` e itens já contestados feita no domínio, não reconstruída no MCP; - totais de contestação calculados somente sobre itens efetivamente aceitos; - valor `0`/`0,00` retornado pela contestação tratado como ausência de valor útil para composição, usando o total cancelado; - valores monetários normalizados em pt-BR na borda do MCP (`14,99`); - `next_subject` não interfere na composição determinística de cancelamento; - falha de serviço em `invoice_explanation` preserva a fraseologia canônica e não ativa auto-finalização. Gates: `compileall` PASS, zero imports do namespace legado e zero imports diretos de LangGraph em `app/`/`mcp/`. ## Baseline 615 — contratos 1:1 dos wrappers históricos A regressão foi ampliada para 615 testes executados, sem skips e sem xfails. Nesta etapa foram portados como contratos explícitos do MCP novo os seguintes outcomes do `backend_cancelar_vas_single` e `finalize_support` históricos: - sucesso implícito quando o workflow reporta `cancelados[]` sem `success=true`; - lista explícita vazia de candidatos não cria fallback indevido para contestação; - no-match não chama contestação nem vocaliza valor como se houvesse ajuste; - `protocol_closed` da contestação é preservado; - flags internas `sms_sent` não vazam no contrato externo e falha de SMS é propagada por `sms_not_send_error`; - cancelamento com protocolo e sem item efetivamente contestado continua resolvido; - candidato explícito pode seguir para RT-02 mesmo após falha/no-match em RT-01; - falha parcial de bloqueio/cancelamento continua elegível à contestação quando marcada pelo domínio; - `holder_msisdn` não pode ser sobrescrito por linha dependente; - item do titular não é marcado como dependente; - titular + múltiplos dependentes são agregados em uma única contestação do titular; - VAS estratégico após invoice explanation prioriza nota estratégica na finalização; - Bundle + Estratégico deferidos geram RT-15 combinado na finalização; - invoice explanation não abre protocolo informacional antes da finalização. Gate executado: ```text pytest -q: 615 passed compileall app/mcp/framework: PASS agente_contas_tim em app/mcp/framework runtime: 0 import direto langgraph.graph em app/mcp: 0 ```