diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc
index f71d1b6..d3fa4e7 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc
index 7cfe855..3dbd1ac 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/base.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc
index 04e56ea..67e644d 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/config_loader.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc
index 69e3d34..b0e4dda 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/custom_rails.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc
index bd17d05..0a90f41 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/framework_llm_client.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc
index 247d900..ddedee0 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/llm_rails.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc
index 8681308..3706435 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/output_supervisor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc
index c9f0a6e..8936cb0 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/parallel_executor.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc
index 2a37530..892395c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/pipeline.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc
index 0796c12..13d5a9c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_action.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc
index 6ffb379..41e348e 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_decision.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc
index 34b0e57..fa80334 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rail_result.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc
index 9bf0bb1..2dbd728 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/__pycache__/rails.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc
index d640def..21efb85 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc
index 2616304..4708cfe 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/_compat.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc
index fe11483..bce6081 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contestation_validation.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc
index 7e2e6f2..9316b89 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/contracts.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc
index a5e5649..9a27145 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/input_size.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc
index 73ac151..fd4d59d 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_adapter.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc
index b708063..5044c00 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_client.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc
index e3c5e17..156e566 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/llm_rails.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc
index d3285e7..1646570 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/output_sanitization.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc
index 6503e22..d3208e5 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/__pycache__/pipeline.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc
index 15d4e82..f2432c2 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc
index a1d2c6a..8110ec9 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/_context.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc
index d7186ef..a4b8461 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ausencia_oferta_proativa.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc
index 3e3772f..131939a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/coerencia.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc
index 7d10414..a91ffe5 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_in.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc
index 2329824..57a065b 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/dlex_out.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc
index f288519..b290cb9 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fallback.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc
index d231d22..885cc2c 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/fraseologia.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc
index c69bef6..2ca4865 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/out_of_scope.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc
index a0659fa..5d759df 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/pinj.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc
index eb043c1..3f2d1a9 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/ragsec.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc
index bb86381..1bf5cbc 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/revprec.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc
index 4949e31..09c1849 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/tox.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc
index 08db33c..f49141f 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/__pycache__/toxicidade_output.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/coerencia.py b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/coerencia.py
index b6acd9e..05c8138 100644
--- a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/coerencia.py
+++ b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/prompts/coerencia.py
@@ -1,148 +1,78 @@
-"""Prompt do rail COER (coerência do input do cliente).
+"""Prompt do rail COER (coerência semântica do input do cliente).
-Roda no INPUT, em paralelo com PINJ (mesmo pool), num 20b. Decide se a fala do
-cliente é aproveitável. Saída BINÁRIA (`1` passa / `0` descarta) — o `reason` é
-texto fixo; pedir motivo antes do dígito foi medido e não paga (+170 ms, empate).
+O COER responde uma pergunta estreita: existe significado conversacional recuperável
+na fala do cliente? Ele não decide intenção, completude de parâmetros, executabilidade,
+escopo de negócio ou se uma solicitação deve ser aceita. Essas decisões pertencem ao
+router, aos contratos transacionais, aos validadores e ao mecanismo de clarification.
-Descarta SÓ por três motivos:
-
-(a) incompreensível — transcrição quebrada, palavra solta, conversa paralela;
-(b) negação ambígua — "não" colado num pedido de AÇÃO do atendente, sem a vírgula
- que decidiria a leitura ("não quero cancelar" × "não, quero cancelar");
-(c) idioma (2026-08-10) — frase INTEIRA em inglês é STT quebrado, não cliente
- bilíngue: descarta mesmo se ela se entende ou responde à pergunta pendente.
- Ressalva: passa quando o agente pediu o NOME do item — nome de serviço É em
- inglês (`coer_ok_0023`). ⚠️ A regra só funciona no ENQUADRAMENTO, acima do
- gate de histórico (dentro de (a): 0/9 nos casos de inglês; no topo: 9/9),
- porque o gate concede 1 a quem responde e o catch-all a quem pede algo
- legível. Travado em `tests/guardrails/test_coerencia.py`.
-
-O resto passa e é tratado adiante (matcher, TOX, OOS, orquestrador): referência
-vaga, nome deformado, xingamento, assunto fora de fatura, resposta curta. O
-histórico entra no prompt porque é ele que resolve fala curta e negação sem vírgula.
-
-Dois bugs de produção fechados, ambos com a mesma assinatura — o modelo reconhece
-a fala e escapa por uma regra de allow antes de aplicar (b):
- - 2026-08-07, "não" seco no degrau 2 da retenção: (b) disparava só por começar
- com "não" e o modelo COMPLETAVA a elipse com a ação que o AGENTE ofereceu.
- Conserto: (b) exige que a fala PEÇA algo, e o teste da subtração proíbe
- completar com a oferta do agente (`coer_ok_0027`: 161/220 → 340/340);
- - 2026-08-10, "não gostaria de falar com a atendente" (`coer_ambig_0014`, 2/9):
- a causa é o VERBO, não o gate nem o histórico (sonda 2×2 — condicional +
- histórico curto 2/10 × "não quero" + o histórico longo do trace 10/10).
- Conserto: gate vale só para a fala que "SÓ responde a ela"; (b) diz que
- entender o pedido não dispensa o teste; a glosa do 1º exemplo cobre o
- condicional. Alvo → 7/9, suíte 176,0 → 180,7/189.
-
-⚠️ Protocolo: decida por BATCH (3 amostras de `--repeat 3` da suíte inteira, banda
-de ruído ±4). `--repeat` focado engana nos dois sentidos — a mesma variante deu
-7/10 focado × 0/9 batch, e o prompt atual dá 7/9 batch × 3/9 focado.
-
-Variantes medidas e REJEITADAS (não retentar sem motivo novo) — a suíte está numa
-fronteira zero-soma, cada cláusula compra um caso e vende outro:
- - "a recusa soar clara não fecha" → CONTRADIZ a exceção "a fala segue dizendo
- qual leitura vale": mata `coer_ok_0003` (7/9 → 0-1/9) em 3 variantes;
- - exceção no GATE ("fala com 'não' ainda passa por (b)") → mata `coer_ruido_0011`
- (9/9 → 0/9): exceção explícita REFORÇA o gate para todo o resto;
- - "gostaria" na lista de modais de (b) → 169,7/189;
- - few-shot NÃO é mais alavanca (era em 2026-08-05, +3,4 p.p.): +3 exemplos = empate
- exato por +132 tokens; só o do NOME em inglês = 189,7/201 (arrasta a regra (c));
- tirar exemplos custa mais do que os tokens que ocupam — inclusive o "não quero
- entender porque…", que o controle FOCADO media como "sem efeito" e em batch vale
- `coer_ok_0010` inteiro (9/9 → 1/9).
-
-Tamanho: 1289 → 1334 (2026-08-07) → **1451 tokens** (cl100k). Suíte: **191,7/201
-(95,4%)**, 67 casos. Detalhe por caso e histórico: `tests/llm_tests/README.md`.
-
-Remedido em 2026-08-12 ao desfazer o revert (41979c4d): 193,7/204 (95,0%), 68 casos
-— o novo `coer_ruido_0022` ("um" respondendo "sanei sua dúvida?", STT que não pegou
-o "sim" → golden 0, reperguntar) sai de 3/10 no prompt antigo para 9/9 em batch só
-com o gate "SÓ responde a ela", sem mudança extra de prompt.
+A classificação continua totalmente delegada ao LLM. Não há listas de frases,
+regexes ou exceções de domínio para liberar/bloquear entradas específicas.
"""
from __future__ import annotations
def build_coer_prompt(text: str, context: str = "") -> str:
- """Monta o prompt do rail COER.
+ """Monta o prompt semântico do rail COER.
Args:
text: fala do cliente a classificar.
- context: bloco de histórico já formatado por
- ``prompts._context.format_context_block`` (para este rail a última
- fala do agente é PRESERVADA — é a pergunta pendente).
+ context: histórico já formatado, incluindo a pergunta pendente do agente
+ quando disponível.
Returns:
Prompt cuja resposta esperada é um único caractere: ``1`` ou ``0``.
"""
- return f"""Você filtra a fala do CLIENTE no atendimento de fatura do provedor. A fala vem de
-transcrição de voz e pode chegar truncada ou trocada. O atendimento é em português:
-frase inteira em INGLÊS é STT quebrado, não cliente bilíngue — responda 0 mesmo que
-ela se entenda ou responda à pergunta do agente; só não vale quando o agente pediu o
-NOME do item, que é em inglês.
+ return f"""Você é o guardrail de COERÊNCIA SEMÂNTICA da fala do CLIENTE em uma conversa.
-PRIMEIRO olhe o histórico. Se o agente terminou com uma pergunta e a fala SÓ responde a ela
-(sim/não, "ainda não", nome de serviço, valor, uma das opções oferecidas), responda 1
-— mesmo curta, estranha ou com o nome deformado pelo STT. Se não há pergunta pendente,
-julgue a fala sozinha pelos casos abaixo, sem dar desconto.
+Sua única responsabilidade é decidir se a fala contém significado conversacional
+recuperável o suficiente para que as próximas camadas do sistema possam trabalhar.
-Responda 0 (descartar) SÓ nestes dois casos:
+NÃO tente decidir aqui:
+- qual é a intenção do cliente;
+- se a intenção mudou em relação ao turno anterior;
+- se uma transação deve continuar, ser abandonada ou encerrada;
+- se faltam parâmetros para executar uma ação;
+- se um valor, nome, data ou outro parâmetro é válido;
+- se a solicitação pertence ao escopo do atendimento;
+- se uma ação é permitida por regra de negócio;
+- se a fala precisa de clarification ou desambiguação posterior.
-(a) NÃO DÁ PARA ENTENDER — você não conseguiria dizer em uma frase, SEM INVENTAR, o
- que o cliente quer, responde ou reclama: transcrição quebrada, frase cortada no
- meio, palavra ou letra solta, frase que soa completa mas cujo pedido não faz
- sentido, ou fala dirigida a OUTRA PESSOA (o cliente conversando com quem está do
- lado, sem falar com o atendimento). Palavra do domínio (plano, fatura, valor,
- cpf) dentro de frase sem sentido não salva a fala. Fala VAGA não é
- incompreensível: se ela aponta para o que está na tela ("esse aí", "isso aqui",
- "esse negócio", "os valores"), responda 1 — perguntar qual item é do fluxo.
- E se a última fala do agente pediu um NOME de item/serviço, nenhuma fala curta
- é incompreensível: ela é a tentativa de dizer o nome, por mais estranha que
- soe → 1 (reconhecê-lo é da etapa seguinte, que tem a fatura).
+Essas responsabilidades pertencem ao router, ao estado transacional, aos validadores
+e ao mecanismo de clarification. Portanto, uma fala pode ser compreensível mesmo
+sendo incompleta para execução, contendo negação, discordância, reclamação, múltiplas
+intenções, informalidade, erro gramatical ou referência que precise ser resolvida pelo
+contexto.
-(b) NEGAÇÃO AMBÍGUA — a fala começa com "não" E PEDE ALGO depois; entender o que ela
- pede não a salva, quem decide é o teste. Faça o teste: tire
- esse "não" do início e olhe SÓ o que sobra na fala — nunca complete com a ação
- que o agente ofereceu. Se não sobra pedido nenhum ("não", "não sanou"), é
- resposta ao agente → 1, seja qual for a pergunta pendente. Se o que sobra é
- pedido de ação do atendente (cancelar, tirar cobrança,
- ajustar/diminuir a fatura, transferir para atendente, encerrar a conta,
- parcelar), sobram duas leituras opostas — recusa ("não quero cancelar") ou
- pedido ("não, quero cancelar") — e a vírgula que decidiria não veio na
- transcrição: responda 0. Vale para qualquer verbo ("não quero/preciso/posso",
- "não quero que vocês...", "não cancela").
- Responda 1 se: vem vírgula, "porque" ou "mas" depois do "não"; há sujeito antes
- do "não" ("eu não quero cancelar"); a fala segue dizendo qual leitura vale; ou o
- que sobra sem o "não" não é ação do atendente (pagar, reconhecer, entender,
- mudar de plano).
+Use o histórico somente para interpretar elipses, respostas curtas e referências ao
+turno anterior. Nunca complete a fala inventando uma intenção que não esteja apoiada
+pela própria fala ou pelo contexto imediato.
-Responda 1 em TODO o resto, inclusive:
-- pedido, queixa, dúvida ou desabafo que você entende, mesmo com erro de transcrição,
- gíria, xingamento, número solto ou assunto fora de fatura (outros filtros cuidam);
-- nome de serviço estranho ou deformado, inclusive quando o agente pediu para repetir
- o nome do serviço;
-- pedido de tempo, "alô?", agradecimento, despedida.
+Responda 1 quando for possível identificar, sem inventar, pelo menos um conteúdo
+conversacional útil: uma intenção, pergunta, resposta, afirmação, negação, reclamação,
+referência, escolha, valor, nome, pedido de esclarecimento, encerramento ou mudança de
+assunto. Não exija que esse conteúdo já seja suficiente para executar uma ferramenta.
-Dúvida se entendeu a fala → 1. Pergunta ou pedido claro dirigido ao atendimento, mesmo
-fora do assunto de fatura → 1. Dúvida entre as duas leituras da negação → 0.
+Responda 0 somente quando, mesmo considerando o contexto imediato, não houver
+significado conversacional recuperável com segurança — por exemplo, transcrição
+fragmentada, palavras desconexas, fala cortada antes de formar qualquer relação
+semântica, ou conversa paralela sem solicitação dirigida ao atendimento.
-Exemplos (ilustram a regra, não são lista de falas):
-- "não quero parcelar a fatura" → 0 (sem a vírgula, pode ser "não, quero parcelar");
- idem no condicional, "não gostaria de parcelar a fatura"
-- "eu não quero parcelar a fatura" → 1 (o "eu" antes do "não" fecha a leitura)
-- "não quero parcelar, quero só entender o valor" → 1 (a fala diz qual leitura vale)
-- "não vou pagar essa multa" → 1 (pagar não é ação do atendente: a queixa é a mesma)
-- "não", depois de "sanou sua dúvida?" → 1 (responde a pergunta pendente)
-- "deixe zero", depois de "qual o nome do serviço?" → 1 (pode ser o nome que o STT
- deformou — "Deezer"; reconhecer o nome é da etapa seguinte, que tem a fatura)
-- "não quero entender porque a conta subiu tanto" → 1 (entender é dúvida, não ação)
-- "olha o menino ali pegando o negócio lá" → 0 (não dá para dizer o que o cliente quer)
-- "bota dois planos um em cima do outro pra cá" → 0 (soa ordem, não quer dizer nada)
-- "está cobrando um" → 0 (cortada no meio: não dá para saber de quê)
+Critério decisivo:
+- compreensível mas incompleto/ambíguo para a regra de negócio -> 1;
+- compreensível mas com possível mudança de intenção -> 1;
+- compreensível mas sem todos os parâmetros -> 1;
+- compreensível e contendo negação/discordância -> 1;
+- impossível determinar qualquer conteúdo conversacional sem inventar -> 0.
+
+Na dúvida entre "há significado, mas outra camada precisa esclarecer" e "não há
+significado recuperável", escolha 1. O COER deve bloquear apenas incompreensibilidade
+semântica real, não incerteza de negócio.
------------------------------------{context}
Fala do cliente:
{text}
------------------------------------
-Responda APENAS um caractere: 1 (aproveitável) ou 0 (descartar).
+Responda APENAS um caractere: 1 (semanticamente compreensível) ou 0
+(semanticamente incompreensível).
"""
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc
index fc0d11b..b332bab 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc
index 3f1f38b..0d98db3 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/alcada.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc
index e7f40e1..31ea4b6 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/anatel.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc
index b9f0d6a..6b3a60e 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/confirmation.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc
index e3e19b2..99a2d6d 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_in.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc
index 291c324..1697f9a 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/dlex_out.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc
index 9d4c5d2..e62ed42 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/ragsec.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc
index 8c92df8..0de4a46 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/revprec.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc
index 4bbce8a..5af6e2e 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rails/__pycache__/tox.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc
index 3503902..2eb99a9 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/__init__.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc
index 84e8436..6a456ac 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/alcada.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc
index 83b9ffb..414c3b8 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/pinj_patterns.cpython-313.pyc differ
diff --git a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc
index aa63f53..34abe3d 100644
Binary files a/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc and b/agent_framework_oci/libs/agent_framework/src/agent_framework/guardrails/calibrated/rules/__pycache__/tox_blocklist.cpython-313.pyc differ
diff --git a/docs/FIX_COER_SEMANTIC_LLM_20260831.md b/docs/FIX_COER_SEMANTIC_LLM_20260831.md
new file mode 100644
index 0000000..ee161cb
--- /dev/null
+++ b/docs/FIX_COER_SEMANTIC_LLM_20260831.md
@@ -0,0 +1,27 @@
+# Ajuste semântico do guardrail COER
+
+## Objetivo
+
+Evitar que o COER confunda negação, reclamação, mudança de intenção ou falta de parâmetros com fala incompreensível.
+
+## Alteração
+
+O prompt de `agent_framework.guardrails.calibrated.prompts.coerencia` foi simplificado para uma responsabilidade única: decidir se existe significado conversacional recuperável.
+
+Foram removidas heurísticas textuais específicas de negação, listas de ações e exemplos de frases usados como regras de decisão. O julgamento continua sendo feito pelo LLM com o perfil `guardrail`.
+
+O COER agora não decide intenção, mudança de intenção, continuidade de transação, completude de parâmetros, validade de parâmetros, escopo ou executabilidade. Essas responsabilidades permanecem no router, runtime transacional, validators e clarification.
+
+## Contrato
+
+- compreensível, mas incompleto/ambíguo para negócio: ALLOW;
+- compreensível com possível mudança de intenção: ALLOW;
+- compreensível faltando parâmetros: ALLOW;
+- compreensível com negação/discordância: ALLOW;
+- sem significado semântico recuperável: BLOCK.
+
+## Testes
+
+- regressão dirigida do COER: 22 PASS;
+- `tests/migration`: 824 PASS / 2 FAIL;
+- os 2 FAIL são preexistentes e não relacionados ao COER.
diff --git a/tests/docs/EXTERNAL_GUARDRAILS_JUDGES.md b/tests/docs/EXTERNAL_GUARDRAILS_JUDGES.md
new file mode 100644
index 0000000..a484181
--- /dev/null
+++ b/tests/docs/EXTERNAL_GUARDRAILS_JUDGES.md
@@ -0,0 +1,71 @@
+# Guardrails e Judges externos — Contas
+
+## Objetivo
+O `agent_framework_oci` mantém apenas mecanismos e políticas realmente genéricos. O agente Contas mantém políticas, exemplos e prompts que conhecem TIM, Contas, VAS, fatura, cancelamento ou nomenclaturas comerciais.
+
+## Regra arquitetural
+- **Framework:** engine, contratos, execução paralela, fail-fast, telemetria, carregamento YAML e implementações genéricas.
+- **Agente:** prompts/policies de domínio e classes externas.
+- Um componente externo só é importado quando o YAML do agente declara `type: external`.
+- Guardrails/judges nativos continuam funcionando sem qualquer alteração de configuração.
+
+## Configuração de guardrail externo
+```yaml
+output:
+ - code: TIM_AOFERTA
+ type: external
+ class: app.extensions.tim_guardrails:TimProactiveOfferRail
+ enabled: true
+```
+O código `TIM_AOFERTA` deixa explícito que esta política é do agente Contas. O framework continua podendo oferecer `AOFERTA` como rail genérico para outros agentes.
+
+## Configuração de judge externo
+```yaml
+judges:
+ - name: tim_groundedness
+ type: external
+ class: app.extensions.tim_judges:TimGroundednessJudge
+ enabled: true
+ threshold: 0.60
+```
+
+## Concorrência e threads
+O `ParallelRailExecutor` executa todos os rails concorrentemente. `evaluate()` assíncrono roda no event loop; plugin síncrono roda por `asyncio.to_thread`, portanto não bloqueia o loop. Judges nativos e externos são disparados com `asyncio.gather`; judges síncronos também são deslocados para `asyncio.to_thread`. A ordem da lista de resultados permanece a ordem do YAML.
+
+## Compatibilidade
+A extensão é aditiva. Entradas antigas como `{code: PINJ}` e `{name: groundedness}` seguem nativas. Somente itens com `type: external` usam import dinâmico. Isso evita dependência reversa do framework para `app.*`.
+
+## Mapeamento nesta versão do Contas
+| Genérico no framework | Específico no Contas | Motivo |
+|---|---|---|
+| OOS | TIM_OOS | escopo do Contas/TIM |
+| AOFERTA | TIM_AOFERTA | política de oferta do atendimento TIM |
+| REVPREC | TIM_REVPREC | exemplos e ações transacionais TIM |
+| FRASEOLOGIA | TIM_FRASEOLOGIA | fraseologia própria (mantido desabilitado como antes) |
+| response_quality | tim_response_quality | prompt original do auditor Contas |
+| groundedness | tim_groundedness | prompt original de alucinação/grounding do Contas |
+
+Os prompts originais foram preservados em `app/extensions/tim_prompts/`. As versões sob `agent_framework/.../calibrated/prompts` foram generalizadas e não devem conter nomes comerciais TIM.
+
+## Como criar um novo componente
+1. Implemente uma classe no agente com `evaluate(...)`.
+2. Para guardrail, retorne `RailDecision`/`RailResult`; para judge, retorne `JudgeResult`.
+3. Declare `type: external` e o caminho `module:Class` no YAML.
+4. Não crie cliente LLM próprio: use o `llm` fornecido pelo framework/contexto para manter `llm_profiles.yaml`, Langfuse e contabilização.
+5. Teste convivência com os rails/judges nativos e o comportamento fail-closed.
+
+## Hardcodes de integração do Contas
+Os valores legados de `clientId`, `channel`, `cspId`, sender e URLs que antes apareciam como fallback em Python foram movidos para `config/tim_integration_defaults.yaml`. A precedência é:
+
+1. variável de ambiente;
+2. `config/tim_integration_defaults.yaml`;
+3. default explícito somente quando a chamada realmente define um default técnico.
+
+Isso preserva contratos diferentes por operação (`TIM_CANCELAMENTO_CHANNEL`, `TIM_DIVERGENCIA_CHANNEL`, etc.) sem usar um `TIM_DEFAULT_*` que altere silenciosamente o legado.
+
+## Validação de contestação
+`validate_contestation_items` é regra do domínio Contas e agora vive em `app/domain/contas/contestation_validation.py`. O módulo antigo no framework existe apenas como shim de compatibilidade/depreciação; código novo do Contas importa a implementação do agente diretamente.
+
+## Observabilidade dos códigos externos
+
+O código semântico de um guardrail/judge externo não deve ser alterado para atender um código numérico de um cliente. Use `config/observability_mapping.yaml` para o contrato de telemetria. Exemplo: a extensão pode continuar emitindo `GRL.TOXOUT`, enquanto o contrato publica `GRL.004`. Isso mantém a política do agente separada do catálogo externo de observabilidade.
diff --git a/tests/docs/FIX_COER_SEMANTIC_LLM_20260831.md b/tests/docs/FIX_COER_SEMANTIC_LLM_20260831.md
new file mode 100644
index 0000000..ee161cb
--- /dev/null
+++ b/tests/docs/FIX_COER_SEMANTIC_LLM_20260831.md
@@ -0,0 +1,27 @@
+# Ajuste semântico do guardrail COER
+
+## Objetivo
+
+Evitar que o COER confunda negação, reclamação, mudança de intenção ou falta de parâmetros com fala incompreensível.
+
+## Alteração
+
+O prompt de `agent_framework.guardrails.calibrated.prompts.coerencia` foi simplificado para uma responsabilidade única: decidir se existe significado conversacional recuperável.
+
+Foram removidas heurísticas textuais específicas de negação, listas de ações e exemplos de frases usados como regras de decisão. O julgamento continua sendo feito pelo LLM com o perfil `guardrail`.
+
+O COER agora não decide intenção, mudança de intenção, continuidade de transação, completude de parâmetros, validade de parâmetros, escopo ou executabilidade. Essas responsabilidades permanecem no router, runtime transacional, validators e clarification.
+
+## Contrato
+
+- compreensível, mas incompleto/ambíguo para negócio: ALLOW;
+- compreensível com possível mudança de intenção: ALLOW;
+- compreensível faltando parâmetros: ALLOW;
+- compreensível com negação/discordância: ALLOW;
+- sem significado semântico recuperável: BLOCK.
+
+## Testes
+
+- regressão dirigida do COER: 22 PASS;
+- `tests/migration`: 824 PASS / 2 FAIL;
+- os 2 FAIL são preexistentes e não relacionados ao COER.
diff --git a/tests/docs/FIX_CONTAS_REPORT_20260831_SECOND_PASS.md b/tests/docs/FIX_CONTAS_REPORT_20260831_SECOND_PASS.md
new file mode 100644
index 0000000..58be391
--- /dev/null
+++ b/tests/docs/FIX_CONTAS_REPORT_20260831_SECOND_PASS.md
@@ -0,0 +1,27 @@
+# Correções após regressão de 31/08/2026
+
+Esta rodada corrige quatro comportamentos observados no relatório de regressão sem reintroduzir o runtime conversacional legado.
+
+## 1. Cancelamento múltiplo após confirmação
+
+O snapshot transacional do framework já preservava corretamente os argumentos confirmados. O defeito estava no preflight de execução do MCP do Contas, que tentava resolver novamente o `subject` de apresentação (por exemplo, `Tamboro Mensal, Paramount+`) mesmo quando `items[]` já continha múltiplas entidades canônicas pré-validadas. Agora `items[]` é a fonte de verdade nessa condição e não há segunda resolução textual.
+
+## 2. Contestação com valor incompatível
+
+Uma divergência de valor comprovada pelo CVAL passa a ser recuperável: o item permanece preservado, apenas `valor` volta para coleta e a resposta informa o valor autoritativo encontrado na fatura. O contrato de auditoria mantém `reason=CVAL` e acrescenta `recoverable_reason=amount_not_supported_by_invoice`.
+
+O runtime genérico ganhou suporte opcional a `parameter_message` emitido por um pre-validator de domínio. O framework apenas apresenta essa mensagem enquanto permanece em `COLLECTING_PARAMETERS`; ele não interpreta a regra de negócio.
+
+## 3. Encerramento explícito
+
+Expressões inequívocas como `entendi, obrigado, era só isso` encerram a sessão como `resolvido`, desde que não exista transação ou workflow ativo. Isso evita que uma despedida caia em fallback/guardrail sem consumir confirmações pendentes.
+
+## 4. Continuação plural após explicação de fatura
+
+Frases como `as duas mesmo, pode seguir` imediatamente após `contas_invoice_explanation` permanecem no contexto de explicação e não são confundidas com finalização genérica.
+
+## Regressão
+
+- Testes novos e direcionados: 58/58 no Contas e 30/30 no runtime transacional do framework.
+- `tests/migration`: 818 PASS / 2 FAIL.
+- Os 2 FAIL restantes são preexistentes nesta base: caso histórico de `validar_contestacao` com TIM Fashion/R$50 e fraseologia de `termino_desconto`.
diff --git a/tests/docs/FIX_CONTAS_RESIDUAL_CONVERSATION_PARITY_20260831.md b/tests/docs/FIX_CONTAS_RESIDUAL_CONVERSATION_PARITY_20260831.md
new file mode 100644
index 0000000..c86dc15
--- /dev/null
+++ b/tests/docs/FIX_CONTAS_RESIDUAL_CONVERSATION_PARITY_20260831.md
@@ -0,0 +1,63 @@
+# Correção de paridade conversacional residual do Contas — 2026-08-31
+
+## Escopo
+
+Correções pontuais extraídas do comportamento útil do Contas anterior sem restaurar o runtime legado:
+
+- retenção antes de handoff humano;
+- jurídico/Anatel/Procon com dependência de entidade previamente em foco;
+- três falas consecutivas realmente incompreensíveis;
+- preservação do pós-finalização no framework, com status terminal de erro respeitado;
+- cancelamento múltiplo por entidades nomeadas/contextuais e por "todos os VAS avulsos".
+
+## Arquitetura
+
+Foi adicionada `app/domain/contas/conversation_policy.py`, executada depois do `EnterpriseRouter` e antes do agente de domínio. A policy não executa side effects: apenas reprompta, enriquece contexto ou altera o roteamento. Toda operação transacional continua passando pelo `AgentRuntimeMixin`, pré-validação MCP, confirmação explícita e workflow do Contas.
+
+## Retenção
+
+Quando o router solicita handoff humano, a policy procura evidência autoritativa de VAS avulso que participou da variação da conta usando `varied_avulso_items`. Sem evidência, o handoff segue normalmente. Com evidência, a policy oferece dois degraus: explicação da variação e, em seguida, tratamento dos VAS identificados. Recusa do segundo degrau leva ao handoff humano normal.
+
+O aceite do tratamento cria reentrada contextual com os nomes já comprovados; o cliente não precisa repeti-los e a transação continua sujeita à pré-validação e confirmação.
+
+## Jurídico / Anatel / Procon
+
+A mera ameaça regulatória não cria uma transação. Sem entidade concreta em foco, a fala é encaminhada como reclamação ampla ao suporte, sem MCP transacional. Quando já existe `subject/items` em estado transacional, o foco é preservado e a fala pode continuar no fluxo correspondente. A fatura inteira nunca é usada para inventar o alvo.
+
+## Três falas incompreensíveis
+
+Foi criada a intent semântica `contas_no_match`, exclusiva para fala sem conteúdo recuperável. `fallback` genérico não conta como incompreensão. O contador é consecutivo e reinicia em qualquer turno compreendido.
+
+- 1ª: pede reformulação;
+- 2ª: pede reformulação;
+- 3ª: encerra pelo nó global `end_session`, chamando `finalizar_atendimento` com `status=erro_no_match`.
+
+O limite pode ser configurado por `CONTAS_NO_MATCH_MAX_CONSECUTIVE`, default 3.
+
+## Cancelamento múltiplo
+
+`validar_vas_subject` agora resolve múltiplas entidades exclusivamente contra catálogo autorizado VAS/fatura. Exemplos suportados após extração semântica contextual:
+
+- `cancela Netflix e HBO`;
+- `os dois` / `ambos`, quando o extrator LLM consegue resolver os nomes pelo contexto imediato;
+- `todos os VAS avulsos`.
+
+`todos` genérico não expande em massa. Para "todos os VAS avulsos", itens estratégicos/bundle são filtrados pela política de domínio. Os itens resolvidos são enviados como `items[]` para o workflow batch existente, com uma única confirmação explícita antes da execução.
+
+## Pós-finalização
+
+Não foi criado runtime duplicado. O lifecycle continua no framework. O nó `end_session` passou apenas a respeitar um `terminal_status` já definido pela policy (por exemplo `erro_no_match`) e uma mensagem terminal específica, mantendo o mecanismo atual de replay/soft reset.
+
+## Testes
+
+Novos testes:
+
+- `tests/migration/test_contas_conversation_policy_residuals.py`;
+- `tests/migration/test_multiple_vas_subject_resolution.py`.
+
+Regressão direcionada: **61 PASS**.
+
+Regressão completa `tests/migration`: **810 PASS / 2 FAIL**. Os mesmos dois FAIL foram reproduzidos no ZIP original sem estas alterações, portanto são falhas preexistentes e fora do escopo desta correção:
+
+1. `test_validar_contestacao_aprova_quando_item_e_valor_sao_comprovados`;
+2. `test_termino_desconto_e_valor_divergente_preservam_semantica_do_original`.
diff --git a/tests/docs/FIX_CVAL_AMOUNT_AND_HOMONYM_RESOLUTION_20260829.md b/tests/docs/FIX_CVAL_AMOUNT_AND_HOMONYM_RESOLUTION_20260829.md
new file mode 100644
index 0000000..5750b08
--- /dev/null
+++ b/tests/docs/FIX_CVAL_AMOUNT_AND_HOMONYM_RESOLUTION_20260829.md
@@ -0,0 +1,24 @@
+# Correção CVAL: valores monetários e itens homônimos
+
+## Problema
+
+O CVAL removia todo ponto de valores textuais antes da conversão decimal. Assim, valores vindos de JSON/backend como `19.99` eram interpretados como `1999`, permitindo indevidamente ajustes como `29.98`.
+
+Além disso, quando o mesmo `subject` aparecia mais de uma vez na fatura, a validação escolhia a primeira ocorrência após a ordenação estrutural, sem usar o valor solicitado para desambiguar a cobrança correta.
+
+## Correção
+
+1. `_parse_amount()` agora reconhece formatos decimais e de agrupamento comuns, incluindo `19.99`, `R$ 19,99`, `1.999,99` e `1,999.99`.
+2. Quando existem múltiplos candidatos com o mesmo nome, o CVAL usa o valor solicitado como evidência:
+ - prefere correspondência exata;
+ - para ajuste parcial, escolhe a menor ocorrência que comporte o valor solicitado;
+ - se nenhuma ocorrência comportar o valor, usa a maior ocorrência para que a regra genérica `validated > item_amount` bloqueie a solicitação.
+
+Nenhuma regra específica para "dobro", "triplo" ou percentual foi adicionada.
+
+## Regressões cobertas
+
+- `19.99` permanece `19.99`;
+- `R$ 19,99` vira `19.99`;
+- `Tamboro Mensal` em `14.99` e `19.99` + solicitação `19.99` resolve a ocorrência correta;
+- `Tamboro Mensal` em `14.99` e `19.99` + solicitação `29.98` é bloqueada com `valor_ajuste_maior_que_item`.
diff --git a/tests/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md b/tests/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md
new file mode 100644
index 0000000..ecbf7ee
--- /dev/null
+++ b/tests/docs/FIX_SCENARIO_23_LIVE_INTERNET_BALANCE_20260831.md
@@ -0,0 +1,29 @@
+# Correção do cenário 23 — saldo de internet em tempo real
+
+## Problema
+
+O FaturasAgent respondia que não havia informação nos "dados consultados" sobre o saldo de internet restante mesmo quando nenhuma tool de consumo em tempo real havia sido executada. Isso criava uma alegação de consulta sem evidência funcional.
+
+## Solução
+
+A correção foi mantida no agente, sem alteração do framework. Para perguntas explícitas de saldo/franquia restante em tempo real, o FaturasAgent:
+
+1. verifica se alguma tool bem-sucedida trouxe evidência de saldo/consumo atual;
+2. se houver evidência, deixa o fluxo normal responder com os dados reais;
+3. se não houver evidência, não afirma que consultou dados e orienta o cliente ao Meu TIM.
+
+Resposta de fallback:
+
+> Não consigo consultar o saldo de internet em tempo real por aqui. Para ver quanto ainda resta neste mês, consulte o app Meu TIM, onde você acompanha o consumo atual da sua franquia.
+
+A regra não intercepta perguntas genéricas sobre internet/plano e não bloqueia uma integração futura que passe a devolver saldo real.
+
+## Arquivo alterado
+
+- `app/agents/faturas_agent.py`
+
+## Testes
+
+- `tests/migration/test_scenario_23_live_internet_balance_guidance.py`
+- 3/3 testes novos PASS
+- regressão `tests/migration`: 821 PASS / 2 FAIL preexistentes
diff --git a/tests/docs/FIX_TRANSACTION_PARAMETER_PRECEDENCE_SEMANTIC_CLASSIFIER_20260828.md b/tests/docs/FIX_TRANSACTION_PARAMETER_PRECEDENCE_SEMANTIC_CLASSIFIER_20260828.md
new file mode 100644
index 0000000..26db861
--- /dev/null
+++ b/tests/docs/FIX_TRANSACTION_PARAMETER_PRECEDENCE_SEMANTIC_CLASSIFIER_20260828.md
@@ -0,0 +1,46 @@
+# Correção: precedência de parâmetros sobre semantic intent shift
+
+## Problema
+
+Durante uma transação ativa em `COLLECTING_PARAMETERS`, o roteador executava o
+`semantic_classifier` de mudança de intenção **antes** da extração dos parâmetros
+quando `ENABLE_LLM_ROUTER=true`. Com isso, respostas referenciais válidas, como
+`"a de quatorze e noventa e nove"`, podiam ser roubadas por outra intent
+semanticamente plausível antes de o contrato da transação tentar consumi-las.
+
+## Regra restaurada
+
+A ordem agora é:
+
+1. `AWAITING_CONFIRMATION`: confirmação explícita continua com precedência absoluta.
+2. `COLLECTING_PARAMETERS`: tentar primeiro extrair pelo menos um parâmetro pendente.
+3. Se algum parâmetro for consumido, manter a transação e **não** executar intent shift.
+4. Somente quando nenhum parâmetro for consumido, avaliar `semantic_classifier` para
+ `CONTINUE`/`SHIFT`.
+5. Um novo objetivo explícito continua podendo mudar a intenção, desde que o extrator
+ corretamente não o converta em parâmetro da transação anterior.
+
+## Resolução contextual
+
+O extrator do roteador agora recebe um contexto conversacional recente e limitado,
+apenas como auxílio não-autoritativo para resolver referências. Exemplo: se o histórico
+recente contém `Tamboro Mensal = R$ 14,99`, a fala `"a de 14,99"` pode produzir o
+candidato `subject=Tamboro Mensal`. A validação/pre-validation da transação continua
+sendo responsável por provar a entidade contra evidência de backend/MCP antes da
+confirmação ou execução.
+
+## Arquivo principal alterado
+
+- `agent_framework_oci/libs/agent_framework/src/agent_framework/routing/enterprise_router.py`
+
+## Testes
+
+Foram atualizados/adicionados testes em:
+
+- `agent_framework_oci/tests/test_transaction_parameter_llm_precedence.py`
+
+Validação executada:
+
+- 8/8 testes do arquivo de precedência passaram.
+- 81/81 testes combinados de transaction routing, state interruption, contextual reentry,
+ expected input semantic classifier e route stickiness passaram.
diff --git a/tests/docs/FIX_TRANSACTION_REQUIRED_FIELD_CORRECTION_PRECEDENCE_20260829.md b/tests/docs/FIX_TRANSACTION_REQUIRED_FIELD_CORRECTION_PRECEDENCE_20260829.md
new file mode 100644
index 0000000..45d8e43
--- /dev/null
+++ b/tests/docs/FIX_TRANSACTION_REQUIRED_FIELD_CORRECTION_PRECEDENCE_20260829.md
@@ -0,0 +1,51 @@
+# Correção: valor já coletado pode ser corrigido durante COLLECTING_PARAMETERS
+
+## Problema
+
+Uma transação podia estar em `COLLECTING_PARAMETERS` com um campo obrigatório já preenchido em turno anterior (por exemplo `valor=19.99`) e outro ainda pendente (`subject`). Se o cliente corrigisse o valor no mesmo turno em que identificava o item — por exemplo `desculpa, é a de quatorze e noventa e nove` — o runtime enviava ao extrator LLM apenas os parâmetros ainda ausentes. Assim, `valor` ficava fora do contrato editável do turno e permanecia congelado em `19.99`.
+
+Isso gerava estados inconsistentes como `resolved_value=14.99` e `valor=19.99`, fazendo a contestação executar com o valor antigo.
+
+## Regra corrigida
+
+Enquanto a transação estiver em `COLLECTING_PARAMETERS`, o extrator transacional recebe o conjunto completo de `policy.requires` como campos editáveis do turno. A LLM continua autorizada a devolver somente valores realmente presentes/inequívocos na fala atual. O merge mantém os valores antigos para campos não citados e sobrescreve apenas as chaves efetivamente extraídas.
+
+Precedência resultante:
+
+1. fala atual explicitamente corrige/preenche required field;
+2. valor previamente coletado é preservado apenas se a fala atual não o alterar;
+3. parâmetros ainda ausentes continuam sendo coletados;
+4. somente depois disso é avaliada mudança de intenção.
+
+## Caso de regressão coberto
+
+Estado anterior:
+
+- `valor=19.99`
+- `subject` pendente
+
+Mensagem atual:
+
+- `desculpa, é a de quatorze e noventa e nove`
+
+Router/contexto resolve:
+
+- `subject=Tamboro Mensal`
+
+Extrator do runtime corrige:
+
+- `valor=14.99`
+
+Resultado esperado antes da confirmação:
+
+- `subject=Tamboro Mensal`
+- `valor=14.99`
+
+## Testes
+
+Foram executados:
+
+- 46 testes de runtime/roteamento/parâmetros transacionais;
+- 27 testes de migração ligados a contestação/CVAL/paridade.
+
+Todos passaram.
diff --git a/tests/docs/LEGACY_INTEGRATION_PARITY_20260822.md b/tests/docs/LEGACY_INTEGRATION_PARITY_20260822.md
new file mode 100644
index 0000000..ab174e6
--- /dev/null
+++ b/tests/docs/LEGACY_INTEGRATION_PARITY_20260822.md
@@ -0,0 +1,104 @@
+# Paridade de Integração Contas — Mock x Sistemas Reais
+
+Data: 2026-08-22
+
+## Objetivo
+
+Validar o agente Contas migrado contra o código original, usando o legado como fonte de verdade para contratos HTTP, autenticação, headers, payloads, URLs, timeouts e comportamento de integração. O objetivo é manter o funcionamento com mocks locais sem impedir a execução contra sistemas reais quando `TIM_GATEWAY_MODE`/`TIM_USE_MOCK_GATEWAY` forem configurados para modo real.
+
+## Correção crítica — identidade do item transacional
+
+Foi corrigido o caso em que um pedido explícito para `TIM CTRL Redes Sociais 8.0` podia ser reinterpretado pelo matcher fuzzy como `TIM Fashion Mensal`.
+
+### Causa
+
+O `InvoiceResolver` eliminava seções não transacionáveis (por exemplo, planos) antes da resolução de identidade e, em seguida, aplicava similaridade somente sobre VAS. Como `TIM Fashion Mensal` ultrapassava o threshold do matcher, o `subject` era substituído antes da execução.
+
+### Correção
+
+- A resolução exata de identidade agora acontece antes de qualquer fuzzy matching.
+- A busca exata considera também itens fora do escopo transacional, como planos.
+- Um plano encontrado exatamente é classificado como `out_of_scope` para a operação VAS.
+- O fuzzy matching continua restrito aos candidatos realmente tratáveis.
+- `resolve_items()` preserva o comportamento anterior onde necessário para compatibilidade; o fluxo operacional usa a proteção de identidade.
+
+Resultado esperado para o caso:
+
+`TIM CTRL Redes Sociais 8.0` -> exact match -> `plano` -> `out_of_scope` -> não substituir por outro VAS -> não executar cancelamento.
+
+## Comparação com o código original
+
+Foram comparados os comandos do projeto original (`agente_contas_tim/commands`), `factory.py`, `config.py` e o gateway HTTP com o adaptador atual `app/domain/contas/client.py`.
+
+| Serviço/Integração | Contrato encontrado no original | Situação no migrado após revisão |
+|---|---|---|
+| Consulta VAS | GET, URL com `{msisdn}` ou append `/msisdn`, normalização para prefixo 55, clientId | Corrigido: aliases originais, timeout, append e prefixo 55 |
+| Histórico VAS | GET com `?msisdn=`, clientId/messageId/auth | Corrigido default `clientId=AIAGENTCR` |
+| Bloqueio VAS | POST, contratos de payload `pmid`/`input`/`vasBlock`, headers extras | Corrigidos aliases de URL/auth/timeout/clientId/operation/payload/encoding |
+| Cancelamento VAS | DELETE, body com channel/msisdn/appId/cspId/interactionProtocol, OAM/CN/type opcionais | Corrigidos aliases `TIM_CANCELLATION_*` e `TIM_CANCELAMENTO_*` |
+| Divergência / explicação de fatura | GET `/?channel=AIAGENTCR`, Basic opcional user/password, clientID | Corrigidos aliases, Basic auth e timeout |
+| CompleteInvoices | POST `{"msisdn": ...}`, `ClientID=AIAGENTCR` | Compatível; timeout respeitado |
+| Profile bill | Factory original usa configuração de CompleteInvoices | Corrigido para priorizar contrato/config de CompleteInvoices |
+| Profile full | GET com placeholder ou append `/msisdn`, `ClientID=AIAGENTCR` | Corrigido append e timeout |
+| Line info | Mesmo padrão de URL do profile full | Corrigido append e timeout |
+| Contrato | GET `/`, clientId do legado | Corrigido default `AIAGENTCR` |
+| Protocolo V2 | POST serviceRequest/interaction, headers opcionais OAM/CN/type | Mantido no adaptador atual |
+| Contestação do cliente | POST, clientId/messageId/X-Agent-Id, user configurável | Corrigido default via `TIM_CUSTOMER_CONTESTATION_USER_ID` |
+| Atualização de Service Request | POST, channel/serviceRequest, headers de integração | Mantido |
+| Tracking Activities | POST com customer/protocol/invoice/activity/user | Mantido |
+| SMS | POST com msisdn/sender/message/URL e receipt opcional | Mantido |
+| Bill PDF detalhada | POST com invoiceId/customerId e invoiceType `DETALHADA`, retorno PDF | Aliases ampliados |
+| Secure PDF / invoice recover | GET com invoiceId/msisdn/customerId | Aliases/header ajustados |
+
+## Configurações presentes no legado sem uso operacional comprovado
+
+- `status_customer`: configuração encontrada, mas sem comando/runtime consumidor localizado na revisão.
+- configuração OAuth específica de SMS: declarada no config original, mas sem consumidor runtime localizado.
+
+Esses itens não foram tratados como requisito ativo sem evidência de uso no código original.
+
+## Mock x modo real
+
+O mock continua suportado. Em mock, o cliente retorna fixtures locais para as operações previstas. Em modo real, o mesmo adaptador segue os contratos HTTP reconstruídos a partir do código original.
+
+A principal diferença de risco é que um mock tende a responder `200/OK` para cenários preparados. Por isso, a validação de identidade deve ocorrer antes do gateway — como agora ocorre — para impedir que um erro de resolução de entidade seja mascarado pelo mock e, principalmente, que chegue a um backend real.
+
+## Testes executados
+
+### Regressão + contratos existentes
+
+- 101 testes passaram no conjunto de contratos, paridade, idempotência e resolução.
+
+### Novos testes de compatibilidade legado/real
+
+- 7 testes passaram cobrindo:
+ - prefixo 55 e composição da URL de consulta VAS;
+ - aliases originais e timeout;
+ - aliases de cancelamento;
+ - append de MSISDN em profile full;
+ - Basic auth de divergência via usuário/senha;
+ - client IDs de histórico VAS e contrato;
+ - uso de CompleteInvoices no profile bill.
+
+### Suite `tests/migration`
+
+Resultado observado após as mudanças:
+
+- 660 passed
+- 4 failed
+
+As quatro falhas remanescentes são de configuração/contexto de guardrails (`conversation_history` e FRASEOLOGIA) e não estão relacionadas ao `InvoiceResolver` nem aos contratos de integração revisados.
+
+## Limite desta validação
+
+A revisão comprova paridade de contrato em nível de código-fonte e testes locais. Ela não é uma certificação de conectividade real porque não foram usados endpoints, credenciais ou rede dos sistemas legados neste ambiente.
+
+Para homologação real, recomenda-se executar testes de contrato contra um ambiente não produtivo dos serviços TIM, verificando status HTTP, schemas reais, autenticação, timeouts, headers obrigatórios e respostas de erro.
+
+## Gaps de endurecimento recomendados
+
+1. Adicionar validação de readiness no startup quando `mock=false`, falhando cedo se endpoint/auth obrigatórios estiverem ausentes.
+2. Criar testes de contrato contra ambiente de homologação para cada integração ativa.
+3. Comparar periodicamente fixtures mock com schemas/respostas reais para evitar drift.
+4. Manter invariantes transacionais: item solicitado, item resolvido e item executado nunca podem divergir silenciosamente.
+5. Evoluir mascaramento/observabilidade do cliente migrado para o mesmo nível do `HttpGateway` original, sem registrar secrets.
diff --git a/tests/docs/MANUAL_AGENT_CONTAS_MIGRADO.md b/tests/docs/MANUAL_AGENT_CONTAS_MIGRADO.md
new file mode 100644
index 0000000..c792196
--- /dev/null
+++ b/tests/docs/MANUAL_AGENT_CONTAS_MIGRADO.md
@@ -0,0 +1,472 @@
+# Manual do Agent Contas Migrado para agent_framework_oci
+
+## 1. Objetivo
+
+Esta versão reconstrói o Agent Contas sobre o `agent_framework_oci` com uma regra arquitetural simples: **o código executável novo não depende do pacote anterior do Contas**. O projeto anterior é apenas referência funcional para preservar regras, contratos de API, fixtures e comportamentos de negócio durante a migração.
+
+O agente novo reutiliza do framework tudo que é infraestrutura genérica: LangGraph, router, stickiness, supervisor, confirmação transacional, clarificação, memória, summary memory, long-term memory, checkpoints, persistence, RAG, embeddings, MCP Tool Router, guardrails, output supervisor, judges, identity, channels, SSE, usage accounting e telemetria.
+
+O novo domínio Contas mantém somente o que é realmente específico da TIM: chamadas de faturas, VAS, contestação, protocolos, tracking, SMS, Secure PDF e regras que relacionam essas operações.
+
+## 2. Regra de independência
+
+A Definition of Done da migração é:
+
+```bash
+grep -R "agente_contas_tim" app mcp config
+```
+
+Resultado esperado: nenhuma ocorrência/import do pacote anterior.
+
+O pacote entregue já inclui `tests/migration/test_no_legacy_dependency.py` para impedir regressão dessa regra.
+
+## 3. Arquitetura
+
+```text
+Canal / Frontend
+ |
+ v
+app/main.py
+ |
+ v
+agent_framework_oci
+ |-- ChannelGateway / IdentityResolver
+ |-- LangGraph / AgentWorkflow
+ |-- EnterpriseRouter / Route Stickiness / Supervisor
+ |-- Guardrails / Output Supervisor / Judges
+ |-- Memory / Summary Memory / LTM / Checkpoints
+ |-- RAG / Embeddings / Cache
+ |-- MCPToolRouter
+ |-- Langfuse / Analytics / OTEL / OCI Streaming
+ |
+ v
+MCP Contas :8400
+ |
+ v
+app/domain/contas
+ |-- TimApiClient
+ |-- ContasDomainService
+ `-- fixtures de desenvolvimento
+ |
+ v
+APIs TIM
+```
+
+Não existe um segundo LangGraph, LLM gateway, confirmation manager, workflow engine ou memory store dentro do MCP.
+
+## 4. Agentes de domínio
+
+A versão migrada possui quatro agentes reais do domínio Contas:
+
+| Agente | Responsabilidade |
+|---|---|
+| `faturas_agent` | Consulta de faturas, composição, variação e explicação de cobrança |
+| `vas_agent` | Consulta de VAS, histórico, serviços estratégicos/bundles e informação de serviços |
+| `contestacao_agent` | Cancelamento transacional de VAS e contestação de cobrança |
+| `suporte_contas_agent` | Protocolos, acompanhamento, suporte e encerramento |
+
+Todos herdam `AgentRuntimeMixin` do framework. Eles não implementam máquina de confirmação/clarificação própria.
+
+## 5. LangGraph do framework
+
+O fluxo principal é o `StateGraph` do `agent_framework_oci` usado em `app/workflows/agent_graph.py`:
+
+```text
+START
+ -> input_guardrails
+ -> load_long_term_memory
+ -> routing_decision
+ -> agente de domínio
+ -> output_supervisor
+ -> output_guardrails
+ -> judge
+ -> supervisor_review
+ -> persist_long_term_memory
+ -> persist
+ -> END
+```
+
+O `EnterpriseRouter` decide a intent, agente e tools. O route stickiness decide continuidade da conversa. O runtime do framework controla coleta de parâmetros e confirmação de tools transacionais.
+
+## 6. Transações
+
+As tools abaixo são transacionais em `config/tool_policies.yaml`:
+
+- `cancelar_vas_avulso`
+- `tratar_vas_estrategico`
+- `contestar_cobranca`
+
+A confirmação ocorre **antes** da chamada MCP e é responsabilidade do `AgentRuntimeMixin`. O domínio recebe a chamada somente depois de a política do framework permitir execução.
+
+Isso evita o problema clássico de um "sim" responder à pergunta errada: a confirmação está vinculada ao estado transacional/tool pendente do framework, não a heurísticas no prompt.
+
+## 7. RAG
+
+Conhecimento conceitual não é uma API TIM e por isso não é implementado como "workflow de busca" dentro do MCP.
+
+O projeto usa diretamente:
+
+- `RagService`
+- `create_embedding_provider()`
+- `VECTOR_STORE_PROVIDER`
+- `GRAPH_STORE_PROVIDER`
+- `EMBEDDING_PROVIDER`
+
+`buscar_informacao` permanece desabilitada no catálogo MCP; perguntas de conhecimento passam pelo RAG nativo do framework.
+
+## 8. Funcionalidades migradas
+
+| Funcionalidade do Contas | Nova implementação | Responsabilidade do framework |
+|---|---|---|
+| Consulta de faturas | `ContasDomainService.consultar_faturas` | seleção da tool, identity, cache, resposta |
+| Explicação de fatura | API de fatura + billing analysis como evidência | LLM produz explicação grounded |
+| Consulta VAS | `consultar_vas` | routing/tool selection |
+| Histórico VAS | `consultar_historico_vas` | routing/tool selection |
+| Cancelamento VAS avulso | consulta -> match -> bloqueio -> cancelamento | parâmetros + confirmação + estado |
+| VAS estratégico/bundle | domínio retorna serviço e orientação | conversa/continuidade no LangGraph |
+| Contestação | faturas/contrato/profile -> protocolo -> contestação -> tracking | parâmetros + confirmação + estado |
+| Status de solicitação | `consultar_status_solicitacao` | roteamento e contexto |
+| SMS | `enviar_sms` | tool policy/contexto |
+| Secure PDF | `recuperar_fatura_pdf` | roteamento/identity |
+| Encerramento | efeitos de domínio opcionais | `end_session`, memória e telemetria |
+| Guardrails | nenhum código local duplicado | framework |
+| Judges | nenhum código local duplicado | framework |
+| Memória | nenhum store local de conversa | framework |
+| LTM | nenhum mecanismo local | framework |
+| Checkpoint | nenhum `MemorySaver` dentro do MCP | framework |
+| Telemetria | eventos do runtime/framework | framework |
+
+## 9. Estrutura do projeto
+
+```text
+app/
+ main.py
+ state.py
+ agents/
+ faturas_agent.py
+ vas_agent.py
+ contestacao_agent.py
+ suporte_contas_agent.py
+ domain/contas/
+ client.py
+ service.py
+ fixtures/
+ workflows/
+ agent_graph.py
+ observability/
+
+config/
+ routing.yaml
+ tools.yaml
+ tool_policies.yaml
+ mcp_servers.yaml
+ mcp_parameter_mapping.yaml
+ identity.yaml
+ prompts/
+
+contas_mcp/servers/contas_mcp_server/
+ main.py
+
+agent_framework_oci/
+ ... framework reutilizado ...
+```
+
+## 10. Arquivo `.env`
+
+O `.env` fornecido para esta reconstrução foi preservado byte a byte no pacote. Não foi recomposto nem reduzido.
+
+O arquivo contém dois grupos:
+
+1. configurações do `agent_framework_oci`;
+2. variáveis TIM de domínio/compatibilidade já compiladas para os ambientes.
+
+Embora algumas variáveis antigas possam deixar de ser usadas depois da migração, elas foram mantidas para não perder o trabalho de consolidação. A remoção deve ocorrer apenas após testes de DEV/FQA/PRD.
+
+### Variáveis principais do framework
+
+- `LLM_PROVIDER`
+- `OCI_AUTH_MODE`, `OCI_CONFIG_FILE`, `OCI_PROFILE`, `OCI_COMPARTMENT_ID`, `OCI_REGION`
+- `SESSION_REPOSITORY_PROVIDER`
+- `MEMORY_REPOSITORY_PROVIDER`
+- `CHECKPOINT_REPOSITORY_PROVIDER`
+- `VECTOR_STORE_PROVIDER`
+- `GRAPH_STORE_PROVIDER`
+- `EMBEDDING_PROVIDER`
+- `ENABLE_LANGFUSE`
+- `ENABLE_INPUT_GUARDRAILS`
+- `ENABLE_OUTPUT_GUARDRAILS`
+- `ENABLE_JUDGES`
+- `ENABLE_SUPERVISOR`
+- `ENABLE_ROUTE_STICKINESS`
+- `ENABLE_MCP_TOOLS`
+- `ENABLE_CONVERSATION_SUMMARY_MEMORY`
+- `ENABLE_LONG_TERM_MEMORY`
+
+### Modo mock x APIs TIM reais
+
+Mock atual:
+
+```env
+TIM_GATEWAY_MODE=mock
+TIM_USE_MOCK_GATEWAY=true
+```
+
+Integrações reais:
+
+```env
+TIM_GATEWAY_MODE=real
+TIM_USE_MOCK_GATEWAY=false
+```
+
+Os dois valores devem estar coerentes.
+
+## 11. Integrações TIM
+
+| Integração | Variável | Método | VPN TIM provável |
+|---|---|---|---|
+| Complete Invoices | `TIM_COMPLETE_INVOICES_URL` | POST | Sim em FQA interno |
+| Billing Analysis | `TIM_DIVERGENCIA_URL` | POST | Sim |
+| Consulta VAS | `TIM_URL_CONSULTA_VAS` | GET | Sim |
+| Histórico VAS | `TIM_VAS_HISTORY_URL` | GET | Sim |
+| Bloqueio VAS | `TIM_URL_BLOQUEIO_VAS` | POST | Sim |
+| Cancelamento VAS | `TIM_CANCELAMENTO_URL` | DELETE | Sim |
+| Contrato | `TIM_CONTRATO_URL` | GET | Sim |
+| Full Profile | `TIM_PROFILE_FULL_URL` | GET | Sim |
+| Contestação | `TIM_CUSTOMER_CONTESTATION_URL` | POST | Sim |
+| Protocolo | `TIM_PROTOCOL_URL` | POST | Sim |
+| Service Request Status | `TIM_SERVICE_REQUEST_STATUS_URL` | POST | Sim |
+| Tracking Activities | `TIM_TRACKING_ACTIVITIES_URL` | POST | Sim |
+| SMS | `TIM_SMS_URL` | POST | Sim |
+| Secure PDF | `TIM_URL_INVOICE_RECOVER` | POST | Sim |
+
+Os endpoints FQA do `.env` usam `pmidfqa.internal.timbrasil.com.br`; portanto DNS/rota corporativa precisa estar disponível para teste real.
+
+## 12. Outras integrações
+
+| Integração | Uso | Ativação |
+|---|---|---|
+| OCI GenAI | LLM | `LLM_PROVIDER=oci_sdk` + `OCI_AUTH_MODE` |
+| Autonomous DB | session/memory/checkpoint/vector/usage | providers `autonomous` + `ADB_*` |
+| OCI Embeddings | RAG | `EMBEDDING_PROVIDER=oci` |
+| Langfuse | tracing | `ENABLE_LANGFUSE=true` |
+| GCP Pub/Sub | analytics corporativo | `ENABLE_ANALYTICS=true`, provider Pub/Sub e credencial GCP |
+| MongoDB | sequence Pub/Sub | `PUBSUB_SEQUENCE_PROVIDER=mongodb` |
+| Redis | cache/sequence opcional | `ENABLE_REDIS_CACHE=true` ou provider sequence redis |
+| OTEL | logs/traces | `ENABLE_OTEL=true` + endpoint |
+| OCI Streaming | eventos alternativos | `ENABLE_OCI_STREAMING=true` |
+
+## 13. Instalação local
+
+Recomendado: Linux/WSL com Python 3.13.
+
+```bash
+cd contas_migrado_framework_native
+uv sync
+```
+
+Se o `uv` ainda não estiver disponível, instale-o conforme o padrão do seu ambiente e depois execute `uv sync`.
+
+## 14. Subir o MCP Contas
+
+Terminal 1:
+
+```bash
+uv run uvicorn contas_mcp.servers.contas_mcp_server.main:app \
+ --host 0.0.0.0 --port 8400
+```
+
+Validar:
+
+```bash
+curl http://localhost:8400/health
+curl http://localhost:8400/mcp/tools/list
+```
+
+`/health` deve reportar:
+
+```json
+{
+ "status": "ok",
+ "architecture": "framework-native",
+ "legacy_dependency": false
+}
+```
+
+## 15. Subir o Agent Contas
+
+Terminal 2:
+
+```bash
+uv run uvicorn app.main:app --host 0.0.0.0 --port 8000
+```
+
+Validar:
+
+```bash
+curl http://localhost:8000/health
+```
+
+## 16. Smoke test do MCP em mock
+
+```bash
+PYTHONPATH=".:agent_framework_oci/libs/agent_framework/src" \
+python scripts/smoke_mcp.py
+```
+
+Esse teste cobre faturas, invoice explanation, VAS, histórico, cancelamento e contestação usando fixtures migradas para o novo domínio.
+
+## 17. Testar o agente pelo Gateway
+
+Exemplo de consulta:
+
+```bash
+curl -X POST http://localhost:8000/gateway/message \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "channel":"web",
+ "agent_id":"telecom_contas",
+ "tenant_id":"default",
+ "payload":{
+ "text":"Quero consultar minha fatura",
+ "session_id":"contas-test-001",
+ "user_id":"user-001",
+ "msisdn":"11999999999",
+ "message_id":"msg-001"
+ }
+ }'
+```
+
+Depois teste continuidade na mesma `session_id`.
+
+## 18. Teste transacional de VAS
+
+1. Envie: `Quero cancelar TIM Fashion Mensal`.
+2. O framework deve identificar tool transacional e pedir confirmação.
+3. Responda `sim` na mesma sessão.
+4. Somente então `cancelar_vas_avulso` deve ser chamada.
+5. Em modo mock, o resultado deve conter `block.status=200` e `cancellation.status=200`.
+
+Teste negativo importante:
+
+1. Entre em estado aguardando confirmação de cancelamento.
+2. Envie `você ainda está por aí?`.
+3. A frase não pode confirmar a transação.
+
+## 19. Teste de contestação
+
+Em mock:
+
+```text
+Quero contestar Tamboro Mensal no valor de 14,99, não reconheço essa cobrança.
+```
+
+Esperado:
+
+- route `contestacao_agent`;
+- parâmetros `subject` e `valor` coletados;
+- confirmação antes da mutação;
+- abertura de protocolo;
+- contestação;
+- tracking;
+- resposta final grounded nos retornos da tool.
+
+## 20. Testes de memória
+
+### Short-term / summary
+
+Na mesma sessão:
+
+```text
+Meu serviço é TIM Fashion Mensal.
+...
+Qual serviço eu mencionei antes?
+```
+
+### Long-term memory
+
+Com `ENABLE_LONG_TERM_MEMORY=true`, grave uma informação elegível, encerre a sessão e abra outra sessão com a mesma identidade de negócio. Verifique se o contexto é recuperado conforme as regras de LTM do framework.
+
+## 21. Testes de RAG
+
+Valide que a intent de conhecimento não dispara MCP desnecessariamente. Exemplos:
+
+```text
+O que significa cobrança proporcional?
+Como funciona o vencimento da fatura?
+```
+
+O trace deve mostrar `RagService`; a tool `buscar_informacao` não precisa ser executada.
+
+## 22. Testes de guardrails e judges
+
+Com as flags habilitadas no `.env`:
+
+- prompt injection deve passar pelos input guardrails;
+- resposta candidata passa pelo Output Supervisor/output guardrails;
+- groundedness deve considerar `mcp_results` quando a resposta usa dados TIM;
+- judges rodam após a geração e antes da persistência final.
+
+Use o Langfuse para observar a sequência de nodes do LangGraph.
+
+## 23. Teste de conectividade/VPN
+
+O pacote contém:
+
+```bash
+python scripts/check_integrations.py
+```
+
+O script resolve DNS e testa TCP dos endpoints configurados. Execute antes e depois de conectar a VPN.
+
+Para APIs FQA, um resultado `DNS_FAIL`, `TCP_FAIL` ou timeout indica que a rede ainda não está pronta. Um `TCP_OK` prova conectividade de rede, mas não autenticação/contrato HTTP.
+
+## 24. Ativar APIs reais gradualmente
+
+Não habilite todas as mutações de uma vez. Ordem recomendada:
+
+1. VPN/DNS;
+2. `consultar_faturas`;
+3. `consultar_vas`;
+4. `consultar_historico_vas`;
+5. contrato/profile;
+6. billing analysis;
+7. Secure PDF;
+8. protocol/status/tracking;
+9. SMS;
+10. cancelamento VAS;
+11. contestação.
+
+Depois faça teste end-to-end completo.
+
+## 25. Kubernetes
+
+Use o mesmo `.env` como fonte para construir ConfigMap/Secret, separando segredos no mecanismo corporativo apropriado. O backend necessita alcançar:
+
+- MCP Contas;
+- OCI GenAI;
+- Autonomous DB;
+- Langfuse, se habilitado;
+- endpoints TIM internos em modo real;
+- providers de analytics habilitados.
+
+O MCP pode rodar no mesmo pod como sidecar ou, preferencialmente, como deployment/service separado. Configure `config/mcp_servers.yaml` para o DNS do Service Kubernetes.
+
+## 26. Critérios de aceite da migração
+
+A migração é considerada concluída quando:
+
+- [ ] zero imports/referências executáveis ao pacote anterior;
+- [ ] backend e MCP sobem após o diretório anterior ser removido;
+- [ ] read-only APIs funcionam em FQA;
+- [ ] cancelamento exige confirmação do framework e funciona em FQA;
+- [ ] contestação exige confirmação e reproduz efeitos esperados;
+- [ ] RAG usa `RagService` do framework;
+- [ ] memory/summary/LTM/checkpoint usam providers do framework;
+- [ ] guardrails e judges aparecem nos traces;
+- [ ] LangGraph é a única máquina de estados conversacional;
+- [ ] Pub/Sub/sequence/OTEL/Langfuse são validados conforme ambiente;
+- [ ] testes de carga são executados antes de produção.
+
+## 27. Segurança do `.env`
+
+O arquivo preservado contém material sensível. Ele foi mantido porque isso foi um requisito explícito da reconstrução. Para distribuição fora do ambiente controlado, rotacione credenciais expostas e substitua valores por Secrets/Vault/Key Vault/Kubernetes Secret conforme política corporativa.
diff --git a/tests/docs/MANUAL_DESENVOLVEDOR_WORKFLOWS_CONTAS.md b/tests/docs/MANUAL_DESENVOLVEDOR_WORKFLOWS_CONTAS.md
new file mode 100644
index 0000000..1311dcc
--- /dev/null
+++ b/tests/docs/MANUAL_DESENVOLVEDOR_WORKFLOWS_CONTAS.md
@@ -0,0 +1,1881 @@
+# Manual do Desenvolvedor — Workflows do Agente Contas
+
+> **Projeto de referência:** `agent_contas_fechado_COMPLETO_corrigido_v4`
+> **Pasta documentada:** `/workflows`
+> **Público:** desenvolvedores que precisam criar, alterar, depurar ou revisar jornadas determinísticas do agente Contas.
+
+---
+
+## 1. Objetivo deste manual
+
+A pasta `workflows/` contém a **orquestração declarativa de jornadas de negócio** do agente Contas. Ela não é apenas uma coleção de YAMLs: cada arquivo descreve um pequeno grafo de execução que o runtime genérico do `agent_framework_oci` carrega, valida, executa, pausa, retoma e finaliza.
+
+O princípio central é:
+
+> **O workflow decide a sequência, as condições e os pontos de pausa. A action executa a operação de domínio. O framework fornece o motor genérico.**
+
+Isso evita que regras de jornada fiquem escondidas em `if/else` dentro dos agentes ou do framework.
+
+Este documento explica:
+
+- como o versionamento dos workflows funciona;
+- como ler um arquivo `.vN.yaml`;
+- o significado de `name`, `version`, `start`, `nodes`, `edges`, `when`, `priority`, `pause`, `expected_input` e `resume_from`;
+- como funcionam `$.input`, `$.vars`, `$.output` e o estado interno;
+- como uma `action` declarada no YAML se conecta a Python;
+- como um workflow pausa e continua em outro turno;
+- como o classificador semântico de `expected_input` funciona;
+- como um workflow termina sem trocar o `session_id`;
+- como cada arquivo atual da pasta `workflows/` funciona, ponto a ponto;
+- como adicionar uma nova versão com segurança.
+
+---
+
+## 2. Estrutura atual da pasta
+
+```text
+workflows/
+├── buscar_fatura.active.yaml
+├── buscar_fatura.v1.yaml
+├── buscar_informacao.active.yaml
+├── buscar_informacao.v2.yaml
+├── cancelamento_vas_avulso.active.yaml
+├── cancelamento_vas_avulso.v1.yaml
+├── contestacao_tool.active.yaml
+├── contestacao_tool.v2.yaml
+├── finalizar_atendimento.active.yaml
+├── finalizar_atendimento.v1.yaml
+├── invoice_explanation.active.yaml
+├── invoice_explanation.v2.yaml
+├── pro_rata.active.yaml
+├── pro_rata.v3.yaml
+├── termino_desconto.active.yaml
+├── termino_desconto.v1.yaml
+├── valor_divergente.active.yaml
+├── valor_divergente.v1.yaml
+├── vas_estrategico.active.yaml
+└── vas_estrategico.v3.yaml
+```
+
+Há sempre dois papéis diferentes:
+
+1. **`.active.yaml`** — marcador da versão ativa;
+2. **`.vN.yaml`** — definição completa e versionada do grafo.
+
+Exemplo:
+
+```yaml
+# invoice_explanation.active.yaml
+version: 2
+```
+
+Esse arquivo não contém a lógica. Ele informa ao `FileWorkflowRepository` que, quando alguém pedir o workflow ativo `invoice_explanation`, deve ser carregado:
+
+```text
+invoice_explanation.v2.yaml
+```
+
+### Regra prática de versionamento
+
+Não altere silenciosamente a semântica de uma versão já publicada quando a mudança for incompatível ou material. Prefira:
+
+```text
+invoice_explanation.v2.yaml # versão atual
+invoice_explanation.v3.yaml # nova implementação
+invoice_explanation.active.yaml -> version: 3
+```
+
+Assim rollback e auditoria permanecem simples.
+
+---
+
+## 3. Quem faz o quê
+
+A arquitetura pode ser entendida em quatro camadas.
+
+```mermaid
+flowchart LR
+ U[Usuário] --> AG[Agente / Router]
+ AG --> W[Workflow YAML]
+ W --> RT[WorkflowRuntime do framework]
+ RT --> A[Actions Python do domínio Contas]
+ A --> S[Services / MCP / APIs legadas]
+ A --> RT
+ RT --> AG
+```
+
+### 3.1 Workflow YAML
+
+Responsável por:
+
+- sequência dos passos;
+- branching;
+- prioridade das transições;
+- definição de pausa;
+- contrato da resposta esperada do usuário;
+- nó de retomada;
+- declaração de valores fixos da jornada;
+- escolha de qual action executar.
+
+### 3.2 `WorkflowRuntime`
+
+É genérico e pertence ao framework. Ele:
+
+- carrega o YAML;
+- valida o grafo;
+- resolve expressões `$.…`;
+- executa actions;
+- mantém `vars`, `output`, `trace` e estado;
+- ordena edges por `priority`;
+- avalia `when`;
+- implementa pause/resume;
+- usa checkpoint do LangGraph em produção;
+- retorna `COMPLETED`, `PAUSED` ou `FAILED`.
+
+O runtime não deve conhecer regras específicas da TIM ou do Contas.
+
+### 3.3 Actions Python
+
+No Contas, a maioria das actions declaradas nos YAMLs é registrada em:
+
+```text
+app/domain/contas/workflow_actions.py
+```
+
+por meio de:
+
+```python
+reg = WorkflowActionRegistry()
+
+@reg.action("nome_da_action")
+def nome_da_action(params, state):
+ ...
+ return {...}
+```
+
+Uma action deve receber:
+
+```python
+params: dict
+state: dict
+```
+
+E deve retornar **sempre um `dict`**.
+
+### 3.4 Services / MCP / legado
+
+As actions podem chamar `ContasDomainService`, clientes HTTP, integrações TIM, mocks ou outros componentes. O YAML não deve conter código de transporte.
+
+---
+
+## 4. Anatomia de um workflow
+
+Um workflow mínimo é:
+
+```yaml
+name: exemplo
+version: 1
+start: primeiro
+
+nodes:
+ - id: primeiro
+ action: minha_action
+ input:
+ msisdn: $.input.msisdn
+
+edges:
+ - from: primeiro
+ to: END
+```
+
+### 4.1 `name`
+
+Nome lógico do workflow.
+
+Precisa ser coerente com o nome do arquivo:
+
+```text
+exemplo.v1.yaml
+name: exemplo
+version: 1
+```
+
+O repository valida essa correspondência.
+
+### 4.2 `version`
+
+Número inteiro da versão do contrato do workflow.
+
+### 4.3 `start`
+
+ID do primeiro nó executado.
+
+### 4.4 `nodes`
+
+Cada nó representa uma unidade de execução.
+
+```yaml
+- id: preparar
+ action: preparar_invoice_explanation
+ input:
+ msisdn: $.input.msisdn
+```
+
+O `id` é o nome do nó dentro do grafo. A `action` é o nome registrado no `WorkflowActionRegistry`.
+
+### 4.5 `edges`
+
+Definem para onde o grafo segue após um nó.
+
+```yaml
+- from: preparar
+ to: formatar
+```
+
+ou condicionalmente:
+
+```yaml
+- from: preparar
+ to: formatar
+ priority: 10
+ when:
+ eq: [$.vars.preparar.success, true]
+```
+
+### 4.6 `END`
+
+`END` representa término do grafo.
+
+```yaml
+- from: finalizar
+ to: END
+```
+
+---
+
+## 5. Modelo de estado e expressões `$.…`
+
+Essa é uma das partes mais importantes para quem altera a pasta `workflows/`.
+
+### 5.1 `$.input`
+
+Representa os dados de entrada da execução.
+
+Exemplo:
+
+```yaml
+input:
+ msisdn: $.input.msisdn
+ invoice_id: $.input.invoice_id
+```
+
+Se o workflow foi iniciado com:
+
+```json
+{
+ "msisdn": "11999999999",
+ "invoice_id": "3000131180"
+}
+```
+
+os dois valores serão passados à action.
+
+### 5.2 `$.vars.`
+
+Após cada action retornar um dicionário, o runtime armazena o resultado em:
+
+```text
+$.vars.
+```
+
+Exemplo:
+
+```yaml
+- id: registrar_protocolo
+ action: registrar_protocolo
+```
+
+Se a action retornar:
+
+```json
+{
+ "success": true,
+ "protocolo_id": "1234567890"
+}
+```
+
+então outro nó pode usar:
+
+```yaml
+protocolo_id: $.vars.registrar_protocolo.protocolo_id
+```
+
+### 5.3 `$.output`
+
+Aponta para o último resultado de action colocado como output corrente.
+
+É muito usado em `pause.return_from`:
+
+```yaml
+pause:
+ return_from: $.output.mensagem
+```
+
+### 5.4 `$.nodes`
+
+O runtime também mantém resultados por nó em uma estrutura de nós. Na maior parte dos workflows do Contas, `$.vars` é a forma declarativa utilizada para encadear dados.
+
+### 5.5 Exemplo encadeado
+
+```yaml
+- id: preparar
+ action: preparar
+
+- id: formatar
+ action: formatar
+ input:
+ dados: $.vars.preparar.dados
+```
+
+Fluxo:
+
+```text
+preparar() -> {dados: X}
+ |
+ v
+$.vars.preparar.dados
+ |
+ v
+formatar(dados=X)
+```
+
+---
+
+## 6. Conditions e prioridade de edges
+
+O runtime agrupa as edges por nó de origem e ordena por `priority` crescente.
+
+Portanto:
+
+```yaml
+priority: 10
+```
+
+é avaliada antes de:
+
+```yaml
+priority: 99
+```
+
+### 6.1 Fallback padrão
+
+Um padrão comum é:
+
+```yaml
+- from: action_x
+ to: caminho_especial
+ priority: 10
+ when:
+ eq: [$.vars.action_x.alguma_flag, true]
+
+- from: action_x
+ to: caminho_padrao
+ priority: 99
+```
+
+A edge de prioridade 99 funciona como fallback porque não possui `when`.
+
+### 6.2 Operadores encontrados nos workflows atuais
+
+Exemplos:
+
+```yaml
+when:
+ eq: [$.vars.preparar.success, true]
+```
+
+```yaml
+when:
+ neq: [$.vars.x.barcode, ""]
+```
+
+```yaml
+when:
+ exists: $.vars.x.barcode
+```
+
+```yaml
+when:
+ all:
+ - eq: [$.vars.x.a, true]
+ - eq: [$.vars.x.b, true]
+```
+
+```yaml
+when:
+ any:
+ - eq: [$.vars.x.a, true]
+ - eq: [$.vars.x.b, true]
+```
+
+### Regra importante
+
+As edges devem ser mutuamente compreensíveis. Se nenhuma edge corresponder, o runtime pode falhar com:
+
+```text
+Nenhuma transição do workflow correspondeu ao estado
+```
+
+Por isso fluxos condicionais normalmente possuem uma edge final sem `when`.
+
+---
+
+## 7. Pause / resume
+
+Workflows conversacionais podem parar no meio da execução para pedir uma resposta ao usuário.
+
+Exemplo simplificado:
+
+```yaml
+- id: formatar
+ action: formatar_invoice_explanation
+ pause:
+ enabled: true
+ return_from: $.output.mensagem
+ expected_input:
+ key: resposta_usuario
+ allowed_values: ["SIM", "NAO"]
+ normalize: upper_strip
+ resume_from: decisao
+```
+
+### 7.1 O que ocorre no primeiro turno
+
+1. `formatar_invoice_explanation` é executada;
+2. a mensagem produzida é obtida de `$.output.mensagem`;
+3. o runtime persiste o checkpoint;
+4. retorna `status=PAUSED`;
+5. a mensagem é enviada ao usuário;
+6. o workflow fica aguardando input.
+
+### 7.2 O que ocorre no turno seguinte
+
+A nova fala é validada contra `expected_input`.
+
+Se aceita:
+
+```text
+resposta do usuário
+ ↓
+normalize
+ ↓
+$.input.resposta_usuario
+ ↓
+resume_from: decisao
+```
+
+### 7.3 Por que pause é separado da action
+
+No runtime atual, pause/resume é implementado em um nó técnico separado. Isso é deliberado.
+
+Ao retomar, **a action anterior não é reexecutada**. Isso evita repetir efeitos externos como:
+
+- abrir protocolo duas vezes;
+- cancelar duas vezes;
+- enviar SMS novamente;
+- criar duas SRs.
+
+---
+
+## 8. `expected_input`
+
+Exemplo:
+
+```yaml
+expected_input:
+ key: resposta_usuario
+ allowed_values: ["SIM", "NAO", "OUTRO"]
+ normalize: upper_strip
+```
+
+### `key`
+
+Nome em que o valor normalizado será gravado em `$.input`.
+
+### `allowed_values`
+
+Valores internos permitidos para a decisão do workflow.
+
+Esses valores são **tokens de controle**, não necessariamente texto exibido ao cliente.
+
+### `normalize: upper_strip`
+
+Remove espaços laterais e converte para maiúsculas.
+
+Exemplo:
+
+```text
+" sim " -> "SIM"
+```
+
+### `reprompt`
+
+Mensagem utilizada quando o input não pode ser interpretado pelo contrato.
+
+---
+
+## 9. Semantic classifier de `expected_input`
+
+O `invoice_explanation` possui um classificador semântico declarativo.
+
+Ele existe porque respostas reais do usuário raramente são somente `sim` ou `não`.
+
+Exemplo:
+
+```text
+"entendi, obrigado, era só isso"
+```
+
+semanticamente é `SIM`.
+
+Já:
+
+```text
+"então no mês que vem vou pagar menos?"
+```
+
+não é `SIM`, mesmo contendo sinal de compreensão; é uma continuação da pergunta.
+
+O YAML define:
+
+```yaml
+semantic_classifier:
+ enabled: true
+ include_relevant_context: true
+ option_actions:
+ CONTINUAR:
+ action: contextual_reentry
+ prompt: |
+ ...
+```
+
+### 9.1 Responsabilidade correta
+
+- **Framework:** executa o classificador e garante que a saída esteja entre os valores permitidos.
+- **Workflow/agente:** define o significado de `SIM`, `NAO`, `CONTINUAR`.
+
+### 9.2 `contextual_reentry`
+
+Quando a opção classificada possui:
+
+```yaml
+CONTINUAR:
+ action: contextual_reentry
+```
+
+o workflow pausado não deve simplesmente tratar a fala como confirmação. A utterance é liberada para nova interpretação pelo roteamento normal, com contexto delimitado.
+
+Isso é particularmente importante para evitar que hipóteses do usuário virem fatos confirmados.
+
+---
+
+## 10. Estado terminal e nova interação na mesma sessão
+
+Um workflow pode terminar sem encerrar tecnicamente o `session_id`.
+
+Isso significa:
+
+```text
+mesma sessão técnica
+ !=
+mesmo workflow ativo
+```
+
+No `invoice_explanation`, por exemplo:
+
+```yaml
+workflow_response_final: true
+```
+
+indica que aquela action produz a resposta final daquele workflow.
+
+Depois de uma execução terminal, o framework deve eliminar o latch operacional do workflow para que o próximo turno seja uma nova entrada, ainda na mesma sessão.
+
+Deve permanecer:
+
+- `session_id`;
+- `session_key`;
+- `conversation_key`;
+- identidade do cliente;
+- auditoria e telemetria;
+- long-term memory.
+
+Não deve continuar controlando a próxima entrada:
+
+- `pending_domain_workflow`;
+- `expected_input`;
+- pause antigo;
+- active transaction antiga;
+- confirmação antiga;
+- route stickiness da jornada terminada;
+- short-term operational context do workflow fechado.
+
+Esse detalhe é fundamental ao depurar cenários como:
+
+```text
+Usuário: entendi, obrigado, era só isso
+Agente: Seu número de protocolo é ...
+Usuário: ah espera
+```
+
+O terceiro turno é uma nova entrada na mesma sessão, e não um resume do workflow anterior.
+
+---
+
+# 11. Workflows atuais — explicação arquivo por arquivo
+
+---
+
+## 11.1 `buscar_fatura.active.yaml`
+
+```yaml
+version: 1
+```
+
+Seleciona `buscar_fatura.v1.yaml` como versão ativa.
+
+## 11.2 `buscar_fatura.v1.yaml`
+
+### Objetivo
+
+Executar uma busca de fatura em um único passo.
+
+### Cabeçalho
+
+```yaml
+name: buscar_fatura
+version: 1
+start: buscar_fatura
+```
+
+### Nó `buscar_fatura`
+
+```yaml
+- id: buscar_fatura
+ action: buscar_fatura
+ input:
+ invoice_id: $.input.invoice_id
+ msisdn: $.input.msisdn
+ customer_id: $.input.customer_id
+ output: $.input.output
+```
+
+A action Python está em `app/domain/contas/workflow_actions.py`.
+
+Com `invoice_id` e `customer_id`, ela tenta buscar a fatura detalhada. Sem os identificadores necessários, usa `consultar_faturas` como fallback.
+
+### Edge
+
+```yaml
+- from: buscar_fatura
+ to: END
+```
+
+Não há branch nem pausa.
+
+### Modelo mental
+
+```text
+INPUT
+ |
+ v
+buscar_fatura
+ |
+ v
+END
+```
+
+### Quando usar
+
+Quando a operação é atômica e não precisa de confirmação do usuário.
+
+---
+
+## 11.3 `buscar_informacao.active.yaml`
+
+```yaml
+version: 2
+```
+
+Ativa `buscar_informacao.v2.yaml`.
+
+## 11.4 `buscar_informacao.v2.yaml`
+
+### Objetivo
+
+Preparar uma consulta RAG e, em seguida, preparar a resposta para composição pelo framework.
+
+### Nó 1 — `buscar_informacao`
+
+```yaml
+id: buscar_informacao
+action: buscar_informacao_rag
+```
+
+Recebe:
+
+- `query`;
+- `queries`;
+- `top_k`;
+- `segment`.
+
+A action atual devolve uma estrutura declarando `delegate_to_framework_rag=true`, isto é, o domínio sinaliza que a capacidade RAG deve ser executada pelo framework.
+
+### Nó 2 — `reescrever_resposta`
+
+Consome os resultados do primeiro nó:
+
+```yaml
+queries: $.vars.buscar_informacao.queries
+documents: $.vars.buscar_informacao.documents
+answer: $.vars.buscar_informacao.answer
+noMatchRag: $.vars.buscar_informacao.noMatchRag
+ragRetrievedDocuments: $.vars.buscar_informacao.ragRetrievedDocuments
+ragSelectedDocuments: $.vars.buscar_informacao.ragSelectedDocuments
+```
+
+A action `reescrever_resposta_buscar_informacao` devolve a mensagem e `delegate_to_framework_llm=true`.
+
+### Fluxo
+
+```text
+buscar_informacao_rag
+ |
+ v
+reescrever_resposta_buscar_informacao
+ |
+ v
+END
+```
+
+### Conceito importante
+
+A pasta `workflows/` orquestra, mas não deve implementar o mecanismo RAG. O framework continua responsável pelo runtime RAG.
+
+---
+
+## 11.5 `cancelamento_vas_avulso.active.yaml`
+
+```yaml
+version: 1
+```
+
+## 11.6 `cancelamento_vas_avulso.v1.yaml`
+
+### Objetivo
+
+Executar cancelamento em lote de VAS avulso.
+
+### Nó único
+
+```yaml
+id: cancelar_vas_avulso
+action: cancelamento_vas_avulso_batch
+```
+
+Entradas:
+
+- `items`;
+- `csp_id`;
+- `channel`;
+- `social_sec_no`;
+- `data_credito_proxima_fatura`;
+- `idempotency_key`.
+
+O YAML também fixa valores do contrato operacional:
+
+```yaml
+request_status: "Fechado"
+status: "CLOSED"
+```
+
+### Fluxo
+
+```text
+cancelamento_vas_avulso_batch -> END
+```
+
+### Ponto de atenção
+
+É uma operação transacional. A confirmação do usuário e as políticas de tool podem ocorrer antes da entrada no workflow. Não mova para o YAML uma duplicação de confirmation policy que pertença ao framework.
+
+O `idempotency_key` é especialmente importante em operações com efeito externo.
+
+---
+
+## 11.7 `contestacao_tool.active.yaml`
+
+```yaml
+version: 2
+```
+
+## 11.8 `contestacao_tool.v2.yaml`
+
+Este é o workflow mais complexo da pasta atual.
+
+### Objetivo
+
+Orquestrar a contestação de cobrança, incluindo protocolo, status da fatura, abertura da contestação, SMS quando aplicável, regra de corte, Conta Certa Manual e atualização de status.
+
+### Visão geral
+
+```mermaid
+flowchart TD
+ A[registrar_protocolo] --> B[check_invoice_status]
+ B --> C[abrir_contestacao_cliente]
+ C -->|success=false| Z[END]
+ C -->|tem barcode| D[enviar_sms]
+ C -->|sem SMS| E[consultar_contrato_corte]
+ D --> E
+ E -->|Conta Certa Manual elegível| F[abrir_sr_conta_certa_manual]
+ E -->|caso padrão| G[atualizar_status_sr]
+ F --> H[atualizar_status_sr_registro]
+ H --> Z
+ G --> Z
+```
+
+### Nó 1 — `registrar_protocolo`
+
+Primeiro efeito da jornada:
+
+```yaml
+action: registrar_protocolo
+```
+
+Configura:
+
+```yaml
+scenario: "contestacao"
+request_status: "Aberto"
+status: "OPENED"
+```
+
+O protocolo retornado fica acessível em:
+
+```text
+$.vars.registrar_protocolo.protocolo_id
+```
+
+### Nó 2 — `check_invoice_status`
+
+Consulta ou reaproveita o `CompleteInvoices` já obtido em prefetch.
+
+O comentário do YAML deixa clara a intenção arquitetural: evitar uma segunda chamada desnecessária quando o payload já existe na sessão.
+
+### Nó 3 — `abrir_contestacao_cliente`
+
+Recebe grande parte do contexto necessário para a operação financeira, inclusive:
+
+- cliente;
+- fatura;
+- serviço/item;
+- valor;
+- descrição;
+- protocolo;
+- tipo da contestação;
+- motivo do ajuste;
+- opção de devolução;
+- regras de Conta Certa Manual;
+- `double_refund`;
+- dados de atendimento;
+- `skip_invoice_item_validation`.
+
+O `invoice_status` vem do nó anterior:
+
+```yaml
+invoice_status: $.vars.check_invoice_status.invoice_status
+```
+
+### Branch de falha financeira
+
+```yaml
+- from: abrir_contestacao_cliente
+ to: END
+ priority: 1
+ when:
+ eq: [$.vars.abrir_contestacao_cliente.success, false]
+```
+
+É avaliado primeiro. Se a validação financeira bloquear a contestação, não deve executar SMS, contrato ou SR.
+
+### Branch de SMS
+
+```yaml
+when:
+ all:
+ - exists: $.vars.abrir_contestacao_cliente.barcode
+ - neq: [$.vars.abrir_contestacao_cliente.barcode, ""]
+```
+
+Se a contestação produzir código de boleto, o fluxo passa por `enviar_sms`.
+
+Caso contrário, a edge `priority: 99` segue diretamente para `consultar_contrato_corte`.
+
+### Nó `consultar_contrato_corte`
+
+Determina a regra de data de corte e também trata particularidade de item dependente de plano família.
+
+### Branch Conta Certa Manual
+
+O branch é propositalmente composto:
+
+```yaml
+when:
+ any:
+ - all:
+ - apos_data_corte == true
+ - contestation_success == true
+ - manual_conta_certa_indicator == true
+ - all:
+ - dependent_invoice_item == true
+ - contestation_registered == true
+```
+
+Isso expressa duas formas de elegibilidade:
+
+1. regra normal após data de corte + indicador manual;
+2. item de dependente cuja contestação foi registrada.
+
+### `abrir_sr_conta_certa_manual`
+
+Cria a SR de Conta Certa Manual.
+
+### `atualizar_status_sr`
+
+Caminho padrão, fecha/atualiza o protocolo principal.
+
+### `atualizar_status_sr_registro`
+
+Caminho usado após Conta Certa Manual, atualizando a SR correspondente.
+
+### Pontos de atenção
+
+- Não trocar prioridades sem revisar todos os branches.
+- `success=false` precisa continuar precedendo qualquer efeito posterior.
+- Reaproveitamento de prefetch evita chamadas duplicadas.
+- É um workflow com múltiplos efeitos externos; qualquer retry deve ser analisado com idempotência.
+
+---
+
+## 11.9 `finalizar_atendimento.active.yaml`
+
+```yaml
+version: 1
+```
+
+## 11.10 `finalizar_atendimento.v1.yaml`
+
+### Objetivo
+
+Centralizar o fechamento final do atendimento.
+
+### Nó único `finalizar`
+
+```yaml
+action: finalizar_atendimento_action
+```
+
+Entradas relevantes:
+
+- `status`;
+- `summary`;
+- `msisdn`;
+- `social_sec_no`;
+- `message_id`;
+- tipos informacionais de VAS;
+- protocolo;
+- flags de supressão/deferimento de eventos.
+
+### Fluxo
+
+```text
+finalizar_atendimento_action -> END
+```
+
+### Observação
+
+Finalizar atendimento é conceitualmente diferente de simplesmente alcançar `END` em qualquer workflow. `END` encerra aquele grafo; `finalizar_atendimento_action` implementa a semântica de negócio de fechamento de atendimento.
+
+---
+
+## 11.11 `invoice_explanation.active.yaml`
+
+```yaml
+version: 2
+```
+
+## 11.12 `invoice_explanation.v2.yaml`
+
+### Objetivo
+
+Explicar variação de fatura, aguardar a confirmação semântica do cliente e então:
+
+- registrar aceite e protocolo final; ou
+- registrar negativa e transferir para atendimento humano.
+
+Também possui caminhos de falha de serviço e validação de tentativa.
+
+### Visão principal
+
+```mermaid
+flowchart TD
+ A[preparar] -->|success| B[formatar]
+ A -->|service_failed| F[resposta_falha_servico]
+ A -->|success=false| C[checar_tentativa]
+ C -->|limite excedido| D[fim_intencao_invalida]
+ C -->|caso contrário| E[texto_intencao_invalida]
+ B --> P{{PAUSE}}
+ P --> G[decisao]
+ G -->|SIM| H[registrar_sim]
+ H --> I[registrar_protocolo_aceite]
+ I --> Z[END]
+ G -->|NAO| J[registrar_nao]
+ J --> K[handoff_pos_explicacao_nao]
+ K --> Z
+```
+
+### Nó `preparar`
+
+Action:
+
+```text
+preparar_invoice_explanation
+```
+
+Responsável por obter ou reutilizar a explicação base.
+
+Recebe dados da fatura atual/passada e também:
+
+```yaml
+tentativa_anterior: $.vars.preparar.tentativa
+```
+
+Isso permite controlar tentativas dentro do estado do workflow.
+
+### Nó `formatar`
+
+Action:
+
+```text
+formatar_invoice_explanation
+```
+
+Ela monta a mensagem, mas **não controla a pausa**. A pausa está declarada no YAML.
+
+Essa separação é intencional: apresentação e controle de fluxo não devem ficar acoplados.
+
+### Pause do `formatar`
+
+Sempre pausa após apresentar a explicação.
+
+```yaml
+allowed_values: ["SIM", "NAO", "CONTINUAR"]
+```
+
+O semantic classifier diferencia confirmação, negativa e continuação contextual.
+
+#### Exemplos
+
+```text
+"sim" -> SIM
+"entendi, obrigado" -> SIM
+"não resolveu" -> NAO
+"é a cobrança de 14,99" -> CONTINUAR
+"mês que vem fica mais barato?" -> CONTINUAR
+```
+
+### `decisao`
+
+É um `no_op`. Sua função é fornecer um ponto explícito no grafo para branching após o resume.
+
+### Caminho SIM
+
+```text
+decisao
+ -> registrar_sim
+ -> registrar_protocolo_aceite
+ -> END
+```
+
+`registrar_protocolo_aceite` usa:
+
+```yaml
+workflow_response_final: true
+```
+
+para indicar que a mensagem retornada é a resposta final do workflow.
+
+### Caminho NAO
+
+```text
+decisao
+ -> registrar_nao
+ -> handoff_pos_explicacao_nao
+ -> END
+```
+
+`preparar_handoff_invoice_explanation` materializa:
+
+```text
+session_control = HUMAN_HANDOFF
+```
+
+A decisão de jornada está no workflow do Contas; a primitive de handoff é do framework.
+
+### Caminhos de erro
+
+`preparar` distingue:
+
+- `success=true`;
+- `service_failed=true`;
+- `success=false` por validação/tentativa.
+
+`checar_tentativa` decide se ainda pode pedir novamente ou se o limite foi excedido.
+
+### Nós `checar_vas_variacao` e `finalizar_nao_resolvido`
+
+Esses nós estão declarados para a política de VAS variado/não resolvido. Observe que, na versão atual, não existe edge de entrada para `checar_vas_variacao` partindo do fluxo principal SIM/NAO mostrado acima. Antes de reutilizar ou alterar esses nós, valide a intenção de jornada e os testes associados.
+
+### Ponto crítico de lifecycle
+
+Quando `registrar_protocolo_aceite` produz `workflow_response_final=true`, o workflow deve ser considerado terminal mesmo que alguma integração legada devolva metadata antiga com `PAUSED`. O próximo turno na mesma sessão não deve reutilizar `expected_input` desse workflow.
+
+---
+
+## 11.13 `pro_rata.active.yaml`
+
+```yaml
+version: 3
+```
+
+## 11.14 `pro_rata.v3.yaml`
+
+### Objetivo
+
+Explicar cobrança proporcional (`pro rata`) e tratar de forma diferente clientes com Plano Controle.
+
+### Nó `preparar`
+
+Action:
+
+```text
+preparar_pro_rata
+```
+
+Recebe planos e `has_plano_controle`.
+
+A action decide se a jornada precisa interagir com o usuário:
+
+```text
+await_user_input = true/false
+```
+
+### Nó `formatar`
+
+Possui pause condicional:
+
+```yaml
+pause:
+ enabled: true
+ when:
+ eq: [$.vars.preparar.await_user_input, true]
+```
+
+Portanto, diferente do `invoice_explanation`, este workflow só pausa quando necessário.
+
+### Expected input
+
+```yaml
+allowed_values: ["SIM", "NAO", "OUTRO"]
+```
+
+### `decisao_esclarecimento`
+
+Branch:
+
+- `SIM` -> `registrar_aceitou`;
+- `NAO` -> `devolver_orquestrador`;
+- qualquer outra situação -> `reperguntar_esclarecimento`.
+
+### `reperguntar_esclarecimento`
+
+Formata novamente e pausa de novo, retomando em `decisao_esclarecimento`.
+
+Isso forma um pequeno loop conversacional controlado:
+
+```text
+reperguntar
+ |
+ pause
+ |
+ +----> decisao_esclarecimento
+```
+
+### Caso sem Plano Controle
+
+Se `await_user_input=false`, a edge de prioridade 20 sai de `formatar` para:
+
+```text
+registrar_nao_controle -> END
+```
+
+### Caso SIM
+
+```text
+registrar_aceitou -> END
+```
+
+Essa action também registra protocolo/evento apropriado.
+
+---
+
+## 11.15 `termino_desconto.active.yaml`
+
+```yaml
+version: 1
+```
+
+## 11.16 `termino_desconto.v1.yaml`
+
+### Objetivo
+
+Formatar a resposta da capability de término de desconto.
+
+### Nó único
+
+```yaml
+id: formatar
+action: formatar_capability_resposta
+```
+
+Com:
+
+```yaml
+tipo: termino_desconto
+```
+
+Além de dados do plano/fatura/evidência de desconto.
+
+### Conceito
+
+A mesma action genérica `formatar_capability_resposta` é parametrizada pelo `tipo` da capability.
+
+### Fluxo
+
+```text
+formatar_capability_resposta(tipo=termino_desconto) -> END
+```
+
+---
+
+## 11.17 `valor_divergente.active.yaml`
+
+```yaml
+version: 1
+```
+
+## 11.18 `valor_divergente.v1.yaml`
+
+### Objetivo
+
+Formatar resposta para a capability de valor divergente.
+
+### Nó único
+
+```yaml
+action: formatar_capability_resposta
+input:
+ tipo: valor_divergente
+ msisdn: $.input.msisdn
+```
+
+É estruturalmente semelhante ao `termino_desconto`, mas com outro `tipo` e conjunto de inputs.
+
+### Ponto de atenção
+
+Se a capability passar a exigir dados adicionais, prefira explicitá-los no YAML, mantendo claro o contrato entre workflow e action.
+
+---
+
+## 11.19 `vas_estrategico.active.yaml`
+
+```yaml
+version: 3
+```
+
+## 11.20 `vas_estrategico.v3.yaml`
+
+### Objetivo
+
+Tratar VAS estratégico e bundle, apresentar explicação, coletar aceite/negativa e registrar o resultado.
+
+### Visão geral
+
+```mermaid
+flowchart TD
+ A[preparar] -->|await_user_input| P{{PAUSE}}
+ A -->|sem pausa| B[resposta_bundle]
+ P --> C[decisao]
+ C -->|SIM| D[resposta_sim]
+ C -->|NAO e estratégico| E[explicar_cancelamento]
+ C -->|NAO bundle puro| B
+ C -->|fallback| F[registrar_outro]
+ D --> G[registrar_sim]
+ E --> H[registrar_nao]
+ B --> I[registrar_bundle]
+ G --> Z[END]
+ H --> Z
+ I --> Z
+ F --> Z
+```
+
+### Nó `preparar`
+
+Action:
+
+```text
+preparar_vas_estrategico
+```
+
+Recebe `items` e `linhas`.
+
+Possui pausa condicionada à saída da própria action:
+
+```yaml
+when:
+ eq: [$.output.await_user_input, true]
+```
+
+### Pause
+
+```yaml
+allowed_values: ["SIM", "NAO", "OUTRO"]
+resume_from: decisao
+```
+
+### `resposta_bundle`
+
+Monta texto a partir de:
+
+```yaml
+$.vars.preparar.mensagem_bundle_fechamento
+```
+
+### `decisao`
+
+É o ponto de branching depois da pausa.
+
+#### SIM
+
+Sempre vai para `resposta_sim`, seja bundle ou estratégico.
+
+#### NAO + estratégico
+
+```yaml
+all:
+ - has_estrategico_items == true
+ - resposta_usuario == NAO
+```
+
+vai para `explicar_cancelamento`.
+
+#### NAO + bundle puro
+
+Quando não há item estratégico, vai para `resposta_bundle`.
+
+#### fallback
+
+`priority: 99` -> `registrar_outro`.
+
+O comentário do arquivo ressalta que, em operação normal, `OUTRO` deveria ser interceptado/reperguntado pelo runtime antes de entrar nessa decisão; o fallback continua existindo como proteção.
+
+### Registro final
+
+Há actions diferentes para preservar o caminho de negócio:
+
+- `registrar_sim`;
+- `registrar_nao`;
+- `registrar_bundle`;
+- `registrar_outro`.
+
+Todas terminam em `END`.
+
+---
+
+# 12. Mapa workflow -> actions Python
+
+| Workflow | Action(s) principais | Implementação |
+|---|---|---|
+| `buscar_fatura` | `buscar_fatura` | `app/domain/contas/workflow_actions.py` |
+| `buscar_informacao` | `buscar_informacao_rag`, `reescrever_resposta_buscar_informacao` | mesmo arquivo |
+| `cancelamento_vas_avulso` | `cancelamento_vas_avulso_batch` | mesmo arquivo |
+| `contestacao_tool` | `registrar_protocolo`, `check_invoice_status`, `abrir_contestacao_cliente`, `enviar_sms`, `consultar_contrato_corte`, `abrir_sr_conta_certa_manual`, `atualizar_status_sr` | mesmo arquivo |
+| `finalizar_atendimento` | `finalizar_atendimento_action` | mesmo arquivo |
+| `invoice_explanation` | `preparar_invoice_explanation`, `formatar_invoice_explanation`, `checar_tentativa_cvn`, `registrar_atendimento_invoice_explanation`, `registrar_protocolo_inicio`, `preparar_handoff_invoice_explanation`, `checar_vas_variado` | mesmo arquivo |
+| `pro_rata` | `preparar_pro_rata`, `formatar_pro_rata`, `registrar_atendimento_pro_rata` | mesmo arquivo |
+| `termino_desconto` | `formatar_capability_resposta` | mesmo arquivo |
+| `valor_divergente` | `formatar_capability_resposta` | mesmo arquivo |
+| `vas_estrategico` | `preparar_vas_estrategico`, `montar_resposta_texto`, `montar_explicacao_cancelamento_vas_estrategico`, `registrar_atendimento_vas_estrategico` | mesmo arquivo |
+
+`no_op` e `montar_resposta_texto` são actions utilitárias registradas pelo mesmo registry de domínio.
+
+---
+
+# 13. Como criar um novo workflow
+
+## Passo 1 — definir a responsabilidade
+
+Pergunte:
+
+- há mais de uma etapa?
+- existe branching?
+- existe efeito externo?
+- existe pausa conversacional?
+- precisa ser retomado em outro turno?
+
+Se a operação for uma única função sem jornada, talvez uma tool/action simples seja suficiente.
+
+## Passo 2 — registrar as actions
+
+Em `workflow_actions.py`:
+
+```python
+@reg.action("consultar_exemplo")
+def consultar_exemplo(params, state):
+ result = service.consultar(...)
+ return {
+ "success": True,
+ "dados": result,
+ }
+```
+
+## Passo 3 — criar `nome.v1.yaml`
+
+```yaml
+name: meu_workflow
+version: 1
+start: consultar
+
+nodes:
+ - id: consultar
+ action: consultar_exemplo
+ input:
+ msisdn: $.input.msisdn
+
+edges:
+ - from: consultar
+ to: END
+```
+
+## Passo 4 — criar marcador ativo
+
+```yaml
+# meu_workflow.active.yaml
+version: 1
+```
+
+## Passo 5 — adicionar branches
+
+Sempre pense em fallback explícito.
+
+```yaml
+- from: consultar
+ to: sucesso
+ priority: 10
+ when:
+ eq: [$.vars.consultar.success, true]
+
+- from: consultar
+ to: falha
+ priority: 99
+```
+
+## Passo 6 — adicionar pause somente quando a jornada exige input
+
+Não coloque pausa dentro da lógica Python da action se ela faz parte do contrato do fluxo.
+
+## Passo 7 — testar
+
+No mínimo:
+
+- happy path;
+- cada branch;
+- falha da integração;
+- input ausente;
+- pause;
+- resume;
+- resposta inválida;
+- idempotência de efeitos externos;
+- terminalidade;
+- novo turno após finalização.
+
+---
+
+# 14. Como criar uma nova versão
+
+Suponha que `vas_estrategico.v3.yaml` precise mudar materialmente.
+
+1. copie para `vas_estrategico.v4.yaml`;
+2. altere internamente `version: 4`;
+3. implemente/teste a nova lógica;
+4. mantenha v3 disponível;
+5. altere somente depois:
+
+```yaml
+# vas_estrategico.active.yaml
+version: 4
+```
+
+### Rollback
+
+Basta voltar o marker:
+
+```yaml
+version: 3
+```
+
+sem apagar a v4.
+
+---
+
+# 15. Como depurar um workflow
+
+## 15.1 Comece pelo status
+
+Procure:
+
+```text
+COMPLETED
+PAUSED
+FAILED
+```
+
+## 15.2 Confira `workflow_name` e `workflow_version`
+
+Isso confirma qual YAML realmente foi carregado.
+
+## 15.3 Confira `trace`
+
+Exemplo:
+
+```text
+preparar -> COMPLETED
+formatar -> COMPLETED
+formatar -> pause_resume RESUMED
+decisao -> COMPLETED
+registrar_sim -> COMPLETED
+registrar_protocolo_aceite -> COMPLETED
+```
+
+O trace responde rapidamente:
+
+- qual action executou;
+- qual nó foi o último;
+- se houve resume;
+- se alguma action foi repetida.
+
+## 15.4 Confira `vars`
+
+Ao investigar uma edge:
+
+```yaml
+when:
+ eq: [$.vars.consultar_contrato_corte.apos_data_corte, true]
+```
+
+primeiro valide o conteúdo real de:
+
+```text
+vars.consultar_contrato_corte.apos_data_corte
+```
+
+Não conclua que a edge está errada sem verificar a saída da action.
+
+## 15.5 Confira `pause.expected_input`
+
+Se o sistema está tratando uma frase como resposta de um fluxo anterior, procure:
+
+```text
+pending_domain_workflow
+expected_input
+transaction_status
+workflow_resume
+```
+
+Após workflow terminal, esses latches não devem sequestrar o próximo turno.
+
+## 15.6 Confira o marker `.active.yaml`
+
+Um erro comum é editar `v3.yaml`, mas o marker continuar apontando para v2.
+
+---
+
+# 16. Regras de desenho recomendadas
+
+## 16.1 Workflow orquestra; action executa
+
+Bom:
+
+```yaml
+when:
+ eq: [$.vars.validar.success, false]
+```
+
+Action retorna a evidência; YAML escolhe o próximo passo.
+
+Evite colocar toda a jornada dentro de uma única action gigante.
+
+## 16.2 Não colocar regra TIM no runtime genérico
+
+Se a regra pertence a contestação, VAS ou fatura, ela deve ficar no domínio/configuração do agente, não hardcoded no framework.
+
+## 16.3 Side effects precisam de idempotência
+
+Especialmente:
+
+- cancelamento;
+- contestação;
+- protocolo;
+- SMS;
+- criação de SR.
+
+## 16.4 Pausa não deve reexecutar action anterior
+
+Mantenha o desenho em que o pause é um contrato do nó e o resume segue para `resume_from`.
+
+## 16.5 Prioridade deve ser intencional
+
+Use números que deixem clara a hierarquia:
+
+```text
+1 bloqueio terminal crítico
+10 caminho específico
+20 segundo caminho específico
+99 fallback
+```
+
+## 16.6 Não use output textual para decidir operação financeira
+
+Branching deve usar campos estruturados como:
+
+```text
+success
+barcode
+apos_data_corte
+dependent_invoice_item
+```
+
+não palavras encontradas em uma frase produzida por LLM.
+
+---
+
+# 17. Anti-patterns
+
+### 17.1 Alterar `.active.yaml` sem criar a versão
+
+Errado:
+
+```yaml
+version: 4
+```
+
+sem existir `nome.v4.yaml`.
+
+### 17.2 Action não registrada
+
+Se o YAML contém:
+
+```yaml
+action: minha_action
+```
+
+mas o registry não possui esse nome, o runtime falhará com action não registrada.
+
+### 17.3 Referenciar `$.vars` de nó que ainda não executou
+
+Exemplo incorreto:
+
+```yaml
+start: B
+
+B:
+ input:
+ protocolo: $.vars.A.protocolo
+```
+
+se `A` nunca foi executado.
+
+### 17.4 Branch sem fallback
+
+Pode provocar falha de transição.
+
+### 17.5 Usar `pause` para esconder estado de domínio
+
+`pause` deve indicar interação com usuário, não substituir persistência correta de transação.
+
+### 17.6 Reutilizar workflow terminal como contexto ativo
+
+Um workflow terminado pode permanecer no histórico para auditoria, mas não deve continuar fornecendo `expected_input` ao próximo turno.
+
+---
+
+# 18. Checklist de code review
+
+Antes de aprovar alteração em `workflows/`:
+
+- [ ] `name` corresponde ao arquivo;
+- [ ] `version` corresponde ao sufixo `.vN`;
+- [ ] `active.yaml` aponta para uma versão existente;
+- [ ] `start` existe;
+- [ ] IDs de nós são únicos;
+- [ ] todas as actions estão registradas;
+- [ ] todos os `$.input` necessários são fornecidos pelo caller;
+- [ ] referências `$.vars.` apontam para nós que executaram antes;
+- [ ] branches específicos têm prioridade anterior ao fallback;
+- [ ] existe fallback quando necessário;
+- [ ] `END` está alcançável;
+- [ ] effects externos são idempotentes ou protegidos;
+- [ ] pause não reexecuta action de efeito externo;
+- [ ] `resume_from` existe;
+- [ ] `allowed_values` são tokens internos coerentes;
+- [ ] semantic classifier não transforma pergunta/hipótese em confirmação;
+- [ ] workflow terminal limpa latch operacional;
+- [ ] próximo turno na mesma sessão é testado;
+- [ ] testes de happy path e todos os branches existem.
+
+---
+
+# 19. Resumo conceitual para novos desenvolvedores
+
+Se você lembrar somente destas dez regras, já consegue navegar pela pasta com segurança:
+
+1. **`.active.yaml` escolhe a versão; `.vN.yaml` contém a lógica.**
+2. **`nodes` executam actions; `edges` decidem o próximo nó.**
+3. **`$.input` é entrada; `$.vars.` é resultado de nó anterior.**
+4. **Menor `priority` é avaliada primeiro.**
+5. **Uma edge sem `when` normalmente é o fallback.**
+6. **`pause` suspende a jornada; `resume_from` determina onde continuar.**
+7. **Tokens `SIM/NAO/CONTINUAR/OUTRO` são controle interno, não fraseologia.**
+8. **Actions fazem domínio/integração; o YAML faz orquestração.**
+9. **`END` termina o grafo; finalização de atendimento pode envolver action própria.**
+10. **Workflow terminado não deve controlar o próximo turno, mesmo quando o `session_id` permanece igual.**
+
+---
+
+# 20. Referências de código dentro do projeto
+
+Para aprofundar a implementação:
+
+```text
+/workflows/
+ definições declarativas do Contas
+
+/app/domain/contas/workflow_actions.py
+ implementação das actions usadas pelos workflows
+
+/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/models.py
+ schema Pydantic do DSL
+
+/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/repository.py
+ resolução de active version
+
+/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/runtime.py
+ executor, branching, pause/resume e LangGraph
+
+/agent_framework_oci/libs/agent_framework/src/agent_framework/workflows/registry.py
+ registro e resolução das actions
+
+/app/workflows/agent_graph.py
+ grafo principal do agente Contas e integração com router/guardrails/judges
+```
+
+---
+
+## Apêndice A — Exemplo completo comentado
+
+```yaml
+name: exemplo_confirmacao
+version: 1
+start: preparar
+
+nodes:
+ # Executa domínio e produz mensagem + dados estruturados.
+ - id: preparar
+ action: preparar_exemplo
+ input:
+ msisdn: $.input.msisdn
+
+ # Apenas apresenta a mensagem e pausa.
+ - id: apresentar
+ action: montar_resposta_texto
+ input:
+ dados:
+ texto_usuario: $.vars.preparar.mensagem
+ pause:
+ enabled: true
+ return_from: $.output.mensagem
+ expected_input:
+ key: resposta_usuario
+ allowed_values: ["SIM", "NAO"]
+ normalize: upper_strip
+ resume_from: decidir
+
+ # Nó estrutural para branch pós-resume.
+ - id: decidir
+ action: no_op
+ input: {}
+
+ - id: confirmar
+ action: executar_exemplo
+ input:
+ msisdn: $.input.msisdn
+
+ - id: cancelar
+ action: montar_resposta_texto
+ input:
+ dados:
+ texto_usuario: "Operação não realizada."
+
+edges:
+ - from: preparar
+ to: apresentar
+
+ - from: apresentar
+ to: decidir
+
+ - from: decidir
+ to: confirmar
+ priority: 10
+ when:
+ eq: [$.input.resposta_usuario, SIM]
+
+ - from: decidir
+ to: cancelar
+ priority: 20
+ when:
+ eq: [$.input.resposta_usuario, NAO]
+
+ - from: confirmar
+ to: END
+
+ - from: cancelar
+ to: END
+```
+
+Leitura em português simples:
+
+> Prepare os dados, mostre uma mensagem, pare e aguarde SIM/NAO. Quando o usuário responder, continue em `decidir`. Se SIM, execute a operação; se NAO, responda que nada foi feito. Depois encerre o workflow.
+
+---
+
+**Fim do manual.**
diff --git a/tests/docs/MATRIZ_MIGRACAO.md b/tests/docs/MATRIZ_MIGRACAO.md
new file mode 100644
index 0000000..23aa131
--- /dev/null
+++ b/tests/docs/MATRIZ_MIGRACAO.md
@@ -0,0 +1,223 @@
+# Matriz de Migração — Contas -> agent_framework_oci
+
+| Capacidade | Destino novo | Reuso framework | Código de domínio novo | Dependência anterior |
+|---|---|---:|---:|---:|
+| LangGraph | `app/workflows/agent_graph.py` | Sim | composição mínima | Não |
+| Router | EnterpriseRouter | Sim | routing.yaml | Não |
+| Stickiness | framework | Sim | configuração | Não |
+| Supervisor | framework | Sim | configuração | Não |
+| Confirmação | AgentRuntimeMixin | Sim | tool policy | Não |
+| Clarificação | AgentRuntimeMixin/MCP mapping | Sim | schemas/mapping | Não |
+| Sessions | framework repository | Sim | Não | Não |
+| Message memory | framework | Sim | Não | Não |
+| Summary memory | framework | Sim | Não | Não |
+| LTM | framework | Sim | Não | Não |
+| Checkpoint | framework | Sim | Não | Não |
+| RAG | RagService | Sim | conteúdo/config | Não |
+| Guardrails | GuardrailPipeline | Sim | config | Não |
+| Output Supervisor | framework | Sim | Não | Não |
+| Judges | JudgePipeline | Sim | config | Não |
+| MCP router | framework | Sim | tool catalog | Não |
+| Faturas | MCP/domain | Não aplicável | Sim | Não |
+| Billing Analysis | MCP/domain | Não aplicável | Sim | Não |
+| Consulta/Histórico VAS | MCP/domain | Não aplicável | Sim | Não |
+| Bloqueio/Cancelamento VAS | MCP/domain | confirmação no framework | Sim | Não |
+| Contestação | MCP/domain | confirmação/estado no framework | Sim | Não |
+| Protocol/Status/Tracking | MCP/domain | contexto no framework | Sim | Não |
+| SMS | MCP/domain | contexto no framework | Sim | Não |
+| Secure PDF | MCP/domain | contexto no framework | Sim | Não |
+| Langfuse | framework | Sim | Não | Não |
+| Pub/Sub/sequence | framework | Sim | configuração | Não |
+| OCI Streaming | framework | Sim | configuração | Não |
+| OTEL | framework | Sim | configuração | Não |
+
+## Regra
+
+O pacote anterior não é uma biblioteca do novo projeto. Se uma regra específica for necessária, ela deve ser portada e testada dentro de `app/domain/contas`; infraestrutura genérica deve ser eliminada em favor do framework.
+
+
+## Contratos TIM validados por regressão
+
+| Integração | Paridade coberta | Estado |
+|---|---|---|
+| CompleteInvoices | método/payload/header `ClientID` | ✅ |
+| Query VAS | URL por MSISDN, `clientId=AIAAGENTCR`, auth | ✅ |
+| VAS History | query `msisdn`, `clientId`, `messageId`, auth | ✅ |
+| Block VAS | payload PMid + fallbacks e headers | ✅ |
+| Cancel VAS | DELETE, channel, protocol, headers, messageId | ✅ |
+| Contract Information | GET por MSISDN, `clientId`, auth | ✅ |
+| Profile/Line Info | GET e header `ClientID` | ✅ |
+| Billing Analysis | GET por MSISDN + channel | ✅ |
+| Bill PDF | POST detalhado + criptografia | ✅ |
+| Secure PDF | GET com parâmetros criptografados | ✅ |
+| Customer Contestation | payload/headers principais | ✅ |
+| Service Request Status | envelope `serviceRequest` | ✅ |
+| Tracking Activities | customer/invoice/activity/user | ✅ |
+| Protocol V2 | envelope Siebel + headers corporativos | ✅ |
+| SMS Barcode | payload completo + retry/RCT | ✅ |
+
+## Jornadas compostas e comportamento conversacional
+
+| Capacidade original | Implementação migrada | Reuso do framework | Regressão |
+|---|---|---:|---:|
+| Cancelamento VAS -> contestação | dois workflows encadeados no MCP | `WorkflowRuntime` | ✅ |
+| Composição final cancelamento | `app/domain/contas/vas_cancellation_message.py` | domínio determinístico | ✅ 19 casos originais |
+| Fallback VAS History | `ContasDomainService.cancelar_vas_avulso` | transporte via adapter | ✅ |
+| Cancelamento parcial em lote | action expõe cancelados/falhas/candidatos | WorkflowRuntime + IdempotencyStore | ✅ |
+| Idempotência transacional | `create_idempotency_store()` | framework | ✅ |
+| Replay pós-finalização | Channel short-circuit | framework | ✅ |
+| Idle nudge replay | Channel short-circuit | framework | ✅ |
+| Processing interruption | replay + classificador LLM fail-safe | `LLMProvider` framework | ✅ |
+| Correção Fim/Mim -> Sim | channel transcription | framework | ✅ |
+| Finalização status/summary | regra pura de domínio | workflow framework | ✅ |
+| Protocolo informacional final | action + ProtocolV2 | WorkflowRuntime | ✅ |
+
+### Bootstrap MCP
+
+`WorkflowRuntime`, checkpointer e `IdempotencyStore` são inicializados de forma lazy. Isto evita dependência de Oracle/Redis para endpoints de diagnóstico e garante que o backend durável só seja aberto quando um workflow realmente precisar ser executado.
+
+## Incrementos de paridade - baseline 420
+
+| Capacidade original | Implementação migrada | Responsabilidade | Estado |
+|---|---|---|---:|
+| InvoiceContextProvider / prefetch | `InvoiceContextService` + `agent_framework.cache.Cache` | domínio escolhe evidências; framework fornece cache | ✅ |
+| Isolamento de invoice context por sessão | chave `session_id:msisdn:invoice_id` | framework cache | ✅ |
+| Plano família titular/dependente | normalização antes do workflow + contestação única no titular | domínio + WorkflowRuntime | ✅ |
+| Correção de linha por invoice detail | normalização determinística | domínio | ✅ |
+| CVAL fail-stop | edge `success=false -> END` | WorkflowRuntime | ✅ |
+| Snapshot parcial em falha | `WorkflowRuntime` recupera último state do LangGraph | framework | ✅ |
+| Retry Billing Analysis / RCT 079-084 | metadata `_transport` + `RCTPolicy` | domínio define códigos; observer publica | ✅ |
+| Finalização invoice explanation | protocolo informacional somente após workflow executado | domínio + WorkflowRuntime | ✅ |
+| VEB fechado / force RT15 | reuso ou novo protocolo conforme flags | domínio | ✅ |
+| Inicialização AgentWorkflow | router/agentes/grafo dentro de `__init__` | aplicação/framework | ✅ |
+
+### Incrementos de paridade — baseline 435
+
+| Capacidade original | Implementação migrada | Responsabilidade | Status |
+|---|---|---|---|
+| Invoice prefetch single-flight | `InvoiceContextService` + `agent_framework.cache.Cache` | domínio escolhe evidências; framework fornece cache | ✅ |
+| CVN de prefetch sem duplicação | `business_events` + cache markers | domínio define código; observer framework publica | ✅ |
+| Latch invoice/workflow já executado | `AgentRuntimeMixin.business_workflows_executed` | framework | ✅ |
+| Batch cancellation max 5 | action async + semaphore | domínio action sobre WorkflowRuntime | ✅ |
+| Protocolo por linha antes de cancelar | action `cancelamento_vas_avulso_batch` | domínio + adapter TIM | ✅ |
+| Erro estruturado de workflow | `WorkflowRunResult.error_details` | framework | ✅ |
+| Provider error de contestação | MCP mapping sobre `error_details` | domínio/MCP fino | ✅ |
+
+
+## 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.
+
+## Finalização - paridade adicional (baseline 533)
+
+| Capability original | Implementação migrada | Framework reutilizado | Evidência |
+|---|---|---|---|
+| CVN aceite/recusa no encerramento | `finalizar_atendimento_action` | `business_events` + `AgentObserver` | testes de finalização estendida |
+| Protocolo RT-15 informacional | action de domínio + TIM client | WorkflowRuntime/observer/idempotência | RCT.085/086 + CVN.010/011 |
+| Nota de invoice explanation | valor canônico `Explicação dos valores da fatura` | workflow latch do framework | regressão |
+| Handoff/retention suppression | flags no state/domain action | estado persistido do framework | regressão |
+| SAD decision tree | `SAD.001/002/003/004/005/006/007` como business events | AgentObserver | regressão |
+| Classificação VAS sem invoice detail | aliases determinísticos de domínio | nenhuma engine paralela | regressão |
+| Regressão offline de workflow | `WorkflowRuntime(... allow_deterministic_fallback=True)` somente em teste | DSL/actions do framework | 18 casos históricos executados |
+
+## Atualização de paridade - baseline 550
+
+| Capability | Original | Migrado | Evidência |
+|---|---|---|---|
+| Finalização com prefetch de fatura | CVN lookup + RT-15 quando aplicável | Implementado | regressão de finalização |
+| Supressão após transição para negócio | não reemite CVN/MPI | Implementado | regressão dedicada |
+| VEB terminal | não duplica RT-15/CVN | Implementado | regressão dedicada |
+| Precedência de tipo pela fatura | total/parcial/sem match | Implementado | regressão dedicada |
+| Protocolo já existente | não duplica RT-15 | Implementado | regressão dedicada |
+| Matcher fonético/transcrição | catálogo real | Implementado sem xfails | suíte de transcrição |
+
+## VAA — rastreabilidade de cancelamento/contestação
+
+| Capability histórica | Implementação migrada | Framework reutilizado | Estado |
+|---|---|---|---|
+| VAA.001–004 cancelamento | `cancelamento_vas_avulso_batch` retorna `business_events` | AgentObserver / analytics | OK |
+| VAA.005–009 contestação/boleto | `abrir_contestacao_cliente` retorna `business_events` | AgentObserver / analytics | OK |
+| VAA.012–015 SMS | `enviar_sms` retorna `business_events` e mantém fluxo em erro | AgentObserver / analytics | OK |
+| VAA.016–017 status SR | `atualizar_status_sr` retorna `business_events` | AgentObserver / analytics | OK |
+
+
+## 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.
+
+### Paridade de eventos conversacionais — baseline 565
+
+| Família | Paridade adicionada |
+|---|---|
+| VEB | ordem dos branches de VAS estratégico e metadata do turno/URA |
+| MPI | contexto de invoice explanation, pró-rata e cancelamento |
+| CVN | contexto conversacional/protocolo no encerramento |
+| SAD | `llmResponse`, `messageId`, sessão/canal e `sessionEndAt` normalizado |
+
+## Incremento de paridade — baseline 570
+
+| Funcionalidade original | Implementação migrada | Responsabilidade |
+|---|---|---|
+| Cancelamento solicitado para item estratégico/bundle | `InvoiceResolver` redireciona para workflow `vas_estrategico` | Domínio + WorkflowRuntime |
+| Invoice explanation SIM | recomenda `resolvido` pelo último node do workflow | MCP adapter fino sobre WorkflowRuntime |
+| Invoice explanation NÃO sem VAS variado | recomenda `nao_resolvido` | MCP adapter fino sobre WorkflowRuntime |
+| Pró-rata aceito / sem Plano Controle | recomenda `resolvido` | MCP adapter fino sobre WorkflowRuntime |
+| Orientação de cancelamento de VAS estratégico por parceiro | action declara `requires_rag/rag_queries`; `AgentRuntimeMixin` chama `RagService` | Framework |
+| Gate padrão de regressão | `pytest -q` -> `tests/` | Projeto migrado |
+
+## 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.
+
+
+### Paridade de wrappers históricos — baseline 593
+
+| Comportamento original | Implementação migrada | Estado |
+|---|---|---|
+| `tipo_atendimento=contestacao` | `_workflow_payload(contestacao_tool)` | ✅ |
+| Contexto do turno no workflow | payload MCP preserva IDs/canal/mensagem | ✅ |
+| CPF como alias de socialSecNo | normalização no wrapper composto | ✅ |
+| `cancelados` sem `results.success` | fallback agregado do wrapper | ✅ |
+| `itens_para_contestacao` | alias de `contestation_candidates` | ✅ |
+| falha block/cancel ainda elegível a RT-02 | composição de dois WorkflowRuntime | ✅ |
+| SMS falha sem derrubar jornada | `sms_not_send_error` | ✅ |
+| Bundle + Estratégico + NÃO | protocolo deferido + RAG obrigatório | ✅ |
+
+## Complemento de paridade — baseline 599
+
+| Comportamento histórico | Implementação migrada | Prova |
+|---|---|---|
+| Itens já contestados | normalização em `abrir_contestacao_cliente` + compositor determinístico | teste de wrapper 1:1 |
+| Itens contestados/não contestados | classificação na action de domínio | regressão de contestação |
+| Total contestado somente dos itens aceitos | cálculo determinístico no domínio | regressão de contestação |
+| Contestação retorna valor zero | fallback para total efetivamente cancelado | teste de wrapper 1:1 |
+| Valor na fala em pt-BR | normalização na borda MCP | teste `R$ 14,99` |
+| `next_subject` | ignorado pelo compositor determinístico | teste de wrapper 1:1 |
+| Billing Analysis indisponível | fraseologia canônica e `auto_finalize_on_failure=false` | regressão invoice explanation |
+
+### Cobertura 1:1 de wrappers — baseline 615
+
+Além dos testes de domínio/actions/workflows, a suíte passa a reproduzir diretamente
+outcomes históricos dos wrappers `cancelar_vas_single` e `finalize_support`, cobrindo
+no-match, candidatos explícitos, falhas parciais RT-01→RT-02, SMS, protocolos,
+`protocol_closed`, plano família/titular-dependente e protocolo informacional deferido.
+
+Esses testes são classificados como **paridade explícita de contrato externo**, e não
+apenas cobertura indireta por actions internas.
diff --git a/tests/docs/OBSERVABILITY_CODE_MAPPING.md b/tests/docs/OBSERVABILITY_CODE_MAPPING.md
new file mode 100644
index 0000000..35045a3
--- /dev/null
+++ b/tests/docs/OBSERVABILITY_CODE_MAPPING.md
@@ -0,0 +1,67 @@
+# Mapeamento contratual da observabilidade do Contas
+
+O Contas usa o mecanismo genérico `ObservabilityCodeMapper` do framework para adaptar **identificadores internos de observabilidade** aos códigos exigidos pelo contrato externo.
+
+Arquivo:
+
+```text
+config/observability_mapping.yaml
+```
+
+Configuração de exemplo deste agente:
+
+```yaml
+version: "1"
+mappings:
+ guardrail.dlex_in: GRL.004
+ guardrail.tox: GRL.005
+```
+
+O mapping é feito pelo nome canônico emitido internamente. Assim, uma generation/observation criada como `guardrail.dlex_in` aparece externamente como `GRL.004`, e `guardrail.tox` como `GRL.005`.
+
+A substituição acontece antes dos providers de observabilidade. O mesmo nome contratual é usado por Langfuse, OTEL e EventBus nos caminhos que passam por `Telemetry`. Eventos publicados pelo `AgentObserver` também continuam usando o mesmo mapper.
+
+Quando um nome de span/generation é substituído, o nome interno é preservado em metadata:
+
+- `observability_name_internal`
+- `observability_name_mapped`
+- `observability_code_mapped: true`
+
+Para eventos estruturados, permanecem disponíveis os campos equivalentes `event_code_internal` e `event_code_mapped`.
+
+Códigos/names ausentes na tabela passam sem alteração. Para acrescentar outro contrato, adicione somente uma nova entrada ao YAML; não altere Python nem o guardrail/judge.
+
+Este arquivo pertence ao agente/deployment. O framework contém apenas a engine genérica de mapping e não conhece os códigos contratuais deste agente ou de qualquer cliente.
+
+## Diagnóstico de carregamento
+
+No startup o agente registra uma linha `Observability mapping:` com `enabled`, `path`, quantidade de entradas, amostras resolvidas e o arquivo real de onde `agent_framework` foi importado. Isso permite detectar `.venv` antigo/cópia errada do framework e path relativo incorreto.
+
+A normalização é aplicada em duas barreiras:
+
+1. `Telemetry._start_observation()` — última barreira para spans/generations criados pelo Telemetry;
+2. `LangfuseAnalyticsPublisher` — necessário porque esse publisher usa o SDK Langfuse diretamente e não passa pelo Telemetry.
+
+Assim, uma configuração como:
+
+```yaml
+mappings:
+ guardrail.dlex_in: GRL.004
+ guardrail.tox: GRL.005
+```
+
+é aplicada independentemente de qual dos dois caminhos produziu a observation.
+
+## Normalização na fronteira do LLM provider
+
+A normalização não depende apenas do `Telemetry`. O `generation_name` é resolvido pelo `ObservabilityCodeMapper` antes de o provider LLM iniciar qualquer instrumentação. Isso garante que nomes como `guardrail.dlex_in` já cheguem ao tracer como `GRL.004`.
+
+Quando o provider já recebe o `Telemetry` do framework, a auto-instrumentação `langfuse.openai` é desabilitada para evitar uma segunda observation fora do contrato central.
+
+O caminho relativo configurado em `OBSERVABILITY_CODE_MAPPING_PATH` é procurado no diretório corrente e nos roots de importação Python, permitindo iniciar o Uvicorn fora do diretório raiz do agente sem perder o mapping.
+
+## Compatibilidade automática do framework
+
+A partir desta versão, o framework possui um registry default interno (`agent_framework/config/observability_mapping.yaml`) carregado mesmo quando o agente não possui `OBSERVABILITY_CODE_MAPPING_*`. O arquivo do agente, quando habilitado, funciona como overlay. Isso permite substituir somente a versão do framework em agentes legados sem mudar a taxonomia GRL nem as decisões históricas dos rails.
+
+Veja também `agent_framework_oci/libs/agent_framework/docs/OBSERVABILITY_DEFAULT_OVERLAY_COMPATIBILITY.md`.
diff --git a/tests/docs/OBSERVABILITY_CONTRACT_REGISTRY.md b/tests/docs/OBSERVABILITY_CONTRACT_REGISTRY.md
new file mode 100644
index 0000000..0e9b071
--- /dev/null
+++ b/tests/docs/OBSERVABILITY_CONTRACT_REGISTRY.md
@@ -0,0 +1,43 @@
+# Observability Contract Registry
+
+`config/observability_mapping.yaml` é a única tabela usada pelo framework para duas responsabilidades relacionadas ao contrato externo:
+
+1. traduzir nomes/códigos semânticos para labels exigidos pela observabilidade do cliente;
+2. preservar ações legadas de guardrails (`retry`, `handover`, etc.) sem hardcode de nomes no Python.
+
+A sintaxe v1 continua válida:
+
+```yaml
+mappings:
+ guardrail.dlex_in: GRL.004
+```
+
+A forma rica adiciona `action` e `aliases`:
+
+```yaml
+mappings:
+ guardrail.revprec:
+ action: retry
+ aliases: [REVPREC, TIM_REVPREC]
+```
+
+`label` é opcional. Quando ausente, o nome de observabilidade não é renomeado. `action` também é opcional.
+
+## Precedência de ação
+
+Para uma decisão negada, o framework usa:
+
+1. `metadata.terminal_action` retornado pelo rail;
+2. `on_deny` do `guardrails.yaml`;
+3. `action` resolvida pelo `observability_mapping.yaml`;
+4. `BLOCK` como fallback fail-safe.
+
+Isso mantém compatibilidade com rails internos e externos sem que `OutputSupervisor` ou `ParallelRailExecutor` conheçam nomes como `REVPREC`, `CMP`, `SCO`, `GND`, `ATH` ou `HUMAN`.
+
+## Aliases
+
+Uma entrada `guardrail.revprec` é automaticamente resolvida também por `REVPREC`. Aliases explícitos permitem associar nomes externos ou históricos, por exemplo `TIM_REVPREC`.
+
+## Compatibilidade
+
+Mappings escalares continuam funcionando sem alteração. Agentes que não habilitam o mapper continuam em passthrough e usam `BLOCK` para negações sem ação explícita.
diff --git a/tests/docs/OBSERVABILITY_OVERLAY_MERGE_FIX.md b/tests/docs/OBSERVABILITY_OVERLAY_MERGE_FIX.md
new file mode 100644
index 0000000..d7abf75
--- /dev/null
+++ b/tests/docs/OBSERVABILITY_OVERLAY_MERGE_FIX.md
@@ -0,0 +1,26 @@
+# Correção do merge Default + Overlay de Observabilidade
+
+## Problema
+O default do framework estava ativo, porém em alguns caminhos o overlay do agente não era carregado. O efeito observado no Langfuse era `GRL.DLEX_IN`/`GRL.TOX` (default) em vez de `GRL.004`/`GRL.005` (Contas).
+
+## Correção
+O framework agora monta um único registry efetivo antes de qualquer resolução:
+
+1. carrega `agent_framework/config/observability_mapping.yaml`;
+2. localiza o overlay do agente;
+3. faz merge por chave canônica, com o agente sobrescrevendo o default;
+4. reconstrói os aliases somente depois do merge;
+5. usa esse único registry em LLM provider, Telemetry, Analytics, OutputSupervisor e ParallelRailExecutor.
+
+## Descoberta do overlay
+Além de `OBSERVABILITY_CODE_MAPPING_PATH`, o framework autodetecta `config/observability_mapping.yaml` no cwd e nos roots de importação Python. O arquivo default empacotado do framework é excluído dessa descoberta.
+
+Assim um agente com arquivo convencional de overlay não depende de alterar seu launcher ou `.env` para que a customização seja aplicada.
+
+## Resultado esperado no Contas
+- `guardrail.dlex_in` -> `GRL.004`
+- `guardrail.tox` -> `GRL.005`
+- componentes não sobrescritos continuam herdando o default do framework.
+
+## Compatibilidade
+Agentes antigos sem overlay continuam usando apenas o default do framework e preservam a taxonomia/ações históricas.
diff --git a/tests/docs/OUTPUT_SUPERVISOR_DECLARATIVE_POLICIES.md b/tests/docs/OUTPUT_SUPERVISOR_DECLARATIVE_POLICIES.md
new file mode 100644
index 0000000..ca3d11f
--- /dev/null
+++ b/tests/docs/OUTPUT_SUPERVISOR_DECLARATIVE_POLICIES.md
@@ -0,0 +1,83 @@
+# OutputSupervisor sem taxonomia contratual hardcoded
+
+## Objetivo
+
+O `OutputSupervisor` do framework trabalha somente com eventos semânticos e ações de runtime. Códigos contratuais externos/numerados pertencem exclusivamente ao `ObservabilityCodeMapper` configurado pelo agente/deployment.
+
+## Eventos internos
+
+Exemplos de eventos internos:
+
+```text
+guardrail.output_supervisor.started
+guardrail.result.allow
+guardrail.result.block
+guardrail.result.retry
+guardrail.output..completed
+guardrail.output_supervisor.completed
+```
+
+Se um cliente exigir códigos próprios, configure `config/observability_mapping.yaml`. O supervisor não conhece a taxonomia externa.
+
+## Ação quando um rail nega
+
+O framework não decide mais a ação procurando nomes específicos de rails. A ação pode vir do próprio resultado:
+
+```python
+metadata={"terminal_action": "retry"}
+```
+
+ou do YAML:
+
+```yaml
+output:
+ - code: MY_VALIDATION
+ enabled: true
+ on_deny: retry
+```
+
+Valores suportados são os valores de `RailAction`, como `block`, `retry` e `handover`.
+
+## Remediação por rewrite
+
+Rewrite também é uma capacidade genérica. O rail/policy declara a remediação:
+
+```yaml
+output:
+ - code: MY_WORDING_POLICY
+ enabled: true
+ on_block:
+ type: rewrite
+ max_attempts: 1
+ prompt_id: FALLBACK
+ profile_name: grl
+ component_name: guardrail.wording.rewrite
+```
+
+O supervisor não verifica se o código é `FRASEOLOGIA` ou qualquer outro nome. Um guardrail externo do agente pode usar exatamente o mesmo contrato.
+
+## Mensagens de UX
+
+Mensagens de fallback/handover pertencem ao agente:
+
+```yaml
+output_supervisor:
+ max_retries: 3
+ fallback_message: "..."
+ handover_message: "..."
+```
+
+Assim o framework não precisa conhecer idioma, marca ou fraseologia do atendimento.
+
+## Contas
+
+O Contas preserva seu comportamento atual:
+
+- `TIM_REVPREC` declara `terminal_action=retry` no próprio rail externo;
+- `CMP` está configurado com `on_deny: retry`;
+- `TIM_FRASEOLOGIA`, quando habilitado, declara remediação `rewrite` no agente;
+- textos de fallback/handover ficam no `config/guardrails.yaml` do Contas.
+
+## Compatibilidade
+
+Rails que retornam apenas `allowed=false` e não declaram policy continuam em `block`, que é o fail-closed genérico. Não há mais inferência de ação pelo nome do rail.
diff --git a/tests/docs/PENTE_FINO_PARIDADE_TOOLS_CONTAS.md b/tests/docs/PENTE_FINO_PARIDADE_TOOLS_CONTAS.md
new file mode 100644
index 0000000..fef7b89
--- /dev/null
+++ b/tests/docs/PENTE_FINO_PARIDADE_TOOLS_CONTAS.md
@@ -0,0 +1,112 @@
+# Pente-fino de paridade — Contas original x Contas migrado
+
+## Escopo
+
+Comparação funcional e arquitetural das 18 capabilities solicitadas, tomando como fonte de verdade o projeto Contas original e preservando, no migrado, as responsabilidades genéricas do `agent_framework_oci` (roteamento, confirmação, pause/resume, RAG, memória, observabilidade e política de tools).
+
+Tools/capabilities avaliadas:
+
+`consultar_faturas`, `consultar_plano`, `invoice_explanation`, `buscar_informacao`, `consultar_vas`, `consultar_historico_vas`, `cancelar_vas_avulso`, `tratar_vas_estrategico`, `validar_contestacao`, `contestar_cobranca`, `finalizar_atendimento`, `consultar_status_solicitacao`, `enviar_sms`, `recuperar_fatura_pdf`, `pro_rata`, `termino_desconto`, `valor_divergente`, `retomar_workflow`.
+
+## Resultado executivo
+
+Depois das correções deste pente-fino, as 18 capabilities estão expostas no MCP e habilitadas no registry do agente. Os workflows conversacionais principais foram preservados do original. Os YAMLs `buscar_fatura`, `buscar_informacao`, `cancelamento_vas_avulso`, `finalizar_atendimento`, `invoice_explanation`, `pro_rata`, `termino_desconto`, `valor_divergente` e `vas_estrategico` permanecem equivalentes ao original. `contestacao_tool` contém uma diferença intencional de segurança: se a validação financeira falhar, o fluxo termina antes de SMS/contrato/SR.
+
+A suíte completa do projeto após as mudanças executa **715 testes com sucesso**.
+
+## Paridade por capability
+
+| Capability | Fonte/semântica no original | Situação após pente-fino | Ação tomada |
+|---|---|---|---|
+| `consultar_faturas` | `complete_invoices` + prefetch de `bill_pdf` + resumo semântico | Corrigida | Mantida a API de Complete Invoices e restaurados `invoice_amount`, `invoice_amount_open`, período e emissão a partir do PDF, sem inventar campo no backend. |
+| `consultar_plano` | Evidência da própria fatura/billing analysis | OK | Extração determinística de seções `Plano/Planos`; não usa RAG nem inferência livre da LLM. |
+| `invoice_explanation` | Workflow v2 + evidência da fatura + capability LLM de reescrita + pause | Corrigida | `invoice_detail` e resumo semântico agora chegam ao workflow; composição volta a ser da LLM do framework; `await_user_input=True` restaurado. |
+| `buscar_informacao` | Tool RAG ativa (`queries` e `query` legado) | Corrigida arquiteturalmente | Reexposta como façade MCP compatível. Não duplica RAG no domínio: retorna `requires_rag` e delega ao `RagService` do framework. Suporta `queries[]` e `query`. |
+| `consultar_vas` | Consulta de VAS ativos | OK | Mantida integração direta e resolução de domínio para os fluxos que precisam classificar item. |
+| `consultar_historico_vas` | Histórico de VAS/serviços | OK | Mantido contrato da integração e normalização usada pelo cancelamento. |
+| `cancelar_vas_avulso` | Tool ativa, somente avulso, confirmação, cancelamento + contestação automática | OK | Confirmação permanece no framework; preflight resolve nome/classe contra a fatura; workflow composto preserva cancelamento + contestação e protocolos. |
+| `tratar_vas_estrategico` | `vas_estrategico`, bundle/estratégico | OK | Alias semântico migrado para `tratar_vas_estrategico`; workflow v3 preservado; redirecionamento automático evita cancelar estratégico como avulso. |
+| `validar_contestacao` | Não existia como tool pública; regras CVAL existiam na execução | Corrigida | A pré-validação agora usa a mesma `validate_contestation_items` da execução financeira; deixa de aprovar algo que seria bloqueado depois. Usa o valor pedido pelo cliente, não o `resolved_value` da fatura. |
+| `contestar_cobranca` | Workflow/ações de contestação e Conta Certa | OK + hardening | Workflow preservado e CVAL fail-closed. Adicionado edge de segurança para não continuar com SMS/contrato/SR após falha financeira. |
+| `finalizar_atendimento` | Tool ativa; `status` obrigatório e regras estritas de finalização | Corrigida | `status` voltou a ser requisito explícito e é extraído genericamente pelo framework com enum semântico; `erro_falha_sistema` continua reservado ao sistema. |
+| `consultar_status_solicitacao` | Integração de status SR usada internamente | Corrigida | Removido mapeamento incorreto `interaction_key -> protocol` (interaction_key é identidade da interação/mensagem, não protocolo). Protocolo é extraído da fala ou recuperado de aliases do contexto de workflow. |
+| `enviar_sms` | Ação de integração usada pelos workflows | OK | Mantida integração TIM; continua sem regra de negócio dentro do framework. |
+| `recuperar_fatura_pdf` | SecurePDF/invoice recover no original | Corrigida | Agora participa do `InvoiceContextService` para obter `customer_id` antes do SecurePDF quando não vier explicitamente. |
+| `pro_rata` | Workflow v3; regra explícita de exatamente 2 planos e `has_plano_controle` | Corrigida | O MCP deriva os planos da evidência do Bill PDF/billing analysis, deduplica repetição DANFE/linha e falha fechado se não houver exatamente dois planos. `has_plano_controle` é determinístico. |
+| `termino_desconto` | Capability/backend existente; versão migrada havia cristalizado causa sem evidência | Corrigida + hardening | A causa só é afirmada quando backend/mock fornece evidência causal explícita de desconto/promoção. Sem essa prova, responde que o motivo não está disponível; parcelas e texto do cliente não viram fato. |
+| `valor_divergente` | Capability/backend existente | Corrigida | Restaurada a semântica original de alteração no valor do plano por linha, em vez de texto genérico sobre billing analysis. |
+| `retomar_workflow` | Resume era interno ao runtime/executor original | OK arquiteturalmente | Exposto como façade genérica do `WorkflowRuntime.aresume`; não replica estado conversacional dentro do domínio Contas. |
+
+## Correções relevantes encontradas
+
+### 1. Contexto de fatura
+
+O original não obtinha o valor total diretamente de `complete_invoices`. Ele construía um contexto enriquecido a partir do PDF parseado. A migração já possuía parser e `InvoiceContextService`, mas faltava publicar integralmente o resumo semântico. Foi restaurada a cadeia:
+
+`Complete Invoices -> invoice/customer id -> Bill PDF -> parser -> total_geral -> invoice_amount/invoice_amount_open`.
+
+### 2. `invoice_explanation`
+
+O YAML migrado preservava o `pause`, mas a action migrada não retornava `await_user_input=True`, ao contrário do original. Isso tornava a condição de pause falsa. Também havia sido eliminada a etapa de composição LLM específica. Agora a action retorna o gate de pause e uma diretiva `requires_llm_composition`, mantendo a LLM no framework, não no domínio.
+
+### 3. `buscar_informacao`
+
+A route ainda referenciava `buscar_informacao`, mas a tool estava `enabled: false` e nem era exposta pelo MCP. Isso criava uma discrepância entre o contrato original e a configuração migrada. A façade foi restaurada sem reintroduzir RAG customizado no Contas.
+
+### 4. `pro_rata`
+
+O schema/descrição original exigia **exatamente dois planos**. A migração aceitava `planos=[]` e podia afirmar pró-rata mesmo sem evidência. Agora os planos são derivados da fatura e a operação retorna `NOT_APPLICABLE` quando a evidência não comprova exatamente dois planos.
+
+### 5. Pré-validação de contestação
+
+`validar_contestacao` apenas resolvia o item e retornava `eligible=true`; a validação CVAL real só acontecia depois, já no workflow transacional. Agora a pré-validação e a execução usam a mesma regra financeira, evitando confirmação para uma operação que será inevitavelmente bloqueada.
+
+### 6. Status de solicitação
+
+O mapper usava `interaction_key` como `protocol`. Isso é semanticamente incorreto: `interaction_key` identifica a interação do framework. O protocolo agora vem da mensagem ou de campos de protocolo existentes no contexto transacional (`protocol_number`, `protocolo_id`, `contestacao_protocol`, `cancelamento_vas_protocol`).
+
+## Arquivos principais alterados
+
+- `contas_mcp/servers/contas_mcp_server/main.py`
+- `app/domain/contas/service.py`
+- `app/domain/contas/workflow_actions.py`
+- `app/domain/contas/invoice_context.py` (correção anterior da Opção A, mantida)
+- `config/tools.yaml`
+- `config/mcp_parameter_mapping.yaml`
+- `workflows/contestacao_tool.v2.yaml` (hardening já presente)
+- `tests/migration/test_requested_tools_parity_pente_fino.py`
+- testes de invoice context previamente adicionados/mantidos
+
+## Testes de regressão adicionados
+
+O novo arquivo `tests/migration/test_requested_tools_parity_pente_fino.py` verifica, entre outros pontos:
+
+- exposição e habilitação das 18 tools;
+- façade framework-native de RAG;
+- consulta determinística de plano;
+- propagação de `invoice_detail` e resumo semântico;
+- composição LLM + pause de `invoice_explanation`;
+- regra de exatamente dois planos em pró-rata;
+- CVAL na pré-validação e rejeição de valor acima da cobrança;
+- semântica de término de desconto e valor divergente;
+- contratos diretos de VAS, histórico, status, SMS e PDF;
+- ausência do mapeamento incorreto `interaction_key -> protocol`;
+- obrigatoriedade/classificação de status na finalização.
+
+## Resultado dos testes
+
+```text
+715 passed
+```
+
+A suíte completa disponível no pacote foi executada, não apenas os testes novos.
+
+## Histórico autoritativo de descontos
+
+A capability `termino_desconto` não deve deduzir a causa da retirada de desconto a partir de parcelas, ausência do item ou texto do cliente. Foi introduzida a integração MCP `consultar_historico_descontos`, com mock em `app/domain/contas/fixtures/discount_history.json`.
+
+O serviço retorna fatos estruturados (`discount_name`, `plan_name`, `previous_value`, `current_value`, `start_date`, `end_date`, `discount_status`, `termination_reason` e `termination_reason_description`). O workflow `termino_desconto` chama essa fonte obrigatoriamente antes de compor a resposta. Se a causa não estiver explícita, mantém `epistemic_status=insufficient_evidence`; quando a causa está presente, usa `epistemic_status=grounded_fact`.
+
+A referência temporal do mock é explícita: `current_value` representa a situação contratual em `as_of_date`, enquanto `last_billed_discount_value` representa o desconto aplicado no último período faturado (`last_billed_period`). Isso evita tratar como contradição o caso em que uma fatura referente a período anterior ainda contém o desconto, embora o benefício já esteja encerrado na data contratual corrente.
+
+O mock serve somente para ilustrar o contrato que deverá ser substituído pela integração real. A resposta ao cliente é composta no agente; o serviço não retorna frase pronta.
diff --git a/tests/docs/RELATORIO_CORRECOES_FRAMEWORK_E_CONTAS_2026-08-28.md b/tests/docs/RELATORIO_CORRECOES_FRAMEWORK_E_CONTAS_2026-08-28.md
new file mode 100644
index 0000000..394cfd7
--- /dev/null
+++ b/tests/docs/RELATORIO_CORRECOES_FRAMEWORK_E_CONTAS_2026-08-28.md
@@ -0,0 +1,302 @@
+# Relatório de Correções — Agent Framework OCI + Contas
+
+Data: 2026-08-28
+Base analisada: `agent_contas_oci_template (6).zip`
+
+## 1. Objetivo
+
+Este trabalho tratou as frentes técnicas identificadas a partir do comparativo de 30 replays e, principalmente, dos contratos de regressão já existentes no próprio projeto. A separação arquitetural foi preservada:
+
+- **Framework**: lifecycle transacional, coleta genérica de parâmetros, confirmação, snapshot, roteamento/continuidade, guardrails e infraestrutura horizontal.
+- **Agent Contas / domínio / MCP**: semântica TIM, prompts voltados ao cliente, contrato das capabilities, evidência de fatura, regras de contestação, pró-rata e mapeamentos de integração.
+
+Nenhuma regra TIM foi movida para o core do framework.
+
+## 2. Correções realizadas no framework
+
+### 2.1 Coleta de parâmetros sem expor nomes internos
+
+**Problema**
+O runtime possuía um pequeno dicionário hardcoded para `order_id`, `reason` e `customer_id` e, para qualquer outro parâmetro, podia produzir o nome técnico convertido para texto. Isso explica respostas da família `informe subject` apontadas no relatório.
+
+**Correção**
+O framework agora usa metadados declarados pelo agente em `args_schema`:
+
+- `user_prompt`: pergunta exata voltada ao cliente, com maior prioridade;
+- `label`: rótulo amigável opcional;
+- `description`: fallback semântico;
+- sem metadados: pergunta neutra que **não expõe o nome técnico**.
+
+Além disso, o framework pergunta **um parâmetro por vez**, embora o extrator LLM continue capaz de consumir vários valores espontaneamente informados no mesmo turno.
+
+**Resultado arquitetural**
+O framework continua sem saber o significado de `subject`, `valor`, `order_id` etc. A semântica pertence ao agente.
+
+### 2.2 Snapshot imutável da confirmação
+
+**Problema**
+Havia `pending_tool_call` e `active_transaction`, mas não existia um snapshot separado e explícito que representasse exatamente a operação apresentada ao usuário no momento da confirmação.
+
+**Correção**
+Foi introduzido `confirmation_snapshot`, contendo:
+
+- `transaction_id`;
+- `tool_name`;
+- cópia dos `arguments`;
+- `started_from_intent`.
+
+Ao entrar em `AWAITING_CONFIRMATION`, o snapshot é congelado. Um `sim` executa **esse snapshot**, mesmo que `active_transaction`, `pending_tool_call` ou outro contexto seja alterado depois. Ao concluir/cancelar a transação, o snapshot operacional é limpo.
+
+**Benefício**
+Garante o contrato:
+
+> confirmar = executar exatamente tool + parâmetros que estavam congelados quando a confirmação foi solicitada.
+
+### 2.3 Itens do framework já presentes nesta versão e apenas revalidados
+
+Não foram duplicadas correções que já estavam na base recebida:
+
+- extração LLM de parâmetros transacionais;
+- precedência de confirmação explícita;
+- `transaction_interruption=intent_shift`;
+- encerramento/limpeza de transações `COMPLETED`, `FAILED`, `CANCELLED`, `BLOCKED`, `OUT_OF_SCOPE`;
+- route stickiness sem reaproveitar transação terminal;
+- replay pós-finalização sem reabrir atendimento;
+- validação direta de `expected_protocols` no CMP;
+- isolamento do contexto operacional dos guardrails após intent shift no Contas.
+
+## 3. Correções realizadas no Agent Contas / MCP
+
+### 3.1 Prompts declarativos dos parâmetros
+
+Foram adicionados `user_prompt` às capabilities transacionais:
+
+- `cancelar_vas_avulso.subject` → `Qual serviço você deseja cancelar?`
+- `tratar_vas_estrategico.subject` → `Qual serviço ou benefício você deseja tratar?`
+- `validar_contestacao.subject` → `Qual cobrança ou item você não reconhece?`
+- `validar_contestacao.valor` → `Qual é o valor da cobrança?`
+- `contestar_cobranca.subject` → `Qual cobrança ou item você deseja contestar?`
+- `contestar_cobranca.valor` → `Qual é o valor da cobrança que você deseja contestar?`
+
+Assim, a linguagem de atendimento fica no domínio e o framework apenas executa o contrato.
+
+### 3.2 Capability `buscar_informacao` restaurada sem duplicar RAG
+
+A capability voltou a existir no registry/MCP para manter paridade de contrato, mas não reimplementa recuperação no domínio.
+
+Ela devolve um contrato explícito:
+
+- `requires_rag=true`;
+- `source=agent_framework.rag`;
+- `rag_queries=[...]`.
+
+Portanto, a API antiga é preservada e o RAG continua sendo responsabilidade do framework.
+
+### 3.3 `invoice_explanation` preserva evidência suficiente para composição
+
+O retorno passa a preservar também:
+
+- `invoice_detail`;
+- `invoice_amount`;
+- `invoice_period`;
+- `invoice_emissao`.
+
+A action `formatar_invoice_explanation` agora sinaliza:
+
+- `await_user_input=true`;
+- `requires_llm_composition=true`;
+- `response_instruction` de composição grounded;
+- preservação da pergunta `Com essa explicação, sanei sua dúvida?`.
+
+### 3.4 Pró-rata determinístico e fail-closed
+
+Foram restaurados helpers de preparação do pró-rata:
+
+- derivação determinística dos planos a partir do PDF parseado;
+- uso da visão contratual por linha, evitando confundir DANFE com plano consolidado;
+- identificação de plano controle;
+- exigência de **exatamente dois planos**;
+- falha fechada com `requires_exactly_two_plans` quando o contrato não é atendido.
+
+Nenhum LLM é usado nessa decisão.
+
+### 3.5 CVAL aplicado também na pré-validação
+
+`validar_contestacao` deixou de apenas aceitar o item após o preflight e passou a executar a mesma validação CVAL usada antes do efeito financeiro.
+
+A validação usa:
+
+- item resolvido;
+- **valor originalmente solicitado pelo cliente**;
+- evidência de `billing_analysis`;
+- `validation_log` estruturado.
+
+Valor solicitado acima do valor comprovado é bloqueado com `reason=CVAL` e erro `valor_ajuste_maior_que_item`.
+
+Foi corrigido também um teste de regressão inconsistente: ele exigia aprovar R$ 50 para um item comprovado em R$ 10, ao mesmo tempo em que dizia proteger a regra “valor não pode exceder o item”. O caso positivo foi ajustado para R$ 10; a implementação não foi enfraquecida para satisfazer uma expectativa insegura.
+
+### 3.6 Grounding de término de desconto e valor divergente
+
+`termino_desconto` foi endurecido para não transformar uma hipótese de negócio em fato. O workflow só informa causa de retirada/término quando a evidência de backend/mock contém um campo causal explicitamente associado a desconto/promoção (por exemplo `discount_reason`, `terminationReason`, status de desconto/promoção encerrado ou data de término registrada). Contadores como `1/12`, `8/12` ou `12/12`, ausência de desconto na fatura e o próprio texto do cliente não são tratados como prova de expiração.
+
+Quando a causa não está disponível, a resposta informa que os dados existentes não registram o motivo, sem afirmar fim de fidelidade ou expiração promocional.
+
+`valor_divergente` preserva a semântica de alteração do valor do plano e referência segura ao final da linha, conforme contrato de regressão.
+
+### 3.7 Status de solicitação não usa `interaction_key` como protocolo
+
+Foi removido:
+
+`interaction_key -> protocol`
+
+O protocolo agora é extraído explicitamente da mensagem, impedindo que `message_id`/`interaction_key` seja tratado como protocolo de atendimento.
+
+### 3.8 Finalização exige status explícito
+
+`finalizar_atendimento` agora declara `status` em `requires`, e o mapping possui extração explícita do campo. Isso preserva o contrato de domínio e evita finalização sem estado definido.
+
+### 3.9 Prompt de billing mais grounded
+
+O `FaturasAgent` recebeu regra explícita para não transformar ausência de evidência em hipótese factual. Sem evidência, ele não pode afirmar como causa:
+
+- fim de promoção;
+- perda de elegibilidade;
+- alteração de consumo;
+- reajuste tarifário;
+- mudança de plano.
+
+Isso endereça diretamente o comportamento observado no comparativo, em que hipóteses eram apresentadas como explicação.
+
+### 3.10 Prompt de suporte não simula efeitos de lifecycle
+
+O `SuporteContasAgent` foi reforçado para não anunciar em texto livre:
+
+- transferência;
+- encerramento;
+- protocolo;
+- sucesso operacional.
+
+Resultados terminais devem refletir apenas o estado/tool atual. Handoff e finalização continuam controlados pela orquestração.
+
+## 4. Fontes alterados
+
+### Framework
+
+| Arquivo | Alteração |
+|---|---|
+| `agent_framework_oci/libs/agent_framework/src/agent_framework/runtime/agent_runtime.py` | Prompt declarativo de parâmetros; remoção de labels hardcoded; pergunta neutra sem leak; `confirmation_snapshot`; execução a partir do snapshot; limpeza do snapshot no lifecycle. |
+| `agent_framework_oci/libs/agent_framework/build/lib/agent_framework/runtime/agent_runtime.py` | Sincronizado com o source para manter o artefato de build consistente. |
+| `agent_framework_oci/tests/test_transactional_tool_flow.py` | Regressões para `user_prompt`, ausência de leak de nome técnico e confirmação por snapshot imutável. |
+
+### Agent Contas / MCP
+
+| Arquivo | Alteração |
+|---|---|
+| `config/tools.yaml` | `user_prompt` dos parâmetros; capability `buscar_informacao`; `finalizar_atendimento.status` obrigatório. |
+| `config/mcp_parameter_mapping.yaml` | Protocolo deixa de vir de `interaction_key`; extração explícita de `protocol`; extração explícita de `status` na finalização. |
+| `config/prompts/billing.yaml` | Proibição explícita de hipóteses causais sem evidência. |
+| `config/prompts/support.yaml` | Não simular handoff/finalização/protocolo; tratamento terminal grounded. |
+| `app/domain/contas/service.py` | `buscar_informacao`; preservação de `invoice_detail`, amount, period e emissão em `invoice_explanation`. |
+| `app/domain/contas/workflow_actions.py` | Metadados de composição LLM/await no invoice explanation; semântica de `termino_desconto` e `valor_divergente`. |
+| `contas_mcp/servers/contas_mcp_server/main.py` | Registro `buscar_informacao`; helpers de pró-rata; preparação fail-closed; CVAL na pré-validação; dispatch das novas/restauradas capabilities. |
+| `tests/migration/test_framework_agent_gap_fixes.py` | Novos contratos de regressão framework × agente. |
+| `tests/migration/test_requested_tools_parity_pente_fino.py` | Correção do caso positivo CVAL inconsistente (R$50 → R$10 comprovados). |
+
+## 5. Validação executada
+
+### Framework — testes focados das frentes alteradas
+
+Resultado:
+
+`40 passed`
+
+Incluiu:
+
+- transaction tool flow;
+- confirmação voltada ao cliente;
+- extração LLM/prevalência de parâmetros;
+- route stickiness / intent shift;
+- novos testes de snapshot e user-facing parameter contract.
+
+### Agent Contas — regressão completa de migração
+
+Resultado final:
+
+`729 passed`
+
+Antes das correções, o `test_requested_tools_parity_pente_fino.py` expunha 11 falhas. Após as correções:
+
+`14 passed` nesse arquivo e `729 passed` em toda `tests/migration`.
+
+### Suíte completa do framework
+
+Resultado observado na árvore corrigida:
+
+- `225 passed`
+- `10 failed`
+
+Os mesmos 10 casos foram executados contra o ZIP original recebido e falham da mesma forma. Portanto são **falhas preexistentes e não introduzidas por este patch**. Estão concentradas em:
+
+- compatibilidade de double de LLM em um teste unitário;
+- checkpoint repository/recovery;
+- compact telemetry Langfuse legado;
+- transactional workflow unit tests;
+- dois testes estáticos que procuram um layout de `agent_template_backend` inexistente nesse caminho.
+
+Esses itens não pertencem às frentes do comparativo tratadas neste patch e não foram mascarados.
+
+## 6. Relação com o relatório comparativo
+
+### Problemas do relatório atacados diretamente
+
+- nomes internos de parâmetros na fala;
+- coleta transacional sem contrato amigável;
+- confirmação sem snapshot explícito;
+- risco de reinterpretar argumentos depois do pedido de confirmação;
+- explicação de cobrança baseada em hipótese sem evidência;
+- gaps de capability/paridade MCP já formalizados pelos testes do projeto;
+- pró-rata sem preparação determinística completa;
+- pre-validation/CVAL incompleta;
+- confusão entre identificador de interação e protocolo;
+- finalização sem `status` obrigatório.
+
+### Problemas que já estavam corrigidos nesta versão recebida
+
+- intent shift durante transação;
+- limpeza de transação terminal;
+- replay pós-finalização;
+- barge-in pós-finalização no framework de interrupção;
+- `expected_protocols`/CMP;
+- contexto histórico de transação anterior nos guardrails do Contas.
+
+## 7. Pontos que continuam sendo política de negócio do Contas
+
+Não foram movidos para o framework, de propósito:
+
+- escada comercial de retenção TIM;
+- quando exatamente transferir para humano após retenção;
+- primeira/segunda ocorrência de fora de escopo;
+- escalonamento jurídico/Anatel específico TIM;
+- política de ressarcimento em dobro;
+- interpretação de conjuntos de cobranças como “nenhuma delas/todas”;
+- regras específicas de VAS avulso/estratégico e ações comerciais.
+
+Esses comportamentos devem ser implementados/testados no domínio Contas quando os cenários executáveis correspondentes estiverem disponíveis. O pacote recebido não contém os 30 YAMLs de replay citados no PDF, portanto este relatório **não afirma** que os 30 replays agora passam; afirma apenas os resultados das suítes efetivamente presentes e executadas no pacote.
+
+## 8. Conclusão
+
+A principal correção estrutural foi tornar a fronteira mais clara:
+
+- o **framework** controla coleta, lifecycle e confirmação sem expor nomes internos e sem reinterpretar o que foi confirmado;
+- o **agente Contas** fornece a linguagem de negócio e os contratos/evidências específicos;
+- o **MCP Contas** mantém capabilities e validações determinísticas de domínio sem absorver responsabilidades de conversa/RAG do framework.
+
+A regressão do Contas presente no projeto ficou integralmente verde (`729 passed`).
+
+### Serviço MCP de histórico de descontos
+
+Foi adicionada a tool interna `consultar_historico_descontos` para representar a fonte autoritativa de status e término de descontos. O mock está em `app/domain/contas/fixtures/discount_history.json` e a implementação em `contas_mcp/servers/contas_mcp_server/discount_history_service.py`.
+
+`termino_desconto` consulta esse serviço obrigatoriamente e só verbaliza uma causa quando `termination_reason`, `termination_reason_description` ou outro campo causal explicitamente permitido estiver presente. Códigos técnicos permanecem em metadados; a resposta usa a descrição legível do sistema. Sem causa explícita, o fluxo continua fail-closed.
+
+O mock também distingue explicitamente a **situação contratual na data de referência** da **última fatura emitida**. No cenário atual, `current_value=0` significa valor contratual do desconto em `as_of_date=2025-11-20`; a última fatura cobre `14/10 a 13/11` e ainda registra R$ 80,00 de desconto. Isso é temporalmente consistente: o desconto estava vigente no período faturado e aparece como encerrado na situação contratual de 20/11/2025. Os campos `last_billed_discount_value`, `last_billed_period`, `last_invoice_issue_date` e `current_value_reference` documentam essa diferença.
diff --git a/tests/docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md b/tests/docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md
new file mode 100644
index 0000000..66c2042
--- /dev/null
+++ b/tests/docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md
@@ -0,0 +1,423 @@
+# Relatório técnico — políticas alternativas de operação por linha
+
+## 1. Objetivo
+
+O Agent Contas possui duas políticas alternativas para controlar operações em uma linha (MSISDN) diferente da linha identificada/autenticada no início do atendimento.
+
+A implementação permanece no **Agent Contas/MCP Contas**, sem regra TIM hardcoded no core do `agent_framework_oci`.
+
+A política ativa entregue no projeto continua sendo a **ALT1 — somente a linha autenticada**.
+
+A ALT2 foi evoluída para não inferir autorização a partir de fatura, billing ou texto do cliente. Ela depende de uma fonte explícita de autorização: a nova tool MCP mock `consultar_linhas_autorizadas`.
+
+## 2. Políticas disponíveis
+
+### 2.1 ALT1 — `authenticated_line_only` — PADRÃO
+
+Fonte:
+
+```text
+contas_mcp/servers/contas_mcp_server/line_policy_alt1.py
+```
+
+Comportamento:
+
+- a linha operacional continua sendo a linha identificada pelo `business_context` da chamada;
+- uma linha citada em texto livre **não substitui** a identidade da sessão;
+- se o cliente mencionar explicitamente outra linha — número completo ou referência como `final 4321` — a execução é bloqueada antes de qualquer operação de domínio;
+- o bloqueio é terminal para o turno e interrompe as tools seguintes;
+- a mensagem devolvida é:
+
+```text
+Por segurança, este atendimento só permite consultar ou realizar operações na linha identificada na chamada. Não posso usar outra linha informada na conversa.
+```
+
+Exemplo:
+
+```text
+linha autenticada: 11999999999
+cliente: "quero cancelar o streaming do número da minha esposa, final quatro três dois um"
+
+resultado:
+LINE_POLICY_BLOCKED / other_line_not_allowed
+nenhuma consulta/cancelamento é executado para a outra linha
+```
+
+### 2.2 ALT2 — `authorized_related_lines`
+
+Fonte:
+
+```text
+contas_mcp/servers/contas_mcp_server/line_policy_alt2.py
+```
+
+Comportamento:
+
+- a linha autenticada continua sendo a origem de confiança;
+- uma outra linha só pode ser usada se for retornada pelo serviço explícito `consultar_linhas_autorizadas`;
+- referências como `final 4321` são resolvidas somente contra as linhas autorizadas retornadas por esse serviço;
+- se houver exatamente uma correspondência, ela vira o `effective_msisdn` da operação;
+- se não houver correspondência, a operação é bloqueada;
+- se houver mais de uma correspondência, o fluxo exige esclarecimento;
+- se o serviço de linhas autorizadas falhar, o ALT2 opera em **fail-closed**: somente a linha autenticada permanece autorizada;
+- a presença de um MSISDN em `invoice_detail`, `billing_analysis` ou outra evidência de cobrança **não concede autorização operacional**;
+- um número pronunciado pelo cliente também **não concede autorização**.
+
+Exemplo:
+
+```text
+linha autenticada: 11999999999
+consultar_linhas_autorizadas retorna:
+ - 11999999999 (titular)
+ - 11988884321 (dependente autorizado)
+
+cliente: "quero cancelar o TIM Fashion da linha final 4321"
+
+resultado ALT2:
+requested reference = 4321
+effective_msisdn = 11988884321
+operação pode prosseguir nessa linha
+```
+
+## 3. Novo serviço MCP mock — `consultar_linhas_autorizadas`
+
+### 3.1 Objetivo
+
+Foi criada uma tool MCP side-effect-free para representar a integração que, em produção, deve consultar um serviço de identidade/conta e responder **quais linhas o atendimento autenticado está autorizado a operar**.
+
+Tool:
+
+```text
+consultar_linhas_autorizadas
+```
+
+Registro MCP:
+
+```text
+contas_mcp/servers/contas_mcp_server/main.py
+```
+
+Implementação mock:
+
+```text
+contas_mcp/servers/contas_mcp_server/authorized_lines_service.py
+```
+
+Fixture mock:
+
+```text
+app/domain/contas/fixtures/authorized_lines.json
+```
+
+### 3.2 Contrato de entrada
+
+A consulta parte da identidade já autenticada no atendimento. O cliente não informa qual linha deve ser autorizada.
+
+Exemplo:
+
+```json
+{
+ "msisdn": "11999999999",
+ "customer_key": "11999999999",
+ "contract_key": "3000131180"
+}
+```
+
+O `msisdn` acima é a linha autenticada/original da chamada.
+
+### 3.3 Contrato de saída mock
+
+```json
+{
+ "success": true,
+ "status": "SUCCESS",
+ "source": "mock",
+ "authenticated_msisdn": "11999999999",
+ "authorized_lines": [
+ {
+ "msisdn": "11999999999",
+ "relationship": "titular",
+ "status": "ACTIVE",
+ "authorized": true
+ },
+ {
+ "msisdn": "11988884321",
+ "relationship": "dependente",
+ "status": "ACTIVE",
+ "authorized": true
+ }
+ ],
+ "authorized_msisdns": [
+ "11999999999",
+ "11988884321"
+ ]
+}
+```
+
+### 3.4 Por que existe uma tool MCP separada
+
+O objetivo é deixar explícita a arquitetura de produção:
+
+```text
+identidade autenticada da chamada
+ ↓
+consultar_linhas_autorizadas
+ ↓
+serviço legado/CRM/IAM/conta
+ ↓
+lista de linhas realmente autorizadas
+ ↓
+line_policy_alt2
+ ↓
+resolve referência conversacional
+ ↓
+0 matches → bloqueia/clarifica
+1 match → effective_msisdn
+>1 matches → clarifica
+```
+
+A autorização não pertence ao LLM. O LLM/text extractor pode interpretar `final 4321`, mas não decide se `4321` é uma linha autorizada.
+
+### 3.5 Comportamento em produção
+
+O arquivo `authorized_lines_service.py` é propositalmente um mock de referência. Em produção, ele deve ser substituído por um adapter que consulte o serviço corporativo responsável pela relação titular/dependentes/linhas autorizadas.
+
+O contrato recomendado deve preservar pelo menos:
+
+```text
+success
+authenticated_msisdn
+authorized_lines[].msisdn
+authorized_lines[].status
+authorized_lines[].authorized
+authorized_lines[].relationship
+authorized_msisdns
+```
+
+Se a integração real falhar ou não puder provar a autorização da outra linha, o comportamento esperado do ALT2 é fail-closed.
+
+## 4. Fluxo ALT2 atualizado
+
+O fluxo completo ficou:
+
+```text
+mensagem do cliente
+ ↓
+extrai requested_line_reference
+ ex.: suffix=4321
+ ↓
+business_context mantém 11999999999
+ ↓
+MCP detecta ALT2 ativo
+ ↓
+consultar_linhas_autorizadas(11999999999)
+ ↓
+authorized_lines_evidence
+ ↓
+line_policy_alt2
+ ↓
+resolve 4321 somente contra authorized_lines_evidence
+ ↓
+effective_msisdn = 11988884321
+ ↓
+só então a tool/workflow de negócio é executada
+```
+
+A ALT2 não usa mais `invoice_detail`, `billing_analysis` ou `complete_invoices_payload` como fonte de **autorização** de linha.
+
+## 5. Política ativa
+
+O MCP importa sempre:
+
+```text
+contas_mcp/servers/contas_mcp_server/line_policy.py
+```
+
+No pacote entregue, `line_policy.py` é uma cópia exata de `line_policy_alt1.py`.
+
+Portanto, **o comportamento corrente permanece bloqueando operações em outra linha**.
+
+O `/health` informa a política carregada:
+
+```json
+{
+ "line_policy": "authenticated_line_only",
+ "line_policy_description": "Somente a linha identificada/autenticada na chamada pode ser consultada ou alterada."
+}
+```
+
+## 6. Como ativar ALT1
+
+Forma recomendada:
+
+```bash
+python scripts/select_line_policy.py alt1
+```
+
+Depois reinicie o backend/MCP Server.
+
+Linux/macOS:
+
+```bash
+cp contas_mcp/servers/contas_mcp_server/line_policy_alt1.py \
+ contas_mcp/servers/contas_mcp_server/line_policy.py
+```
+
+PowerShell:
+
+```powershell
+Copy-Item `
+ contas_mcp/servers/contas_mcp_server/line_policy_alt1.py `
+ contas_mcp/servers/contas_mcp_server/line_policy.py -Force
+```
+
+## 7. Como ativar ALT2
+
+```bash
+python scripts/select_line_policy.py alt2
+```
+
+Depois reinicie o backend/MCP Server.
+
+Ao iniciar com ALT2, o MCP passa a consultar automaticamente `consultar_linhas_autorizadas` quando houver uma referência explícita a linha no turno.
+
+Não é necessário inserir manualmente:
+
+```python
+context["authorized_msisdns"] = [...]
+```
+
+nem:
+
+```python
+args["authorized_msisdns"] = [...]
+```
+
+A lista vem do serviço MCP de autorização.
+
+## 8. Como alterar o mock para testes
+
+Para ilustrar outra linha autorizada, edite apenas:
+
+```text
+app/domain/contas/fixtures/authorized_lines.json
+```
+
+Exemplo:
+
+```json
+{
+ "msisdn": "11977771234",
+ "relationship": "dependente",
+ "status": "ACTIVE",
+ "authorized": true
+}
+```
+
+Não altere `line_policy_alt2.py` para cadastrar linhas.
+
+Esse desenho deixa claro que a política apenas **consome autorização**; ela não é o cadastro das linhas autorizadas.
+
+## 9. Abrangência
+
+A política é aplicada no ponto único `_invoke()` do MCP Contas antes da execução de domínio. Dessa forma cobre as tools/serviços baseados em MSISDN, inclusive quando passam por workflows.
+
+Cobertura funcional inclui:
+
+- `consultar_faturas`
+- `consultar_plano`
+- `invoice_explanation`
+- `consultar_vas`
+- `consultar_historico_vas`
+- `cancelar_vas_avulso`
+- `tratar_vas_estrategico`
+- `validar_vas_subject`
+- `validar_contestacao`
+- `contestar_cobranca`
+- `pro_rata`
+- `termino_desconto`
+- `valor_divergente`
+- `consultar_status_solicitacao`
+- `enviar_sms`
+- `recuperar_fatura_pdf`
+- `finalizar_atendimento`
+
+`consultar_linhas_autorizadas` é a fonte de autorização da ALT2 e não passa pela própria política para evitar dependência circular.
+
+`buscar_informacao` não depende de linha e `retomar_workflow` apenas retoma execução já iniciada.
+
+## 10. Arquivos alterados/criados
+
+### Política de linha
+
+```text
+contas_mcp/servers/contas_mcp_server/line_policy.py
+contas_mcp/servers/contas_mcp_server/line_policy_alt1.py
+contas_mcp/servers/contas_mcp_server/line_policy_alt2.py
+```
+
+### Novo serviço MCP de autorização
+
+```text
+contas_mcp/servers/contas_mcp_server/authorized_lines_service.py
+app/domain/contas/fixtures/authorized_lines.json
+contas_mcp/servers/contas_mcp_server/main.py
+```
+
+### Referência conversacional e seleção da política
+
+```text
+app/domain/contas/line_reference.py
+scripts/select_line_policy.py
+```
+
+### Testes e documentação
+
+```text
+tests/migration/test_line_policy_alternatives.py
+docs/RELATORIO_POLITICAS_DE_LINHA_ALT1_ALT2.md
+```
+
+## 11. Validação
+
+Testes específicos da política e do novo mock:
+
+```text
+12 passed
+```
+
+Smoke ALT2:
+
+```text
+policy = authorized_related_lines
+authorized = [11999999999, 11988884321]
+requested = final 4321
+allowed = true
+effective = 11988884321
+```
+
+Após o smoke, ALT1 foi restaurado e validado como política ativa entregue.
+
+Suíte completa de migração com ALT1 ativa:
+
+```text
+765 passed
+```
+
+## 12. Decisão arquitetural
+
+Responsabilidades finais:
+
+| Camada | Responsabilidade |
+|---|---|
+| Framework | identidade/contexto, execução genérica, terminalidade e short-circuit de tools |
+| Agent/MCP Contas | política ALT1/ALT2 e integração de autorização |
+| `consultar_linhas_autorizadas` | informar quais linhas a identidade autenticada está autorizada a operar |
+| Backend real futuro | fonte de verdade de titular/dependentes/autorização |
+| LLM | interpretar a referência conversacional; nunca conceder autorização |
+
+A principal regra arquitetural é:
+
+> **linha mencionada ≠ linha autorizada**
+
+A autorização precisa vir de uma fonte explícita e confiável. Na versão demonstrativa essa fonte é o mock MCP `consultar_linhas_autorizadas`; em produção, deve ser substituída pela integração corporativa correspondente.
diff --git a/tests/docs/VALIDACAO_MIGRACAO.md b/tests/docs/VALIDACAO_MIGRACAO.md
new file mode 100644
index 0000000..f960a68
--- /dev/null
+++ b/tests/docs/VALIDACAO_MIGRACAO.md
@@ -0,0 +1,295 @@
+# 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
+```
diff --git a/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc
index 1f8ed39..8357fb8 100644
Binary files a/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc
index f1ebe29..64ddc57 100644
Binary files a/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_adversarial_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc
index cabcf0d..fa16a45 100644
Binary files a/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_backend_wrapper_contract_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc
index dcbeadd..ebc73db 100644
Binary files a/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_business_events_and_guardrails.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc
index 926bf2a..dd946b0 100644
Binary files a/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_backend_wrapper_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc
index 04df92e..c2915b0 100644
Binary files a/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_composite_workflow.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc
index 0e26019..e15ac38 100644
Binary files a/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_cancel_wrapper_remaining_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc
new file mode 100644
index 0000000..9eff3ee
Binary files /dev/null and b/tests/migration/__pycache__/test_coer_semantic_prompt_contract.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc
index 1136a28..61bb8fd 100644
Binary files a/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contas_domain_mock.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc
index b0520f1..48a4e77 100644
Binary files a/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contas_invoice_explanation_routing.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc
index edbf7e1..d51697f 100644
Binary files a/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_business_rules_full.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc
index b931732..cfb1ae9 100644
Binary files a/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_failure_mapping.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc
index 16e04ef..da2211f 100644
Binary files a/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_prompt_terminal_out_of_scope.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc
index b246278..7cf7ad8 100644
Binary files a/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_single_item_mock_regression.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc
index 048e47e..f0bfbaf 100644
Binary files a/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_contestation_subject_entity_resolution.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc
index ede5d1f..89dc352 100644
Binary files a/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_discount_history_mcp_service.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc
index f9b7993..383a5f0 100644
Binary files a/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_dispute_actions_remaining_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc
index 0ba4609..a8bcabe 100644
Binary files a/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_external_guardrails_judges_spi.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc
index 0a86270..3cdc6d8 100644
Binary files a/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_finalization_parity_extended.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc
index 61c210c..07ecd4c 100644
Binary files a/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_agent_gap_fixes.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc
index 8c05746..c26f17c 100644
Binary files a/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_llm_composition_directive.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc
index 0e84c2d..a8c1765 100644
Binary files a/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_native_structure.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc
index d37dc7e..4dcb845 100644
Binary files a/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_rag_directive.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc
index 14c5743..8f00c16 100644
Binary files a/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_framework_zero_legacy.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc
index 7f89119..3be12a5 100644
Binary files a/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_binary_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc
index 46bba07..36f5cd9 100644
Binary files a/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_context_dict_history.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc
index 4be4182..c4d9299 100644
Binary files a/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_guardrail_original_defaults_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc
index 63edf42..46d0518 100644
Binary files a/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_human_handoff_guardrail_context.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc
index 210ed65..abf7764 100644
Binary files a/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_input_guardrail_user_feedback.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc
index 2bad33a..3ce9bae 100644
Binary files a/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_context_framework_native.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc
index ecb5e81..033b9f2 100644
Binary files a/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_final_response.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc
index 7adc904..e9532de 100644
Binary files a/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_handoff_policy.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc
index ef4c528..b8246ac 100644
Binary files a/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_invoice_explanation_unmatched_meaningful.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc
index 00d4cbc..9d23286 100644
Binary files a/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_line_policy_alternatives.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc
index cc24aed..89698e8 100644
Binary files a/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_mcp_contestation_subject_guard.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc
index 3c46450..e27cb21 100644
Binary files a/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_no_legacy_dependency.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc
index f54e8dd..edb6eed 100644
Binary files a/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_code_mapping.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc
index a00a868..9dc2b6c 100644
Binary files a/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_default_overlay_compatibility.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc
index 98170a2..f55a535 100644
Binary files a/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_mapping_provider_boundary.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc
index 46609e0..0d1e4a6 100644
Binary files a/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_observability_mapping_registry_actions.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc
index b07780c..33f8843 100644
Binary files a/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_compliance_anatel.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc
index 74c60d5..9e8dc9d 100644
Binary files a/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_contestation_validation.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc
index 004efbe..ab8c9b2 100644
Binary files a/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_item_matcher_transcription.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc
index d3aed9b..8fa25a6 100644
Binary files a/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_vas_cancellation_message.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc
index feef625..ad44cdc 100644
Binary files a/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_vas_variation.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc
index ae1b709..0f02333 100644
Binary files a/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_vas_variation_adversarial.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc
index 52fcc86..10d1a9a 100644
Binary files a/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_original_workflow_cases.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc
index 235e056..1e3f40e 100644
Binary files a/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_output_supervisor_no_contract_hardcodes.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc
index 327619a..b191a85 100644
Binary files a/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_pro_rata_full_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc
index 632d130..d194b2b 100644
Binary files a/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_rag_resilience_and_informational_context.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc
index b1cd14d..9a190a3 100644
Binary files a/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_real_legacy_integration_compat.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc
index bde8924..e3f5367 100644
Binary files a/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_remaining_command_contracts.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc
index a63a299..0e53775 100644
Binary files a/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_requested_tools_parity_pente_fino.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc
index aa89d56..a0f2d60 100644
Binary files a/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_revprec_epistemic_uncertainty.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc
index 1d7688b..2444470 100644
Binary files a/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_scenario_23_live_internet_balance_guidance.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc
index 1896825..745a525 100644
Binary files a/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_termino_desconto_grounding.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc
index 1ae2238..2c6e069 100644
Binary files a/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_aoferta_transaction_continuation.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc
index d60b9b4..6b5347b 100644
Binary files a/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_contract_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc
index fccbe76..cf9e1a3 100644
Binary files a/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_contracts_and_idempotency.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc
index 105b9b8..5872fb1 100644
Binary files a/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_event_metadata_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc
index 95a1c25..c28ae8c 100644
Binary files a/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tim_oos_handoff_bypass.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc
index ad63ecb..90fead3 100644
Binary files a/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_tool_policy_operation_types.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc
index cf44e09..8fdda3b 100644
Binary files a/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_vaa_dispute_parity.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc
index 44d653e..7ba20e3 100644
Binary files a/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_vas_response_renderer.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc
index a3d0a27..3b6d41c 100644
Binary files a/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_workflow_execution_latch.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc b/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc
index f028ce7..d54802f 100644
Binary files a/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc and b/tests/migration/__pycache__/test_wrapper_last_contracts.cpython-313-pytest-9.0.2.pyc differ
diff --git a/tests/migration/test_coer_semantic_prompt_contract.py b/tests/migration/test_coer_semantic_prompt_contract.py
new file mode 100644
index 0000000..8fd4788
--- /dev/null
+++ b/tests/migration/test_coer_semantic_prompt_contract.py
@@ -0,0 +1,27 @@
+from agent_framework.guardrails.calibrated.prompts.coerencia import build_coer_prompt
+
+
+def test_coer_prompt_delega_intencao_parametros_e_execucao_para_camadas_seguintes():
+ prompt = build_coer_prompt("qualquer fala", "")
+ lowered = prompt.lower()
+ assert "não tente decidir aqui" in lowered
+ assert "qual é a intenção" in lowered
+ assert "faltam parâmetros" in lowered
+ assert "mudança de intenção" in lowered
+ assert "compreensível mas sem todos os parâmetros -> 1" in lowered
+
+
+def test_coer_prompt_trata_negacao_sem_heuristica_textual_chumbada():
+ prompt = build_coer_prompt("qualquer fala", "")
+ lowered = prompt.lower()
+ assert "contendo negação/discordância -> 1" in lowered
+ assert "tire esse \"não\"" not in lowered
+ assert "não quero parcelar" not in lowered
+ assert "cancelar, tirar cobrança" not in lowered
+
+
+def test_coer_prompt_bloqueia_apenas_incompreensibilidade_semantica_real():
+ prompt = build_coer_prompt("qualquer fala", "")
+ lowered = prompt.lower()
+ assert "bloquear apenas incompreensibilidade" in lowered
+ assert "não incerteza de negócio" in lowered