diff --git a/README.md b/README.md index d4eeac2..8c5c962 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ ## Reference Architecture para acesso agêntico governado por Client-Bound Entitlements **Status:** Working Draft v0.2 -**Publicação:** 3 de setembro de 2026 **Escopo:** Arquitetura corporativa para publicar, descobrir, personalizar, orquestrar e executar capacidades empresariais por meio de MCP, com um MCP Gateway atuando como Policy Enforcement Point (PEP), o MCP Client definindo o limite máximo de privilégio e o Service Orchestrator calculando a Next Best Action (NBA) dentro desse limite. ## Reference implementation -A implementação executável desta arquitetura está em [`RI/`](RI/README.md), com gateway MCP, PEP/PDP, OPA, Keycloak, serviços de exemplo, testes adversariais e ambiente Docker Compose. +A implementação executável desta arquitetura está planejada para [`RI/`](RI/README.md) — diretório a ser criado a partir do backlog estruturado em [`docs/RI-PLANNING.md`](docs/RI-PLANNING.md) — com gateway MCP, PEP/PDP sobre OPA (Rego v1), Keycloak como Identity Provider (OAuth 2.1/OIDC), serviços de exemplo, testes adversariais e ambiente Docker Compose. + +> **Nota de implementação (v0.2+).** A camada de orquestração de agentes (LangGraph) e de observabilidade de LLM (Langfuse) da RI adota como referência operacional o projeto open-source *Agent Platform OCI*, de Christiano Hoshikawa — ver §6.2, §6.12, §35.1 e §43.5 para as anotações correspondentes, e `docs/research/hoshikawa-agent-platform-oci.md` para a análise completa. ### Novidades da v0.2 @@ -578,6 +579,8 @@ Guardrails são defense-in-depth, não substitutos do PEP. O Orchestrator decide **qual ação tentar**. O PEP/PDP decide **se aquela ação pode ser executada**, e o backend continua decidindo sobre seus próprios recursos. +> **Nota de implementação.** Na prática, este papel é frequentemente cumprido por uma plataforma de orquestração multiagente externa e independente do bus — por exemplo, um framework baseado em LangGraph com múltiplos backends de agente e um supervisor global de roteamento entre eles (ver `Agent Platform OCI`, §43.5). Quando isso ocorre, cada backend/agente dessa plataforma deve se conectar ao Enterprise MCP Service Bus como um MCP Client distinto (§6.3), nunca como uma extensão de confiança do Gateway. A relação entre este papel e o Service Orchestrator (§6.12) desta arquitetura é de escolha modular, não de sobreposição: ver §6.12 para os dois modos de integração possíveis. + --- ### 6.3 MCP Client @@ -903,8 +906,12 @@ Responsabilidades: - correlacionar decisão, chamada MCP, execução e resultado; - recalcular ou degradar com segurança quando uma ação for negada ou indisponível. +> **Nota de implementação.** O termo NBA usado nesta arquitetura cobre também a escolha de uma **Next Best Offer (NBO)** quando o universo de `FilteredOfferings` (§6.11) inclui ofertas comerciais, benefícios ou campanhas, e não apenas ações transacionais. A "função de decisão versionada" citada acima é deliberadamente pluggable — pode ser um conjunto de regras determinístico ou incorporar um modelo de IA de ranking/recomendação — desde que opere estritamente sobre `FilteredOfferings`, nunca ampliando o universo permitido (Axiom 11). + O Orchestrator não pode emitir credenciais de maior privilégio, alterar entitlement, suprimir a reautorização de `tools/call` ou substituir a decisão do backend. +> **Nota de implementação — dois modos de integração.** Esta arquitetura não impõe onde o cálculo de NBA/NBO deve residir. **Modo A (governança-somente):** uma plataforma de orquestração externa (§6.2) mantém sua própria lógica de decisão e consome o bus apenas para acesso governado ao Fabric. **Modo B (substituição completa):** o Service Orchestrator aqui descrito assume integralmente o cálculo de NBA/NBO, e a plataforma externa, quando existente, atua apenas como executor de dispatch via Agent Runtime (§6.13). Em ambos os modos o núcleo de segurança (§6.4, §6.5) permanece no mesmo lugar — apenas a origem da decisão de NBA/NBO muda. Modularidade e compatibilidade são, portanto, uma escolha do adotante, não uma imposição da arquitetura. + ### 6.13 Agent Runtime e channel adapters O Agent Runtime traduz a NBA em execução coordenada. Ele gerencia sessão, contexto mínimo, lifecycle do agente, retries seguros e handoff. Channel adapters encapsulam diferenças entre experiências conversacionais, notificações proativas, mídia, campanhas e novos agentes especializados. @@ -2115,6 +2122,8 @@ agent.role = "finance-admin" **Problema:** informação controlada pelo agente eleva privilégio. +> **Nota de implementação.** Este anti-pattern não é apenas hipotético. A análise de um MCP Gateway de terceiros (projeto *Agent Platform OCI*, §43.5) encontrou exatamente esta forma: um allowlist de tools avaliado sobre um campo `agent_id` que chega como dado do payload de entrada da conversa, não como claim de uma identidade autenticada verificada independentemente — a própria documentação daquele projeto reconhece que "a política conversacional não substitui autenticação, autorização, idempotência nem atomicidade no MCP Server". O caso serve como validação prática do princípio P3 (§4): qualquer campo que o agente, o canal ou o payload de entrada possam declarar deve ser tratado como não confiável para fins de autorização, mesmo quando usado de boa-fé por um MCP Gateway aparentemente funcional. + --- ### 35.2 Catálogo completo + deny somente na execução @@ -2452,6 +2461,12 @@ Publicações recentes descrevem: Esses trabalhos mostram convergência do mercado para gateways, agregação e policy enforcement. +### 43.5 Plataformas de orquestração multiagente + +Frameworks de orquestração de agentes baseados em LangGraph — como o projeto open-source **Agent Platform OCI**, de Christiano Hoshikawa (`github.com/hoshikawa2/agent_platform_oci`) — resolvem um problema complementar ao desta arquitetura: coordenação de múltiplos agentes/backends, roteamento de conversa, handoff, checkpointing de estado e observabilidade de LLM (no caso daquele projeto, via Langfuse, com uma taxonomia de eventos de negócio/operacional/guardrail). Esse tipo de plataforma tipicamente expõe seu próprio "MCP Gateway" para dar aos agentes acesso a tools, mas com um modelo de autorização simplificado — no caso analisado, um allowlist declarativo por `agent_id`, sem PDP dedicado nem separação formal entre discovery e execution authorization. + +A relação entre esta arquitetura e uma plataforma desse tipo não é de concorrência, mas de composição: a plataforma de orquestração cumpre o papel de Agent Host/Orchestrator (§6.2) e, opcionalmente, de Service Orchestrator (§6.12, Modo A), enquanto o Enterprise MCP Service Bus assume integralmente o papel de PEP/PDP e Fabric — cada backend de agente daquela plataforma conecta-se ao bus como um MCP Client distinto (§6.3), e o "MCP Gateway" interno da plataforma deixa de ser a fonte de autorização, sendo substituído pelo Gateway desta arquitetura. Ver §35.1 para o anti-pattern especificamente observado nesse tipo de integração, e §6.12 para o Modo B, em que o Service Orchestrator desta arquitetura substitui inteiramente a camada de decisão de NBA/NBO da plataforma externa. + --- ## 44. Relação com governança interna de Skills/MCP @@ -2656,17 +2671,20 @@ A visão arquitetural está consistente, mas uma implementação concreta precis - workload identity? - mTLS? - cloud IAM? + - → **Decisão adotada na RI**: OAuth 2.1 `client_credentials` via Keycloak, com mTLS/workload identity documentados como extensão de produção não implementada (`docs/RI-PLANNING.md`, ADR-019). 2. **Qual será a granularidade dos client profiles?** - domínio? - domínio + read/write? - domínio + risk tier? + - → **Decisão adotada na RI**: domínio + risk tier (ver §17; `docs/RI-PLANNING.md`, ADR-008). 3. **Qual engine implementará o PDP?** - engine próprio? - Cedar? - OPA/Rego? - IAM/policy service existente? + - → **Decisão adotada na RI**: OPA/Rego (sintaxe v1), self-hosted (`docs/RI-PLANNING.md`, ADR-025 — não confundir com o ADR-010 acima, "Global Capability Registry Governance", que é uma decisão distinta). 4. **Quem é owner do Global Capability Registry?** @@ -2674,16 +2692,21 @@ A visão arquitetural está consistente, mas uma implementação concreta precis - workload identity? - OBO/token exchange? - backend service account? + - → **Decisão adotada na RI**: token exchange (RFC 8693) quando o backend suportar OIDC, senão service account isolado por adapter (`docs/RI-PLANNING.md`, ADR-020). 6. **Quais tools exigirão human approval?** 7. **Como policy revocation invalidará caches?** + - → **Decisão adotada na RI**: invalidação disparada por evento de revogação/mudança de política, com TTL curto como rede de segurança adicional (`docs/RI-PLANNING.md`, EP-04-T03/EP-05-T03). 8. **Qual será o modelo de federation entre domain MCP servers?** + - → **Escopo da RI**: federação completa fica fora de escopo — a RI demonstra 2 domínios (Sales, Finance), suficiente para provar segmentação/blast-radius (`docs/RI-PLANNING.md`, lacuna G9). 9. **Como impedir bypass em ambientes híbridos/multi-cloud?** + - → **Escopo da RI**: a RI roda 100% local via Docker Compose; ambientes híbridos/multi-cloud ficam fora de escopo (`docs/RI-PLANNING.md`, lacuna G10). 10. **Como separar dev/test/prod client identities e entitlements?** + - → **Decisão adotada na RI**: overlay de configuração por ambiente (`config/env/{dev,ci}.yaml`), sem infraestrutura cloud real (`docs/RI-PLANNING.md`, P10). --- @@ -2711,6 +2734,8 @@ ADR-016: Agent Runtime Dispatch and Handoff Contract ADR-017: End-to-End Decision and Execution Correlation ``` +> **Nota de implementação.** A RI estende esta lista com ADR-018 em diante — decisões de tecnologia concreta (stack Python/YAML, engine do PDP, Identity Provider, downstream identity, referência de implementação Python/LangGraph/Langfuse, posicionamento frente a plataformas de orquestração externas) necessárias para transformar estes 17 ADRs conceituais em uma implementação executável. Ver `docs/RI-PLANNING.md`, seção 8.4, para a lista completa (ADR-001 a ADR-025) e `docs/research/hoshikawa-agent-platform-oci.md` para a pesquisa que fundamenta ADR-022 a ADR-024. + --- ## 50. Axiomas da arquitetura diff --git a/docs/RI-PLANNING.md b/docs/RI-PLANNING.md new file mode 100644 index 0000000..6d834a1 --- /dev/null +++ b/docs/RI-PLANNING.md @@ -0,0 +1,1553 @@ +# Planejamento da Reference Implementation (RI) — Enterprise MCP Service Bus + +> Gerado a partir do `README.md` (Working Draft v0.2, 2026-09-03) na raiz do repositório, seguindo o processo de planejamento em 4 fases (extração → decisões técnicas → decomposição em backlog → autovalidação). Este documento **planeja**, não implementa: nenhum código de produção foi escrito. + +--- + +## 8.1 Resumo da arquitetura compreendida + +O README descreve uma **reference architecture** (não uma RI pronta) para um Enterprise MCP Service Bus: uma camada de integração corporativa acessível via MCP, em que o **MCP Client** (identidade autenticada de workload) define o teto máximo de privilégio (`MaximumEntitlement`), o **MCP Gateway** atua como PEP determinístico, e um **PDP/Policy Engine** avalia entitlement policy e transaction policy. O agente (LLM) nunca é raiz de confiança: informação por ele controlada pode restringir mas nunca ampliar autorização (`EffectiveCapabilities ⊆ ClientEntitlement`). Um **Enterprise MCP Fabric** publica/roteia/media capabilities entre domínios e backends corporativos existentes (API Gateway, ESB, service mesh, SaaS). Em torno desse núcleo de segurança, a v0.2 adiciona uma cadeia de personalização e orquestração governadas: **Service owners + publishing pipeline** alimentam o **Global Capability Registry**; **Profile Intelligence** produz uma visão de perfil genérica e substituível; o **Entitlement Manager** resolve cardápios MCP personalizados; o **Offering Filter** intersecta catálogo, entitlement, perfil e consentimento; o **Service Orchestrator** calcula a Next Best Action (NBA) sobre esse universo já governado; e o **Agent Runtime** despacha a NBA para agentes/canais. Toda decisão de relevância (perfil, ranking, NBA) é estritamente um subconjunto do que o entitlement já permite — nunca o contrário. O documento define 17 RFs, RNFs de segurança/disponibilidade/performance/escalabilidade/portabilidade, um modelo de risco (R0–R4), política de fail-closed, 10 testes de aceite de segurança e 18+ cenários de teste adversarial. + +**Componentes identificados:** Agent · Agent Host/Orchestrator · MCP Client · MCP Gateway/PEP · PDP/Policy Engine · Global Capability Registry · Enterprise MCP Fabric · Service owners/Publishing pipeline · Entitlement Manager · Profile Intelligence · Offering Filter · Service Orchestrator · Agent Runtime/Channel adapters · Identity Provider · Audit/Telemetry. + +--- + +## 8.2 Matriz de rastreabilidade + +| Seção do README | Componente/Requisito | Tipo | Épico(s) | +|---|---|---|---| +| §6.1 | Agent | RF | EP-13 | +| §6.2 | Agent Host/Orchestrator | RF | EP-11, EP-12 (ver [LACUNA] G12) | +| §6.3, §18, §19 | MCP Client, identidade, shared-client rule | RF/RNF | EP-01 | +| §6.4, §9, §10, §21, §22 | MCP Gateway/PEP, tools/list, tools/call, anti-bypass, header routing | RF | EP-05 | +| §6.5, §25 | PDP/Policy Engine, entitlement+transaction policy | RF | EP-03 | +| §6.6, §23, §24 | Global Capability Registry, governance lifecycle, control/data plane | RF | EP-02 | +| §6.7, §32, §34 | Enterprise MCP Fabric, integração com plataformas existentes, topologia | RF | EP-06, EP-07 | +| §6.8 | Service owners, publishing pipeline, manifesto | RF | EP-02 | +| §6.9, §41 | Entitlement Manager, client profile | RF | EP-04 | +| §6.10 | Profile Intelligence, clusterização genérica | RF | EP-09 | +| §6.11, §7.3 | Offering Filter, conjuntos Global/Max/Filtered/Ranked | RF | EP-10 | +| §6.12 | Service Orchestrator, NBA | RF | EP-11 | +| §6.13 | Agent Runtime, channel adapters | RF | EP-12 | +| §7, §17 | Modelo de entitlement, risk tiers R0–R4 | RF | EP-02, EP-04 | +| §8, §11, §12 | Client-bound capability view, discovery vs execution, dynamic catalog rule | RF | EP-05 | +| §13 | Caching privado de tools/list | RNF | EP-05 | +| §14, §15, §29, §30 | Prompt injection, escalation vs abuse, cenários de ataque | RNF/Segurança | EP-15 | +| §16 | Modelo em camadas de segurança | RNF | EP-01, EP-03, EP-05, EP-14, EP-15 | +| §20 | Inbound vs outbound identity | RF | EP-07 | +| §26 | Human-in-the-loop | RF | EP-14 | +| §27 | Observabilidade e auditoria | RF | EP-08 | +| §28 | Threat model | RNF | EP-15 | +| §31 | Blast-radius containment | RNF | EP-04, EP-01 | +| §33, §43 | Comparação arquitetural, validação externa | Contexto | (sem tarefa — referência) | +| §35 | Anti-patterns | Restrição | EP-01, EP-05, EP-07, EP-15 | +| §36 (RF-01..17) | Requisitos funcionais | RF | todos | +| §37, §38 | RNFs, política de falha | RNF | EP-05, EP-14, EP-15 | +| §39, §40 | Naming/namespaces, versionamento | Restrição | EP-02 | +| §42 | Zero Trust (NIST) | RNF | EP-01, EP-03, EP-05 | +| §45, §46 | Critérios de aceite de segurança, testes adversariais | RF | EP-15 | +| §47 | Roadmap de adoção (Fases 0–5) | Contexto | Mapeado em Marcos (8.7) | +| §48 | Decisões abertas | — | Ver ADRs (8.4) e Lacunas (8.3) | +| §49 | ADRs sugeridos | — | Ver 8.4 | +| §50 | Axiomas | Restrição | Transversal a todos os épicos | + +--- + +## 8.3 Lacunas e premissas + +| ID | Tipo | Descrição | Impacto | Pergunta ao autor do README | +|---|---|---|---|---| +| ~~G1~~ | ~~[LACUNA]~~ **[RESOLVIDO]** | O README cita `RI/` com "gateway MCP, PEP/PDP, OPA, Keycloak" na linha 10, mas o diretório não existia no repositório no início deste planejamento. **Confirmado pelo autor em 2026-09-11**: `RI/` é a pasta-alvo onde a implementação será construída do zero — a frase no README é aspiracional/prospectiva, não uma referência a algo já existente. | — (resolvido) | — | +| P1 | [PREMISSA CONFIRMADA] | OPA (PDP) e Keycloak (IdP/OAuth2-OIDC) são as tecnologias-alvo desta RI — citadas no próprio README e OSS/padrões abertos (seção 5 do superprompt). `RI/` deve ser criado integralmente por EP-00 em diante (ver EP-00-T01, atualizado para deixar explícito que cria a pasta do zero). | — | — | +| ~~G2~~ | ~~[LACUNA]~~ **[RESOLVIDO]** | O README não define stack de implementação. **Confirmado pelo autor em 2026-09-11**: adotar como referência concreta de camada Python/LangGraph/Langfuse o repositório de Christiano Hoshikawa, "Agent Platform OCI" (`github.com/hoshikawa2/agent_platform_oci`), analisado em detalhe (README + SPEC-002/003/004/007/012/018). | — (resolvido) | — | +| P2 | [DECISÃO CONFIRMADA] | Python 3.12+/YAML/LangGraph/Langfuse conforme padrão do superprompt, **com o padrão operacional concreto** do Agent Platform OCI (Hoshikawa) como referência de implementação: StateGraph com router/supervisor por backend + Agent Gateway/Global Supervisor roteando entre backends; checkpointing plugável (memory/sqlite/mongodb/produção); taxonomia de eventos Langfuse **IC/NOC/GRL** (negócio/operacional/guardrail); instrumentação híbrida (spans automáticos por nó + `_emit_ic/_emit_noc/_emit_grl` manuais, fail-open); FastMCP como padrão de servidor MCP. **Ressalva crítica adotada como ADR-022/023**: o modelo de autorização do MCP Gateway daquele projeto (allowlist por `agent_id` declarado no payload de entrada) é o anti-padrão exato que nosso ADR-001 proíbe e **não deve ser copiado** — apenas a engenharia de proxy/cache/contratos é reaproveitável, nunca a decisão de autorização. Ver ADR-018, ADR-022, ADR-023. | — | — | +| ~~G3~~ | ~~[LACUNA]~~ **[RESOLVIDO]** | §48, pergunta 3: engine do PDP em aberto. **Confirmado pelo autor em 2026-09-11**: decisão técnica definitiva, não apenas premissa a validar. | — (resolvido) | — | +| P3 | [DECISÃO CONFIRMADA] | OPA/Rego é o PDP da RI — por ser OSS (Apache-2.0) e aderente a "padrões abertos" (seção 5 do superprompt); não é uma suposição a revisitar. Ver ADR-025 (numeração corrigida em 2026-09-11 para não colidir com o ADR-010 do README, "Global Capability Registry Governance"). | — | — | +| ~~G4~~ | ~~[LACUNA]~~ **[RESOLVIDO]** | §48, pergunta 1: tecnologia de identidade do MCP Client em aberto. **Confirmado pelo autor em 2026-09-11**: Keycloak é decisão técnica definitiva, por ser OSS. | — (resolvido) | — | +| P4 | [DECISÃO CONFIRMADA] | OAuth 2.1 `client_credentials` via Keycloak é a identidade do MCP Client na RI (não apenas para dev/CI). mTLS/workload identity seguem como ponto de extensão documentado para produção, não implementado na RI. Ver ADR-019 (atualizado). | — | — | +| G5 | [LACUNA] | §48, pergunta 5: modelo de downstream/outbound identity em aberto. | Médio | — | +| P5 | [PREMISSA] | Token exchange (RFC 8693) via Keycloak quando o backend suportar OIDC; senão, service account por adapter, documentado por backend em `config/backends/*.yaml`. Ver ADR-020. | — | — | +| G6 | [LACUNA] | §48, pergunta 2: granularidade de client profiles em aberto. | Médio | — | +| P6 | [PREMISSA] | Granularidade = domínio + risk tier (ex.: `Sales.Read`=R1, `Sales.Write`=R2, `Finance.Payments`=R3), como já exemplificado em §7.1 e §17. | — | — | +| G7 | [LACUNA] | Profile Intelligence "genérica e substituível" (§6.10) não define algoritmo, fonte de dados real nem taxonomia. | Médio | Existe um dataset de referência (sintético) que a organização gostaria de usar na RI, ou a RI deve ficar 100% sintética? | +| P7 | [PREMISSA] | RI implementa um stub baseado em regras determinísticas sobre atributos sintéticos, atrás de uma interface substituível (`ProfileIntelligenceProvider`), sem clusterização real nem taxonomia proprietária — cumpre o contrato, não a "inteligência". | — | — | +| ~~G8~~ | ~~[LACUNA]~~ **[RESOLVIDO]** | README não menciona LangGraph/Langfuse explicitamente. **Resolvido em 2026-09-11** ao adotar o padrão operacional do Agent Platform OCI (Hoshikawa) — ver P2/ADR-022. | — (resolvido) | — | +| P8 | [DECISÃO CONFIRMADA] | LangGraph aplicado em duas camadas espelhando o padrão de referência: (a) EP-11 — StateGraph de jornada do Service Orchestrator, com nós fixos (resolve profile → resolve entitlement → filter offerings → decide NBA) e recálculo/degradação (§10.1) como aresta condicional; (b) EP-12-T03 — handoff entre agentes, inspirado no padrão de dois níveis do Hoshikawa (Global Supervisor entre backends + router/supervisor interno por backend), mas com a autorização de cada etapa **sempre** resolvida pelo nosso PEP/PDP, nunca por um campo autodeclarado no payload (essa é a lição central do ADR-023). Langfuse aplicado apenas onde há chamada real a LLM (EP-13), com instrumentação híbrida: spans automáticos por nó do grafo + eventos de negócio manuais (fail-open) na taxonomia IC/NOC/GRL (ver EP-08-T02 atualizado). Nenhum roteamento simples do Gateway/Fabric usa LangGraph. | — | — | +| G9 | [LACUNA] | §47 Fase 3 pede "multi-domain fabric" com federação; escopo de RI vs. produto não é definido. | Baixo | — | +| P9 | [PREMISSA] | RI demonstra 2 domínios (Sales, Finance) — suficiente para provar segmentação/blast-radius (§7, §31) sem federação completa. Federação fica fora de escopo (marcada `could`/stretch). | — | — | +| G10 | [LACUNA] | §48 pergunta 9 (multi-cloud/híbrido) e pergunta 10 (dev/test/prod) não são endereçáveis em uma RI local. | Baixo | — | +| P10 | [PREMISSA] | RI roda 100% local via `docker compose`; separação de ambientes é demonstrada via overlay de configuração (`config/env/{dev,ci}.yaml`), sem infraestrutura cloud real. | — | — | +| ~~G11~~ | ~~[LACUNA]~~ **[RESOLVIDO]** | §6.2 ("Agent Host/Orchestrator") e §6.12 ("Service Orchestrator") descrevem responsabilidades sobrepostas. **Resolvido em 2026-09-11** pelo posicionamento confirmado com o autor: §6.2 corresponde a uma plataforma multiagente *externa e consumidora* do bus (ex.: Agent Platform OCI/Hoshikawa — StateGraph + Global Supervisor), não a um componente que a RI precisa construir. | — (resolvido) | — | +| P11 | [DECISÃO CONFIRMADA] | §6.2 é o papel que uma plataforma de agentes externa (como o Agent Platform OCI) ocupa ao se conectar ao E-MCP-BUS como consumidora governada — ver ADR-024. Dentro da RI, apenas EP-11 (Service Orchestrator/NBA) e EP-12 (Agent Runtime/dispatch) são construídos, como a fronteira que essa plataforma externa atravessa até o MCP Client. Nenhum componente duplicado. | — | — | + +--- + +## 8.4 ADRs + +Formato: contexto → decisão → alternativas → consequências. ADR-001 a ADR-017 seguem os títulos sugeridos em §49 do README (decisões arquiteturais já implícitas nos princípios/axiomas); ADR-018+ são decisões de implementação da RI necessárias para fechar as lacunas da seção 8.3. + +| ADR | Contexto | Decisão | Alternativas consideradas | Consequências | +|---|---|---|---|---| +| ADR-001 | Quem define o teto de privilégio do agente. | MCP Client (identidade autenticada) é a fronteira de `MaximumEntitlement`, nunca o agente/prompt. | Autorização por role autodeclarado do agente. | Todo design de EP-01/EP-04 gira em torno da identidade do client, não do agente. | +| ADR-002 | Onde aplicar enforcement. | MCP Gateway é o único PEP; nunca delega decisão ALLOW/DENY ao LLM. | Enforcement distribuído em cada MCP Server. | Centraliza EP-05; servidores de domínio (EP-06) não reimplementam authz. | +| ADR-003 | `tools/list` filtrado é suficiente? | Não — `tools/call` deve ser reautorizado independentemente do discovery. | Confiar apenas em filtered discovery. | EP-05 implementa dois pontos de enforcement, não um. | +| ADR-004 | Contexto agêntico pode elevar privilégio? | Nunca — contexto só pode subtrair (`Context may subtract, must never add`). | Permitir elevação mediante "confiança" do modelo. | Testes de EP-15 (Teste 3/4) validam a invariante. | +| ADR-005 | Cache de `tools/list` entre clients. | `cacheScope=private`, chave = identidade+contexto+policy_version. | Cache compartilhado por performance. | EP-05-T03; teste dedicado de isolamento (EP-15). | +| ADR-006 | Token do agente pode ser repassado ao backend? | Não — proibido blind token passthrough; inbound ≠ outbound identity. | Passthrough direto do token do client. | EP-07 implementa exchange/service account por backend. | +| ADR-007 | Backend acessível diretamente pelo client? | Não — rede/identidade restringem acesso só ao Gateway/Fabric. | Expor Fabric publicamente com authz "best effort". | EP-05-T07, EP-15 (teste de bypass). | +| ADR-008 | Como segmentar clients. | Por domínio + risk tier (R0–R4), não um client global. | Um client "enterprise admin" único. | Define P6 (granularidade) e EP-04. | +| ADR-009 | Como classificar capabilities. | Risk tiers R0–R4 no Registry, controlando aprovação/observabilidade exigidas. | Sem classificação formal. | EP-02-T01 inclui `risk_tier` obrigatório no schema do manifesto. | +| ADR-010 | Governança do Global Capability Registry (título e número herdados de §49 do README — "Global Capability Registry Governance"). | O Registry é um control-plane asset (§6.6) com autoridade e ciclo de vida próprios, separados da visão de catálogo entregue a cada client. Na RI: armazenamento e API de leitura dedicados (EP-02-T03), consumidos por Gateway/Fabric/Offering Filter mas nunca editados por eles; lifecycle formal nominate→review→register→publish→observe→(change\|deprecate→retire) (§23) implementado como máquina de estados (EP-02-T04); toda alteração passa pelo publishing pipeline (EP-02-T02) — nenhum cadastro manual direto em produção (RF-11). Owner organizacional do Registry (quem aprova mudanças de schema/política do próprio control plane) permanece uma decisão de governança corporativa fora do escopo técnico da RI (§48, pergunta 4, segue aberta). | Deixar o Registry como uma tabela editável ad hoc por qualquer serviço; fundir a visão de controle (Registry) com a visão de execução (catálogo filtrado por client), o que violaria a separação control plane/data plane do §24. | EP-02 inteiro; reforça que EP-05 (Gateway) e EP-10 (Offering Filter) só leem o Registry, nunca escrevem nele. | +| ADR-011 | Ciclo de publicação de capabilities. | Pipeline obrigatório com gates de schema/risco/policy/health antes de entrar no Registry — sem cadastro manual em produção. | Registro direto via API administrativa sem pipeline. | EP-02-T02 implementa o gate; nenhuma capability "nasce" já publicada. | +| ADR-012 | Autoridade do cardápio máximo. | Entitlement Manager é a única autoridade de `MaximumEntitlement`; não faz ranking nem confia em atributos autodeclarados do LLM. | Fundir Entitlement Manager e Offering Filter num único componente. | Mantém EP-04 e EP-10 como serviços/módulos distintos com contratos próprios. | +| ADR-013 | Contrato de Profile Intelligence. | Interface genérica e substituível (`ProfileView` versionado, com `reasonCodes`), implementação plugável. | Acoplar a um vendor/taxonomia específica de segmentação. | EP-09 define apenas o contrato + stub de referência (ver P7). | +| ADR-014 | Perfil pode ampliar entitlement? | Nunca — `FilteredOfferings ⊆ MaximumEntitlement`, perfil só ordena/reduz. | Perfil de alto engajamento desbloqueando novas tools. | EP-10-T02 traz teste de propriedade validando a invariante para entradas aleatórias. | +| ADR-015 | NBA é decisão de autorização? | Não — é intenção de orquestração; Gateway/PDP reautorizam toda execução. | Orchestrator despachar direto para o backend sem passar pelo Gateway. | EP-11-T04 implementa o branch de DENY/recálculo do §10.1. | +| ADR-016 | Contrato de dispatch/handoff do Agent Runtime. | Objeto de dispatch versionado (`decisionId`, `expiresAt`, `reasonCodes`) que não substitui o token do MCP Client. | Runtime injetar credenciais próprias no agente. | EP-12-T01 consome exclusivamente o dispatch object + MCP Client já existente. | +| ADR-017 | Correlação ponta a ponta. | `profileVersion`+`entitlementVersion`+`decisionId`+`policyDecisionId`+`mcpRequestId` propagados via OTel/trace context em todos os componentes. | Correlação apenas por log textual sem padrão. | EP-08-T03 formaliza os campos; base para EP-15 (auditabilidade). | +| ADR-018 | Stack de implementação da RI (fecha G2). | Python 3.12+, `uv`, Pydantic v2, YAML declarativo com JSON Schema, SDK oficial `mcp`. | Node/TypeScript (SDK MCP também oficial); Go. | Define toda a estrutura de repositório (8.5). | +| ADR-019 | Identidade do MCP Client na RI (fecha G4 — **decisão confirmada pelo autor em 2026-09-11**). | Keycloak (OAuth 2.1 `client_credentials`) é a identidade do MCP Client na RI, por ser OSS. Decisão definitiva; mTLS/workload identity seguem documentados como extensão de produção não implementada na RI. | mTLS completo desde o início. | Reduz esforço de EP-01 sem violar §19 (propriedades desejáveis ainda são satisfeitas: curta duração, revogável, audience-bound). | +| ADR-020 | Downstream identity na RI (fecha G5/P5). | Token exchange (RFC 8693) quando backend suporta OIDC; senão service account isolado por adapter. | Reutilizar sempre o token do client (proibido por ADR-006). | EP-07; documentado por backend em manifesto. | +| ADR-021 | Escopo de LangGraph/Langfuse (fecha G8/P8). | LangGraph somente no Service Orchestrator (EP-11) e handoff do Agent Runtime (EP-12); Langfuse somente onde há chamada real a LLM (EP-13). Checkpointer: SQLite em dev. | Usar LangGraph também no Gateway/Fabric para "consistência de stack". | Evita over-engineering vetado pela seção 6 do superprompt; Gateway/Fabric continuam Python puro. | +| ADR-022 | Referência concreta de implementação Python/LangGraph/Langfuse (fecha G2 — **decisão confirmada pelo autor em 2026-09-11**). | Adotar como referência operacional o "Agent Platform OCI" de Christiano Hoshikawa (`github.com/hoshikawa2/agent_platform_oci`): StateGraph com router/supervisor por backend + camada superior de roteamento entre backends (nosso EP-11/EP-12); checkpointing plugável (memory/sqlite/mongodb/produção — nós adotamos SQLite em dev, ver ADR-021); taxonomia de observabilidade **IC/NOC/GRL** via Langfuse (evento de negócio/operacional/guardrail), com instrumentação híbrida (span automático por nó + emissão manual fail-open); `identity.yaml`/`BusinessContext` para normalizar identidade de negócio entre canais; FastMCP como padrão de servidor MCP. Análise completa registrada em `docs/research/hoshikawa-agent-platform-oci.md`. | Continuar com o padrão genérico da seção 6 do superprompt sem referência concreta de terceiros (mais lento, mais decisões ad hoc); adotar um framework de agentes de mercado (CrewAI, AutoGen) em vez de LangGraph puro. | Muda a forma (não a segurança) de EP-08, EP-11, EP-12, EP-13 — ver ADR-023 para o limite explícito do que NÃO é adotado. | +| ADR-023 | Autorização de tools NUNCA vem de um campo declarado no payload/conversa (achado crítico da análise do Hoshikawa, reforça ADR-001/002/003). | O MCP Gateway do Agent Platform OCI autoriza tools por um allowlist estático (`tools.yaml`/`authorization.agents..allowed_tools`) avaliado sobre `agent_id`, campo que chega como **dado do payload de entrada da conversa**, não como claim de uma identidade autenticada verificada independentemente — a própria SPEC-004 deles admite que isso não substitui autenticação/autorização real. Nossa RI reaproveita a *forma* de proxy desse MCP Gateway (contratos `ToolInvocation`/`ToolResult`, cache por tool idempotente, catálogo `/v1/tools`) **exclusivamente na camada de Fabric (EP-06)/UX conversacional**, mas a decisão ALLOW/DENY continua 100% no nosso PEP (EP-05) consultando o PDP (EP-03) com a identidade autenticada do MCP Client (EP-01) — nunca com `agent_id`/`tenant_id` autodeclarado no payload. | Reaproveitar também o bloco `authorization` deles como atalho de implementação. | EP-15-T03 ganha um cenário adversarial explícito de "payload-declared agent_id spoofing" (variação do Teste 4, §45) para provar que esse vetor específico está fechado na nossa RI. | +| ADR-024 | Posicionamento do E-MCP-BUS em relação a plataformas de orquestração multiagente externas, como o Agent Platform OCI (**decisão confirmada e ampliada pelo autor em 2026-09-11** — resolve G11). | O E-MCP-BUS **não compete** com plataformas de orquestração como o Agent Platform OCI — mas também **não se limita a coexistir sob elas**. Princípio de design explícito: **modularidade + compatibilidade = escolha**. Nosso Service Orchestrator (EP-11) não é um passa-through fino: ele calcula NBA **e NBO** (Next Best Action/Next Best Offer) com um modelo de decisão próprio (regras + guardrails +, opcionalmente, um modelo de IA de ranking/recomendação — ver EP-11-T03) sobre o universo de `FilteredOfferings` que o MCP Fabric expõe (serviços, ofertas, ações, campanhas — não só "tools" técnicas). Isso habilita **dois modos de integração igualmente válidos, escolhidos pelo adotante conforme a topologia desejada**: **(A) Governança-somente** — uma plataforma de orquestração externa (ex.: Agent Platform OCI, com seu próprio Global Supervisor e StateGraph decidindo NBA) conecta-se ao E-MCP-BUS apenas para acesso governado ao Fabric; nosso Service Orchestrator não participa, ou participa apenas como fonte de `FilteredOfferings` (como já descrito no restante deste ADR e em EP-13-T05). **(B) Substituição completa** — nosso próprio Service Orchestrator (EP-11) assume integralmente o papel de "cérebro" de decisão (NBA/NBO), dispensando o Global Supervisor/StateGraph de decisão da plataforma externa; o Agent Runtime (EP-12) e os channel adapters da plataforma externa (ex.: os backends de agente do Hoshikawa) tornam-se meros executores do dispatch que nosso Orchestrator produz. Em qualquer um dos dois modos, o núcleo de segurança (PEP/PDP, EP-05/EP-03) nunca muda de lugar — apenas quem calcula a NBA/NBO muda. Em ambos os modos, cada backend/agente de domínio continua registrado como um **MCP Client distinto** (EP-01), vinculado a um client profile/entitlement próprio por domínio+risk tier (EP-04, P6), e o "MCP Gateway" interno de qualquer plataforma consumidora (ADR-023) é sempre substituído pelo nosso Gateway/PEP como único ponto de autorização. Isso é a "elevação a Enterprise": catálogo/entitlement/auditoria governados centralmente, com a decisão de NBA/NBO podendo vir de dentro (modo B) ou de fora (modo A) do bus, por escolha modular do adotante. | Forçar um único modo de integração (ex.: só "governança-somente"), o que subutilizaria o Service Orchestrator e obrigaria sempre uma plataforma externa de decisão; tratar o Agent Platform OCI como um substituto do E-MCP-BUS (incorreto — ele não resolve entitlement); reimplementar do zero a engenharia de orquestração já validada em ADR-022 mesmo quando o modo B é escolhido (a forma StateGraph/checkpointing/handoff continua reaproveitável dentro do nosso EP-11/EP-12). | Reformula o objetivo de EP-11 (Service Orchestrator explicitamente descrito como calculador de NBA **e NBO** sobre serviços/ofertas/ações do Fabric, com modelo de decisão pluggable) e de EP-13 (demonstra os dois modos — ver EP-13-T05 para o modo A e EP-13-T06, novo, para o modo B). Reforça que EP-06/EP-05 tratam qualquer "MCP Gateway" de uma plataforma consumidora como não-confiável para autorização, nos dois modos. | +| ADR-025 | Engine do PDP (fecha G3 — **decisão confirmada pelo autor em 2026-09-11**; renumerado de ADR-010 para ADR-025 em 2026-09-11 para não colidir com o ADR-010 do README, "Global Capability Registry Governance"). | OPA/Rego, self-hosted, OSS (Apache-2.0). Decisão definitiva, não sujeita a revisão. **Verificado em 2026-09-11 via busca web**: OPA está em v1.0+, com sintaxe Rego v1 como padrão (breaking change vs. v0) — EP-03 deve escrever policies em sintaxe Rego v1 desde o início. | Cedar (AWS, cita-se em §43.2); engine própria em Python. | EP-03 inteiro fica acoplado à API REST do OPA; policies versionadas como código em Rego v1. | + +--- + +## 8.5 Estrutura proposta do repositório + +```text +Enterprise-MCP-Service-Bus/ +├── README.md # arquitetura de referência (existente) +├── docs/ +│ ├── RI-PLANNING.md # este documento +│ └── adr/ # ADR-001..021 em arquivos individuais (EP-16-T03) +└── RI/ # implementação executável (não existe ainda — ver G1) + ├── pyproject.toml # PEP 621, uv, ruff, mypy/pyright, pytest + ├── Makefile # up/down/logs/test/lint/validate-schemas + ├── docker-compose.yml # stack completa (perfis: core, llm-observability) + ├── README.md # quickstart da RI + ├── config/ # TUDO que é configurável, em YAML + │ ├── env/ # overlays dev/ci + │ ├── clients/ # client profiles (§41) — EP-04 + │ ├── capabilities/ # manifests de capability (§6.8) — EP-02 + │ ├── policies/ # Rego (entitlement + transaction) — EP-03 + │ ├── backends/ # config de downstream identity por adapter — EP-07 + │ └── orchestrator/ # definição declarativa de jornadas/regras — EP-11 + ├── schemas/ # 1 JSON Schema por tipo de YAML/JSON acima + ├── src/emcp_bus/ + │ ├── common/ # config loader, Pydantic base models, OTel setup + │ ├── identity/ # EP-01 + │ ├── registry/ # EP-02 (control plane) + │ ├── pdp/ # EP-03 (cliente OPA) + │ ├── entitlement/ # EP-04 + │ ├── gateway/ # EP-05 (PEP, data plane) + │ ├── fabric/ # EP-06 (router, mediation) + │ ├── downstream/ # EP-07 (outbound identity) + │ ├── audit/ # EP-08 + │ ├── profile_intelligence/ # EP-09 + │ ├── offering_filter/ # EP-10 + │ ├── orchestrator/ # EP-11 (grafos LangGraph) + │ └── agent_runtime/ # EP-12 + ├── services/ + │ └── example_mcp_servers/ + │ ├── sales_domain/ # EP-06 + │ └── finance_domain/ # EP-06 + ├── agents/ + │ └── example_agent/ # EP-13 (Langfuse) + ├── deploy/ + │ ├── keycloak/ # realm export — EP-01 + │ ├── opa/ # bundles Rego — EP-03 + │ ├── otel/ # collector config — EP-08 + │ └── langfuse/ # compose overlay OSS — EP-13 + └── tests/ + ├── unit/ + ├── contract/ # EP-15-T01 + ├── e2e/ # EP-15-T02 + └── adversarial/ # EP-15-T03/T04 +``` + +--- + +## 8.6 Backlog estruturado + +```yaml +backlog: + - epico_id: EP-00 + titulo: "Bootstrap e tooling da RI" + objetivo: "Estabelecer scaffolding Python/YAML, CI e docker-compose base necessários a todos os demais épicos." + secoes_readme: ["Seção 5 do superprompt (stack)"] + tarefas: + - id: EP-00-T01 + titulo: "Scaffold do projeto Python" + descricao: "Criar o diretório RI/ do zero (não existe no repositório) com pyproject.toml (PEP 621, uv), ruff, mypy/pyright estrito, pytest+pytest-asyncio, pre-commit." + componente: "tooling" + tipo: infra + prioridade: must + estimativa: P + depende_de: [] + entregaveis: ["RI/pyproject.toml", "RI/.pre-commit-config.yaml", "RI/Makefile"] + criterios_aceite: ["Dado o repo clonado, quando se roda `make lint test`, então lint e um teste smoke passam sem erro."] + padroes_abertos: ["SemVer", "Conventional Commits"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: ["Escolha prematura de versões — mitigado por EP-16-T04"] + rastreabilidade: ["ADR-018"] + + - id: EP-00-T02 + titulo: "Framework de configuração declarativa" + descricao: "Loader YAML->Pydantic v2 com interpolação de variáveis de ambiente, overlay por ambiente (config/env/*.yaml) e exportação automática de JSON Schema por modelo." + componente: "common/config" + tipo: infra + prioridade: must + estimativa: M + depende_de: ["EP-00-T01"] + entregaveis: ["src/emcp_bus/common/config.py", "schemas/README.md (convenção de geração)"] + criterios_aceite: ["Dado um YAML inválido contra seu schema, quando carregado, então falha com erro legível apontando o campo.", "Segredos nunca são aceitos como valor literal em YAML — apenas via ${ENV_VAR}."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["ADR-018"] + + - id: EP-00-T03 + titulo: "Pipeline de CI" + descricao: "CI (lint, type-check, testes, validação de todo YAML em config/ contra seu JSON Schema) executando a cada PR." + componente: "ci" + tipo: infra + prioridade: must + estimativa: P + depende_de: ["EP-00-T01", "EP-00-T02"] + entregaveis: [".github/workflows/ci.yml (ou equivalente)"] + criterios_aceite: ["Quando um YAML de config/ viola seu schema, então o CI falha antes de qualquer teste de integração."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: [] + + - id: EP-00-T04 + titulo: "docker-compose base (walking skeleton)" + descricao: "docker-compose.yml mínimo capaz de subir apenas o Gateway e um MCP Server de exemplo, com perfis (`core`, `llm-observability`) para adicionar serviços pesados depois." + componente: "deploy" + tipo: infra + prioridade: must + estimativa: P + depende_de: ["EP-00-T01"] + entregaveis: ["RI/docker-compose.yml"] + criterios_aceite: ["Quando `docker compose --profile core up`, então gateway e example-server sobem e respondem health check."] + padroes_abertos: ["OCI"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: [] + + - id: EP-00-T05 + titulo: "Gestão de segredos" + descricao: "Convenção e mecanismo (.env + docker secrets, ou SOPS quando fizer sentido) para segredos: client secrets, tokens de backend, credenciais de banco — nunca em YAML versionado." + componente: "common/config" + tipo: seguranca + prioridade: must + estimativa: P + depende_de: ["EP-00-T02"] + entregaveis: ["RI/.env.example", "docs de convenção em RI/README.md#secrets"] + criterios_aceite: ["Um `git grep` por padrões de segredo nos arquivos versionados não encontra nenhum valor real.", "Todo segredo referenciado em config YAML usa ${ENV_VAR}."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: ["Vazamento acidental de secret em commit — mitigado por pre-commit hook de detecção"] + rastreabilidade: ["RNF-Segurança (§37)"] + + - epico_id: EP-01 + titulo: "Identidade do MCP Client e autenticação" + objetivo: "Garantir que a identidade usada para autorização venha de um mecanismo autenticado, nunca de campos autodeclarados (§5, Observação crítica sobre clientInfo)." + secoes_readme: ["§5", "§6.3", "§18", "§19", "§20", "§35.5", "§35.6"] + tarefas: + - id: EP-01-T01 + titulo: "Keycloak como Identity Provider da RI" + descricao: "Serviço Keycloak no compose com realm de exemplo (script de import) representando ambiente enterprise." + componente: "identity" + tipo: infra + prioridade: must + estimativa: P + depende_de: ["EP-00-T04"] + entregaveis: ["deploy/keycloak/realm-export.json", "compose service `keycloak`"] + criterios_aceite: ["Dado o compose no ar, quando se acessa o endpoint OIDC discovery do realm, então retorna metadata válida."] + padroes_abertos: ["OAuth 2.1", "OpenID Connect"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: ["[NÃO-OSS]: nenhum — Keycloak é Apache-2.0"] + rastreabilidade: ["RF-02", "ADR-019"] + + - id: EP-01-T02 + titulo: "Schema de registro de MCP Client" + descricao: "YAML `config/clients/*.yaml` + Pydantic + JSON Schema mapeando client_id ↔ client Keycloak ↔ nome do client profile (ex.: Sales.Read)." + componente: "identity" + tipo: feature + prioridade: must + estimativa: P + depende_de: ["EP-00-T02"] + entregaveis: ["schemas/client-registration.schema.json", "src/emcp_bus/identity/models.py"] + criterios_aceite: ["Dado um client_id não registrado, quando consultado, então retorna 'not found' determinístico."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-01"] + + - id: EP-01-T03 + titulo: "Middleware de autenticação do Gateway" + descricao: "Validação de access token OAuth2/OIDC (assinatura via JWKS do Keycloak, audience, expiração) antes de qualquer processamento MCP; identidade nunca lida de clientInfo autodeclarado." + componente: "identity" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-01-T01", "EP-01-T02"] + entregaveis: ["src/emcp_bus/identity/authn.py", "tests/unit/test_authn.py"] + criterios_aceite: ["Dado um token com audience incorreta, quando usado em qualquer request, então é rejeitado (Teste 8, §45).", "Dado um `clientInfo.name` autodeclarado divergente do token, quando processado, então a identidade usada é a do token, não a declarada."] + padroes_abertos: ["OAuth 2.1", "OpenID Connect"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-02", "Axiom 2", "ADR-001"] + + - id: EP-01-T04 + titulo: "Spike: workload identity / mTLS (extensão de produção)" + descricao: "Documentar (sem implementar) como mTLS/cloud workload identity substituiria client_credentials em produção, como ponto de extensão do middleware EP-01-T03." + componente: "identity" + tipo: spike + prioridade: could + estimativa: P + depende_de: ["EP-01-T03"] + entregaveis: ["docs/adr/ADR-019-identity-extension.md"] + criterios_aceite: ["Documento revisado explicita a interface de extensão sem exigir mudança de contrato no restante do Gateway."] + padroes_abertos: ["mTLS"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§19", "ADR-019"] + + - id: EP-01-T05 + titulo: "Regra de shared client" + descricao: "Validação (lint de config) que impede dois agentes distintos referenciarem o mesmo MCP Client quando seus client profiles nominais diferem (anti-pattern §18/§35)." + componente: "identity" + tipo: seguranca + prioridade: should + estimativa: P + depende_de: ["EP-01-T02"] + entregaveis: ["src/emcp_bus/identity/shared_client_lint.py"] + criterios_aceite: ["Dado dois agentes configurados apontando ao mesmo client_id com client profiles diferentes, quando o lint roda, então falha com mensagem explicando o anti-pattern."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§18", "§35.4"] + + - epico_id: EP-02 + titulo: "Global Capability Registry e publishing pipeline" + objetivo: "Permitir que service owners publiquem capabilities por pipeline governado, alimentando o Registry como control-plane asset (§6.6, §6.8, §23, §24)." + secoes_readme: ["§6.6", "§6.8", "§17", "§23", "§24", "§39", "§40"] + tarefas: + - id: EP-02-T01 + titulo: "Schema de capability manifest" + descricao: "YAML de manifesto (baseado no exemplo §6.8) + Pydantic + JSON Schema: nome canônico `.`, versão, owner, risk_tier, data_classification, side_effects, idempotent, requiredEntitlements, backend, lifecycle." + componente: "registry" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-00-T02"] + entregaveis: ["schemas/capability-manifest.schema.json", "config/capabilities/README.md (convenção de naming §39)"] + criterios_aceite: ["Um manifesto sem `risk_tier` ou `owner` é rejeitado pelo schema."] + padroes_abertos: ["JSON Schema", "SemVer"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-06", "RF-11", "ADR-009"] + + - id: EP-02-T02 + titulo: "Publishing pipeline (gate de validação)" + descricao: "CLI/serviço que valida manifesto (schema, referência de policy existente em EP-03, health check do backend declarado) antes de permitir publicação — nenhum cadastro manual direto em produção." + componente: "registry" + tipo: feature + prioridade: must + estimativa: G + depende_de: ["EP-02-T01"] + entregaveis: ["src/emcp_bus/registry/pipeline.py", "tests/contract/test_publishing_gate.py"] + criterios_aceite: ["Dado um manifesto com política inexistente referenciada, quando submetido ao pipeline, então é rejeitado com motivo.", "Dado um manifesto válido, quando aprovado, então aparece no Registry com status `active`."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-11", "ADR-011"] + + - id: EP-02-T03 + titulo: "Armazenamento e API de leitura do Registry" + descricao: "Persistência (SQLite dev / Postgres compartilhado) do catálogo administrativo completo + API de leitura consumida por Gateway, Fabric e Offering Filter — distinta da visão por client." + componente: "registry" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-00-T04", "EP-02-T01"] + entregaveis: ["src/emcp_bus/registry/store.py", "src/emcp_bus/registry/api.py"] + criterios_aceite: ["Dado o Registry populado, quando consultado por domínio, então retorna somente capabilities `active` daquele domínio."] + padroes_abertos: ["OpenAPI 3.1"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-06"] + + - id: EP-02-T04 + titulo: "Máquina de estados de lifecycle" + descricao: "Implementar nominate→review→register→publish→observe→(change|deprecate→retire) do §23 como status controlado, com transições auditadas (EP-08)." + componente: "registry" + tipo: feature + prioridade: should + estimativa: M + depende_de: ["EP-02-T03"] + entregaveis: ["src/emcp_bus/registry/lifecycle.py"] + criterios_aceite: ["Uma capability não pode ir de `register` direto para `publish` sem passar por `review` aprovado."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§23"] + + - id: EP-02-T05 + titulo: "Manifests de exemplo (Sales, Finance)" + descricao: "Seed data cobrindo tiers R1–R3: sales.customer.get/search (R1), sales.quote.create/update (R2), finance.payment.execute (R3, exige aprovação)." + componente: "registry" + tipo: feature + prioridade: must + estimativa: P + depende_de: ["EP-02-T02", "EP-02-T03"] + entregaveis: ["config/capabilities/sales/*.yaml", "config/capabilities/finance/*.yaml"] + criterios_aceite: ["Todos os manifestos de seed passam pelo pipeline (EP-02-T02) sem intervenção manual."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§7.1", "§17"] + + - epico_id: EP-03 + titulo: "PDP / Policy Engine (OPA)" + objetivo: "Avaliar entitlement policy e transaction policy de forma determinística e auditável, sem nunca conceder tool ausente do client entitlement (§6.5, §25)." + secoes_readme: ["§6.5", "§16", "§25", "§42", "§48(3)"] + tarefas: + - id: EP-03-T01 + titulo: "Integração com OPA" + descricao: "Serviço OPA no compose + client Python (REST) para avaliação de policies; contrato de decisão ALLOW/DENY/REQUIRE_APPROVAL." + componente: "pdp" + tipo: infra + prioridade: must + estimativa: M + depende_de: ["EP-00-T04"] + entregaveis: ["deploy/opa/", "src/emcp_bus/pdp/client.py"] + criterios_aceite: ["Dado o OPA no ar, quando uma policy de teste é avaliada via client Python, então o resultado corresponde ao esperado pela suíte `opa test`."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: ["[NÃO-OSS]: nenhum — OPA é Apache-2.0"] + rastreabilidade: ["ADR-025"] + + - id: EP-03-T02 + titulo: "Entitlement policy em Rego" + descricao: "Policy `permit if requested_tool in entitlement(client)` (§25 Passo 1), com dados de entitlement carregados do client profile (EP-04-T01)." + componente: "pdp" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-03-T01", "EP-04-T01"] + entregaveis: ["config/policies/entitlement.rego", "tests/unit/policies/entitlement_test.rego"] + criterios_aceite: ["Dado client=Sales.Read e tool=finance.createPayment, quando avaliado, então DENY (Teste 1/2, §45)."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-05", "Axiom 4", "ADR-025"] + + - id: EP-03-T03 + titulo: "Transaction policy em Rego" + descricao: "Policy de argumentos/recursos (§25 Passo 2, exemplo quote.create: amount ≤ limit, region, classification)." + componente: "pdp" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-03-T01"] + entregaveis: ["config/policies/transaction.rego", "tests/unit/policies/transaction_test.rego"] + criterios_aceite: ["Dado quote.create com amount acima do limite do client, quando avaliado, então DENY mesmo que a tool esteja no entitlement."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-05", "§6.5.B"] + + - id: EP-03-T04 + titulo: "Contrato de decisão do PDP" + descricao: "Modelo Pydantic unificado de decisão (ALLOW/DENY/REQUIRE_APPROVAL + policy_version + reason_code) consumido pelo Gateway." + componente: "pdp" + tipo: feature + prioridade: must + estimativa: P + depende_de: ["EP-03-T02", "EP-03-T03"] + entregaveis: ["src/emcp_bus/pdp/models.py"] + criterios_aceite: ["Toda resposta do PDP inclui policy_version e reason_code, nunca apenas um booleano."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§27 (campos de auditoria)"] + + - epico_id: EP-04 + titulo: "Entitlement Manager" + objetivo: "Resolver e versionar o MaximumEntitlement por client, com provenance e invalidação em revogação (§6.9, §41)." + secoes_readme: ["§6.9", "§7.1", "§7.2", "§17", "§31", "§41"] + tarefas: + - id: EP-04-T01 + titulo: "Schema de client profile" + descricao: "YAML `config/clients profile` (§41 exemplo) + Pydantic + JSON Schema: name, owner, risk_ceiling, allowed_tools, restrictions, credential_policy, audit level." + componente: "entitlement" + tipo: feature + prioridade: must + estimativa: P + depende_de: ["EP-00-T02"] + entregaveis: ["schemas/client-profile.schema.json", "config/clients/profiles/*.yaml (Sales.Read/Write/Approve, Finance.*)"] + criterios_aceite: ["Um client profile sem `risk_ceiling` é rejeitado pelo schema."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-01", "ADR-008", "P6"] + + - id: EP-04-T02 + titulo: "Serviço resolveMaximumEntitlement" + descricao: "Combina client profile + decisão de entitlement do PDP, produz versão cacheável (`entitlementVersion`) com provenance (qual regra incluiu/restringiu cada capability)." + componente: "entitlement" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-04-T01", "EP-03-T02", "EP-03-T04"] + entregaveis: ["src/emcp_bus/entitlement/manager.py"] + criterios_aceite: ["Dado client=Sales.Write, quando resolvido, então o conjunto retornado é exatamente {customer.search, customer.get, order.get, inventory.check, quote.create, quote.update} (§7.1)."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-01", "RF-12", "Axiom 2"] + + - id: EP-04-T03 + titulo: "Revogação e invalidação de cache" + descricao: "Endpoint/evento de revogação de client ou policy que invalida entitlement cacheado e emite `policy_changed`/`client_revoked` (consumido por EP-08 e EP-05)." + componente: "entitlement" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-04-T02"] + entregaveis: ["src/emcp_bus/entitlement/revocation.py"] + criterios_aceite: ["Dado um client revogado, quando uma nova tools/call chega, então é negada mesmo com cache de tools/list ainda não expirado (Teste 6, §45)."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-08"] + + - id: EP-04-T04 + titulo: "Contrato de versão de entitlement" + descricao: "Expor `entitlementVersion` de forma consistente para PDP, Gateway e Orchestrator, usado na correlação ponta a ponta (RF-17)." + componente: "entitlement" + tipo: feature + prioridade: should + estimativa: P + depende_de: ["EP-04-T02"] + entregaveis: ["src/emcp_bus/entitlement/models.py (EntitlementVersion)"] + criterios_aceite: ["Toda resposta de resolveMaximumEntitlement inclui entitlementVersion não-nulo."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-17"] + + - epico_id: EP-05 + titulo: "MCP Gateway / PEP" + objetivo: "Implementar o PEP: discovery filtrado, execution enforcement, caching privado, anti-bypass e fail-closed (§6.4, §9, §10, §12, §13, §21, §22, §38)." + secoes_readme: ["§6.4", "§9", "§10", "§11", "§12", "§13", "§21", "§22", "§35.3", "§38", "§42"] + tarefas: + - id: EP-05-T01 + titulo: "Gateway skeleton (walking skeleton)" + descricao: "Servidor MCP (SDK oficial `mcp`, transporte Streamable HTTP) que recebe tools/list e tools/call e os encaminha, sem enforcement ainda, a um roteamento estático de um único MCP Server de exemplo." + componente: "gateway" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-00-T04", "EP-06-T02"] + entregaveis: ["src/emcp_bus/gateway/server.py"] + criterios_aceite: ["Dado o compose no ar (perfil core), quando um cliente MCP chama tools/list e tools/call, então recebe resposta do example server via Gateway, com trace_id visível no log (EP-08-T01)."] + padroes_abertos: ["MCP", "JSON-RPC 2.0"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "Roteamento/proxy simples — pertence ao núcleo do bus em Python puro, não a um grafo de agente." + riscos: [] + rastreabilidade: ["Marco M0"] + + - id: EP-05-T02 + titulo: "Filtered tools/list" + descricao: "Implementar sequência do §9: autenticar client (EP-01-T03), resolver MaximumEntitlement (EP-04-T02), obter catálogo do Registry (EP-02-T03), retornar interseção." + componente: "gateway" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-05-T01", "EP-01-T03", "EP-04-T02"] + entregaveis: ["src/emcp_bus/gateway/discovery.py"] + criterios_aceite: ["Dado client=Sales.Read, quando tools/list é chamado, então finance.createPayment NUNCA aparece na resposta (Teste 1, §45)."] + padroes_abertos: ["MCP"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-03", "P4", "P5 (invariantes §9)"] + + - id: EP-05-T03 + titulo: "Cache privado de tools/list" + descricao: "Cache com chave = client_identity+authorization_context+policy_version+params, cacheScope=private, TTL curto configurável, invalidado em revogação (EP-04-T03). Verificado em 2026-09-11: SEP-2549 (ttlMs/cacheScope, GA na revisão 2026-07-28) já é suportado nativamente pelo SDK Python `mcp` v2 via `cache_hints=` no servidor — usar o primitivo do SDK em vez de reimplementar cache HTTP manual." + componente: "gateway" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-05-T02"] + entregaveis: ["src/emcp_bus/gateway/cache.py"] + criterios_aceite: ["Dado catálogo cacheado de Sales.Read, quando Finance.Payments faz a mesma requisição, então nunca recebe o cache de Sales.Read (Teste 5, §45)."] + padroes_abertos: ["MCP 2026-07-28 (SEP-2549) — confirmado via busca web em 2026-09-11"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§13", "Threat: Cache leakage (§28)"] + + - id: EP-05-T04 + titulo: "Enforcement de tools/call" + descricao: "Reautorização independente do discovery (§10): client capability check → transaction policy → REQUIRE_APPROVAL/ALLOW/DENY determinístico." + componente: "gateway" + tipo: seguranca + prioridade: must + estimativa: G + depende_de: ["EP-05-T01", "EP-03-T04"] + entregaveis: ["src/emcp_bus/gateway/enforcement.py"] + criterios_aceite: ["Dado client=Sales.Read chamando diretamente tools/call(finance.createPayment) sem nunca ter visto a tool em tools/list, quando executado, então DENY determinístico (Teste 2, §45)."] + padroes_abertos: ["MCP"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-04", "Axiom 5", "ADR-003"] + + - id: EP-05-T05 + titulo: "Rate limiting e quota por client" + descricao: "Limites configuráveis por client profile, aplicados no Gateway antes do roteamento ao Fabric." + componente: "gateway" + tipo: seguranca + prioridade: should + estimativa: M + depende_de: ["EP-05-T01"] + entregaveis: ["src/emcp_bus/gateway/rate_limit.py"] + criterios_aceite: ["Dado um client excedendo seu limite configurado, quando chama tools/call, então recebe 429/DENY determinístico sem consultar backend."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RNF-Disponibilidade (§37)", "Threat: bulk exfiltration (§28)"] + + - id: EP-05-T06 + titulo: "Fail-closed em indisponibilidade" + descricao: "Implementar tabela do §38: PDP indisponível → fail closed; Registry indisponível → cache válido dentro do contexto; approval service indisponível → nega high-risk." + componente: "gateway" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-05-T04"] + entregaveis: ["src/emcp_bus/gateway/failure_policy.py"] + criterios_aceite: ["Dado o OPA fora do ar, quando qualquer tools/call chega, então a resposta é DENY, nunca ALLOW por omissão."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RNF-Disponibilidade (§37)", "§38"] + + - id: EP-05-T07 + titulo: "Prevenção de bypass do PEP" + descricao: "Isolamento de rede no compose (Fabric/backends só aceitam origem do Gateway) + detecção de chamadas que não atravessaram o PEP em telemetria." + componente: "gateway" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-06-T01"] + entregaveis: ["docker-compose network segmentation", "src/emcp_bus/audit/bypass_detection.py"] + criterios_aceite: ["Dado acesso direto tentado ao Fabric/backend a partir de fora da rede do Gateway, quando tentado, então falha (Teste 7, §45)."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§21", "ADR-007", "Axiom 7"] + + - id: EP-05-T08 + titulo: "Consistência header/body (revisão MCP 2026-07-28)" + descricao: "Validar consistência entre `Mcp-Method`/`Mcp-Name` e o corpo JSON-RPC antes de decisão de autorização, produzindo representação canônica única (§22). Verificado em 2026-09-11: a revisão 2026-07-28 é real (GA, SEP-2243) e o SDK Python `mcp` v2 já expõe esses headers nativamente no transporte Streamable HTTP — a tarefa é validar/canonicalizar, não implementar o parsing do zero." + componente: "gateway" + tipo: seguranca + prioridade: should + estimativa: M + depende_de: ["EP-05-T04"] + entregaveis: ["src/emcp_bus/gateway/canonical_request.py"] + criterios_aceite: ["Dado header Mcp-Name divergente do `params.name` do body, quando recebido, então a requisição é rejeitada por ambiguidade, nunca processada com um dos dois valores 'na confiança'."] + padroes_abertos: ["MCP 2026-07-28 (SEP-2243) — confirmado via busca web em 2026-09-11"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§22"] + + - epico_id: EP-06 + titulo: "Enterprise MCP Fabric e servidores de domínio de exemplo" + objetivo: "Resolver capability em implementação concreta via roteamento por domínio, com servidores MCP de exemplo demonstrando risk tiers (§6.7, §32, §34)." + secoes_readme: ["§6.7", "§17", "§32", "§34"] + tarefas: + - id: EP-06-T01 + titulo: "Fabric core (capability router)" + descricao: "Router que resolve `.` para o adapter/backend correto usando o Registry (EP-02-T03), com retries/timeouts e telemetria." + componente: "fabric" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-02-T03", "EP-06-T02", "EP-06-T03"] + entregaveis: ["src/emcp_bus/fabric/router.py"] + criterios_aceite: ["Dado uma capability publicada em Sales, quando roteada, então chega ao MCP Server de Sales, não ao de Finance."] + padroes_abertos: ["MCP"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "Roteamento determinístico simples — não é fluxo de agente." + riscos: [] + rastreabilidade: ["RF component (§6.7)"] + + - id: EP-06-T02 + titulo: "MCP Server de exemplo: domínio Sales" + descricao: "Servidor MCP (SDK oficial, padrão FastMCP — `from mcp.server.fastmcp import FastMCP`, `@mcp.tool()`, transporte `streamable-http` — confirmado como padrão de referência em ADR-022) com customer.search/get (R1), order.get (R1), inventory.check (R1), quote.create/update (R2) contra dados mockados. O servidor permanece 'burro': nunca decide autorização (ADR-023) — toda decisão vem do Gateway/PDP antes da chamada chegar aqui." + componente: "fabric" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-00-T01"] + entregaveis: ["services/example_mcp_servers/sales_domain/"] + criterios_aceite: ["Cada tool expõe input/output schema conforme manifesto correspondente (EP-02-T05)."] + padroes_abertos: ["MCP"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§7.1", "Marco M0"] + + - id: EP-06-T03 + titulo: "MCP Server de exemplo: domínio Finance" + descricao: "Servidor MCP com invoice.get (R1), payment.create (R2), payment.execute (R3, sujeito a aprovação — EP-14) contra dados mockados." + componente: "fabric" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-00-T01"] + entregaveis: ["services/example_mcp_servers/finance_domain/"] + criterios_aceite: ["payment.execute >= 10k dispara REQUIRE_APPROVAL na policy correspondente (§26)."] + padroes_abertos: ["MCP"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§17", "§26"] + + - id: EP-06-T04 + titulo: "Adapter REST genérico" + descricao: "Padrão de adapter reutilizável que traduz uma capability MCP para uma chamada REST (documentada via OpenAPI) de backend, usado pelos dois domínios." + componente: "fabric" + tipo: feature + prioridade: should + estimativa: M + depende_de: ["EP-06-T01"] + entregaveis: ["src/emcp_bus/fabric/adapters/rest_adapter.py"] + criterios_aceite: ["Dado um novo backend descrito por OpenAPI, quando configurado via YAML, então nenhum código Python novo é necessário para expô-lo (apenas config)."] + padroes_abertos: ["OpenAPI 3.1"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§6.7"] + + - epico_id: EP-07 + titulo: "Downstream identity (inbound vs outbound)" + objetivo: "Evitar blind token passthrough — identidade de entrada no Fabric não é reutilizada automaticamente no backend (§20)." + secoes_readme: ["§20", "§35.6"] + tarefas: + - id: EP-07-T01 + titulo: "Componente de outbound identity" + descricao: "Antes de chamar o backend, o Fabric obtém credencial apropriada (token exchange RFC 8693 via Keycloak, ou service account por adapter) em vez de repassar o token do client." + componente: "downstream" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-06-T04", "EP-01-T01"] + entregaveis: ["src/emcp_bus/downstream/identity.py"] + criterios_aceite: ["Dado um tools/call autorizado, quando a chamada chega ao backend mock, então o token usado no backend é diferente do token do MCP Client (audience distinta)."] + padroes_abertos: ["OAuth 2.1 (token exchange RFC 8693)"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-09", "ADR-006", "ADR-020"] + + - id: EP-07-T02 + titulo: "Config de credenciais por backend" + descricao: "Schema YAML `config/backends/*.yaml` (sem segredos inline — via ${ENV_VAR}, ver EP-00-T05) declarando o modelo de outbound identity por backend." + componente: "downstream" + tipo: feature + prioridade: must + estimativa: P + depende_de: ["EP-07-T01", "EP-00-T05"] + entregaveis: ["schemas/backend-credentials.schema.json", "config/backends/*.yaml"] + criterios_aceite: ["Um arquivo com segredo literal (não ${ENV_VAR}) é rejeitado pelo schema/CI."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-09", "ADR-020"] + + - epico_id: EP-08 + titulo: "Auditoria, correlação e observabilidade" + objetivo: "Tornar toda decisão relevante rastreável ponta a ponta, sem registrar segredos (§27, RF-17)." + secoes_readme: ["§27", "§28 (traceability gap)", "RF-17"] + tarefas: + - id: EP-08-T01 + titulo: "Tracing OTel de base" + descricao: "Instrumentação OpenTelemetry (traces/metrics/logs) com propagação W3C Trace Context em Gateway, Fabric e serviços de exemplo." + componente: "audit" + tipo: observabilidade + prioridade: must + estimativa: M + depende_de: ["EP-00-T01"] + entregaveis: ["src/emcp_bus/common/otel.py", "deploy/otel/collector-config.yaml"] + criterios_aceite: ["Dado um tools/call ponta a ponta, quando inspecionado no coletor, então um único trace_id conecta Gateway→Fabric→backend mock (Marco M0)."] + padroes_abertos: ["OpenTelemetry", "W3C Trace Context"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RNF-Performance (§37)", "Marco M0"] + + - id: EP-08-T02 + titulo: "Emissor de eventos de auditoria" + descricao: "Taxonomia de eventos do §27 (client_authenticated, tools_list_filtered, tool_call_denied, etc.) com sink append-only (arquivo em dev, Postgres opcional), excluindo tokens/secrets/dados sensíveis. Adota o vocabulário IC/NOC/GRL (evento de negócio-jornada / operacional-erro / guardrail-governança) do Agent Platform OCI (ADR-022) como categorização do `EventEnvelope`, carregando `decisionId`/`policyDecisionId`/`entitlementVersion` (ADR-017) como campos extras do payload em vez de reinventar taxonomia." + componente: "audit" + tipo: observabilidade + prioridade: must + estimativa: M + depende_de: ["EP-08-T01"] + entregaveis: ["src/emcp_bus/audit/events.py", "src/emcp_bus/audit/sink.py"] + criterios_aceite: ["Um evento de auditoria nunca contém o valor de um token/secret, apenas seu hash/referência (§27, 'Não registrar').", "Toda decisão DENY do PDP gera um evento `tool_call_denied` com reason_code.", "Todo evento é classificável em IC (negócio/jornada), NOC (operacional/erro) ou GRL (guardrail/governança)."] + padroes_abertos: ["CloudEvents"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-07", "ADR-022"] + + - id: EP-08-T03 + titulo: "Correlação ponta a ponta" + descricao: "Encadear profileVersion + entitlementVersion + decisionId + policyDecisionId + mcpRequestId através de todos os componentes, expostos no trace e no evento de auditoria." + componente: "audit" + tipo: observabilidade + prioridade: must + estimativa: M + depende_de: ["EP-08-T02", "EP-04-T04"] + entregaveis: ["src/emcp_bus/audit/correlation.py"] + criterios_aceite: ["Dada uma jornada completa (perfil→entitlement→NBA→execução), quando consultada por decisionId, então todos os eventos relacionados são recuperáveis (fecha Threat: Traceability gap, §28)."] + padroes_abertos: ["W3C Trace Context"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-17", "ADR-017"] + + - id: EP-08-T04 + titulo: "Stack local de observabilidade" + descricao: "OTel Collector + backend de traces OSS (Jaeger ou Tempo) no compose (`llm-observability` profile opcional para não pesar o M0)." + componente: "audit" + tipo: infra + prioridade: should + estimativa: P + depende_de: ["EP-08-T01"] + entregaveis: ["compose services `otel-collector`, `jaeger`"] + criterios_aceite: ["Traces do EP-08-T01 são visíveis na UI do Jaeger local."] + padroes_abertos: ["OpenTelemetry"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: [] + + - epico_id: EP-09 + titulo: "Profile Intelligence (contrato genérico)" + objetivo: "Expor uma visão de perfil versionada e substituível que apenas ordena/reduz candidatas, nunca concede entitlement (§6.10)." + secoes_readme: ["§6.10", "Axiom 11"] + tarefas: + - id: EP-09-T01 + titulo: "Contrato ProfileView" + descricao: "Schema JSON + Pydantic do exemplo §6.10 (subjectRef, profileVersion, segments, attributes, reasonCodes, expiresAt)." + componente: "profile_intelligence" + tipo: feature + prioridade: must + estimativa: P + depende_de: ["EP-00-T02"] + entregaveis: ["schemas/profile-view.schema.json", "src/emcp_bus/profile_intelligence/models.py"] + criterios_aceite: ["Um ProfileView sem `expiresAt` ou `profileVersion` é rejeitado pelo schema."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-13", "ADR-013"] + + - id: EP-09-T02 + titulo: "Stub de referência (regras determinísticas)" + descricao: "Implementação plugável (`ProfileIntelligenceProvider`) baseada em regras sobre atributos sintéticos (ver P7) — não implementa clusterização real nem taxonomia proprietária." + componente: "profile_intelligence" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-09-T01"] + entregaveis: ["src/emcp_bus/profile_intelligence/rule_based_provider.py"] + criterios_aceite: ["Dado o mesmo subjectRef e mesma janela temporal, quando resolvido duas vezes, então produz o mesmo profileVersion (determinismo para fins de demo)."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "Regra determinística, sem estado conversacional — não requer LangGraph." + riscos: ["Risco de escopo: tentar implementar clusterização 'de verdade' sem dataset real — mitigado por P7."] + rastreabilidade: ["RF-13", "P7"] + + - id: EP-09-T03 + titulo: "Guardrails de governança de dados" + descricao: "subjectRef pseudonimizado, log de lineage de features/versão/janela temporal, stub de monitor de drift (§6.10, requisitos de governança)." + componente: "profile_intelligence" + tipo: seguranca + prioridade: should + estimativa: M + depende_de: ["EP-09-T02"] + entregaveis: ["src/emcp_bus/profile_intelligence/governance.py"] + criterios_aceite: ["Nenhum ProfileView expõe PII direta — apenas subjectRef pseudonimizado (Threat: Sensitive attribute leakage, §28)."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§6.10", "Threat §28"] + + - epico_id: EP-10 + titulo: "Offering Filter" + objetivo: "Intersectar catálogo ativo, entitlement, perfil e contexto/consentimento sem nunca reintroduzir capability fora do entitlement (§6.11, §7.3)." + secoes_readme: ["§6.11", "§7.3", "Axiom 8/11"] + tarefas: + - id: EP-10-T01 + titulo: "Serviço Offering Filter" + descricao: "FilteredOfferings = ActiveCatalog ∩ MaximumEntitlement ∩ SubjectAndContextConstraints, consumindo Registry (EP-02), Entitlement Manager (EP-04), ProfileView (EP-09) e consentimento/contexto declarados." + componente: "offering_filter" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-02-T03", "EP-04-T02", "EP-09-T01"] + entregaveis: ["src/emcp_bus/offering_filter/service.py"] + criterios_aceite: ["Dado um item fora do MaximumEntitlement do client, quando o filtro roda com qualquer score de perfil, então o item nunca aparece em FilteredOfferings."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "Interseção de conjuntos determinística — não é fluxo de agente." + riscos: [] + rastreabilidade: ["RF-14", "ADR-014"] + + - id: EP-10-T02 + titulo: "Testes de propriedade da invariante de subconjunto" + descricao: "Testes baseados em propriedade (ex.: Hypothesis) provando FilteredOfferings ⊆ MaximumEntitlement ⊆ GlobalCatalog para entradas geradas aleatoriamente (§7.3)." + componente: "offering_filter" + tipo: teste + prioridade: must + estimativa: M + depende_de: ["EP-10-T01"] + entregaveis: ["tests/unit/test_offering_filter_invariants.py"] + criterios_aceite: ["1000+ casos gerados aleatoriamente não produzem nenhuma violação da invariante de subconjunto."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§7.3", "RF-14"] + + - epico_id: EP-11 + titulo: "Service Orchestrator (NBA/NBO)" + objetivo: "Calcular a Next Best Action e a Next Best Offer (NBA/NBO) como decisão de orquestração — nunca de autorização — sobre estado de jornada, regras, guardrails, memória e o universo de serviços/ofertas/ações que o MCP Fabric expõe (§6.12, §10.1). Por ADR-024, este componente é deliberadamente robusto o suficiente (modelo de decisão pluggable, incluindo IA de ranking/recomendação) para operar tanto como fonte de FilteredOfferings para uma plataforma de orquestração externa (modo A) quanto como o único cérebro de decisão de uma integração ponta a ponta, substituindo a camada de decisão dessa plataforma externa (modo B) — modularidade + compatibilidade = escolha do adotante, nunca uma imposição da RI." + secoes_readme: ["§6.12", "§10.1", "P9 (NBA não é autorização)", "Axiom 12", "ADR-024"] + tarefas: + - id: EP-11-T01 + titulo: "Contrato DecisionContext" + descricao: "Modelo Pydantic versionado combinando ProfileView, identidade autenticada, EntitlementLimits, FilteredOfferings e RuntimeContext (§1, contrato formal)." + componente: "orchestrator" + tipo: feature + prioridade: must + estimativa: P + depende_de: ["EP-09-T01", "EP-04-T04", "EP-10-T01"] + entregaveis: ["src/emcp_bus/orchestrator/decision_context.py"] + criterios_aceite: ["DecisionContext é serializável/versionado e rejeita construção sem entitlementVersion."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§1"] + + - id: EP-11-T02 + titulo: "Grafo de jornada em LangGraph" + descricao: "StateGraph com estado de jornada, objetivo e memória; checkpointer SQLite (dev, com ponto de extensão para mongodb/produção — padrão de providers plugáveis do ADR-022). Nós fixos: resolve profile → resolve entitlement → filter offerings → decide NBA, seguindo o padrão de grafo corporativo fixo (dev só acrescenta nós de domínio, nunca mexe na espinha dorsal) descrito em `docs/research/hoshikawa-agent-platform-oci.md`." + componente: "orchestrator" + tipo: feature + prioridade: must + estimativa: G + depende_de: ["EP-11-T01"] + entregaveis: ["src/emcp_bus/orchestrator/graph.py", "src/emcp_bus/orchestrator/state.py (TypedDict do estado de jornada)"] + criterios_aceite: ["Dado um checkpoint salvo, quando o processo reinicia, então a jornada retoma do último estado sem reprocessar do zero."] + padroes_abertos: [] + usa_langgraph: true + usa_langfuse: false + justificativa_langgraph_langfuse: "Fluxo com estado, múltiplos passos e necessidade de checkpointing/retomada — critério explícito da seção 6 do superprompt para uso de LangGraph." + riscos: ["Overhead de checkpointing para um fluxo ainda simples na RI — mitigado mantendo o grafo mínimo até M3"] + rastreabilidade: ["RF-15", "ADR-015", "ADR-022"] + + - id: EP-11-T03 + titulo: "Nó de decisioning (NBA/NBO)" + descricao: "Função de decisão versionada combinando rules + guardrails sobre DecisionContext, produzindo NBA/NBO (§6.13, exemplo JSON) e persistindo a trilha de decisão. Modelo de decisão pluggable (interface `NBADecisionModel`): implementação de referência determinística (regras/prioridade) na RI, com ponto de extensão explícito para um modelo de IA de ranking/recomendação sobre `FilteredOfferings` (serviços/ofertas/ações do Fabric) — sempre restrito ao universo já filtrado por entitlement (Axiom 8/11), nunca decidindo autorização." + componente: "orchestrator" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-11-T02"] + entregaveis: ["src/emcp_bus/orchestrator/decisioning.py", "src/emcp_bus/orchestrator/nba_model.py (interface pluggable)"] + criterios_aceite: ["Toda NBA/NBO emitida inclui reasonCodes, policyContext e expiresAt (§6.13).", "Trocar a implementação de `NBADecisionModel` (regras → modelo de IA) não exige mudança de contrato em EP-05/EP-03/EP-04 — a superfície de autorização é idêntica independentemente do modelo de decisão."] + padroes_abertos: [] + usa_langgraph: true + usa_langfuse: false + justificativa_langgraph_langfuse: "Nó do grafo de jornada definido em EP-11-T02." + riscos: [] + rastreabilidade: ["RF-15", "ADR-024"] + + - id: EP-11-T04 + titulo: "Recálculo e degradação segura" + descricao: "Branch DENY/indisponível do fluxo §10.1: recalcular NBA, degradar com segurança ou encerrar jornada quando o Gateway nega a execução — a NBA negada NUNCA é substituída por bypass." + componente: "orchestrator" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-11-T03", "EP-05-T04"] + entregaveis: ["src/emcp_bus/orchestrator/fallback.py"] + criterios_aceite: ["Dado um DENY do Gateway, quando recebido pelo Orchestrator, então nenhuma tentativa de execução direta ao Fabric ocorre — apenas recálculo ou encerramento auditado."] + padroes_abertos: [] + usa_langgraph: true + usa_langfuse: false + justificativa_langgraph_langfuse: "Edge condicional do StateGraph de EP-11-T02." + riscos: [] + rastreabilidade: ["§10.1", "Axiom 12", "P9"] + + - id: EP-11-T05 + titulo: "Interrupção human-in-the-loop" + descricao: "Nó de interrupção do grafo (`interrupt`) disparado quando a NBA envolve capability REQUIRE_APPROVAL, integrando com EP-14." + componente: "orchestrator" + tipo: feature + prioridade: should + estimativa: M + depende_de: ["EP-11-T03"] + entregaveis: ["src/emcp_bus/orchestrator/hitl_node.py"] + criterios_aceite: ["Dada uma NBA de alto risco, quando o grafo chega ao nó de aprovação, então a execução pausa até resposta de EP-14, sem timeout silencioso que vire ALLOW."] + padroes_abertos: [] + usa_langgraph: true + usa_langfuse: false + justificativa_langgraph_langfuse: "Human-in-the-loop com pausa/retomada de estado — critério explícito da seção 6 do superprompt." + riscos: [] + rastreabilidade: ["RF-10", "§26"] + + - epico_id: EP-12 + titulo: "Agent Runtime e channel adapters" + objetivo: "Traduzir a NBA em execução coordenada — sessão, contexto mínimo, handoff — sem nunca substituir o token do MCP Client (§6.13)." + secoes_readme: ["§6.13", "ADR-016"] + tarefas: + - id: EP-12-T01 + titulo: "Serviço de dispatch" + descricao: "Consome o objeto de dispatch da NBA (§6.13, exemplo JSON), gerencia sessão/correlação e invoca o MCP Client correspondente ao agente/canal selecionado." + componente: "agent_runtime" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-11-T03", "EP-01-T02"] + entregaveis: ["src/emcp_bus/agent_runtime/dispatcher.py"] + criterios_aceite: ["O Agent Runtime nunca gera ou armazena credenciais próprias — usa exclusivamente o MCP Client já registrado (EP-01)."] + padroes_abertos: ["MCP"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "Dispatch é invocação direta de um MCP Client já autenticado — não há estado cíclico próprio aqui (o estado vive no Orchestrator)." + riscos: [] + rastreabilidade: ["RF-16", "ADR-016"] + + - id: EP-12-T02 + titulo: "Channel adapter conversacional" + descricao: "Um adapter concreto (app/conversacional) para a demo da RI, encapsulando diferenças de canal citadas em §6.13/§3.3." + componente: "agent_runtime" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-12-T01"] + entregaveis: ["src/emcp_bus/agent_runtime/channels/conversational.py"] + criterios_aceite: ["Uma NBA com channel=app é entregue ao adapter correto; um channel não suportado falha de forma explícita, não silenciosa."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-16"] + + - id: EP-12-T03 + titulo: "Handoff entre agentes" + descricao: "Lógica de handoff (ex.: engagement-assistant → especialista de domínio) coordenada como subgrafo LangGraph, preservando contexto mínimo necessário e correlação. Inspirado no padrão de dois níveis do Agent Platform OCI (ADR-022): handoff intra-processo via aresta condicional de retorno, e um campo de metadata análogo a `metadata.handover_backend` quando o handoff cruza para outro agente/serviço. Restrição de segurança explícita (ADR-023): o campo de handoff é só um sinal de intenção de roteamento — a tools/list e tools/call do agente-destino continuam sendo autorizadas do zero pelo Gateway/PDP (EP-05), nunca herdadas do agente de origem." + componente: "agent_runtime" + tipo: feature + prioridade: could + estimativa: M + depende_de: ["EP-12-T01"] + entregaveis: ["src/emcp_bus/agent_runtime/handoff_graph.py"] + criterios_aceite: ["Dado um handoff, quando concluído, então o novo agente recebe apenas o contexto mínimo necessário, não o histórico bruto completo.", "O MCP Client/entitlement do agente-destino após handoff é resolvido independentemente, nunca herdado do agente de origem."] + padroes_abertos: [] + usa_langgraph: true + usa_langfuse: false + justificativa_langgraph_langfuse: "Coordenação multiagente com handoff — critério explícito da seção 6 do superprompt (padrão supervisor/handoff)." + riscos: ["Feature poderia ser considerada fora do essencial da RI — marcada `could`, não bloqueia marcos principais."] + rastreabilidade: ["§6.13", "ADR-022", "ADR-023"] + + - id: EP-12-T04 + titulo: "Identity Resolver / BusinessContext" + descricao: "Adota o padrão `identity.yaml`/BusinessContext do Agent Platform OCI (ADR-022): aliases declarativos de nomes de canal → chaves canônicas de negócio (ex.: customer_key, contract_key, session_key), usados para montar argumentos de tool de forma padronizada. É puramente normalização de identidade de negócio — nunca decide autorização (a diferença explícita do achado do ADR-023: aqui o campo resolvido não deve, em nenhuma hipótese, alimentar a decisão do PDP)." + componente: "agent_runtime" + tipo: feature + prioridade: should + estimativa: M + depende_de: ["EP-12-T01"] + entregaveis: ["schemas/identity-resolver.schema.json", "config/orchestrator/identity.yaml", "src/emcp_bus/agent_runtime/identity_resolver.py"] + criterios_aceite: ["Dado um payload de canal com um alias declarado (ex.: `msisdn`), quando resolvido, então produz a chave canônica correta (`customer_key`).", "O resultado do Identity Resolver nunca é aceito como entrada do PDP (EP-03) — apenas do Fabric/MCP Parameter Mapping."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["ADR-022", "ADR-023"] + + - epico_id: EP-13 + titulo: "Agente de exemplo e instrumentação Langfuse" + objetivo: "Demonstrar um agente LLM real consumindo o catálogo filtrado, com observabilidade de LLM via Langfuse self-hosted e mascaramento de PII (§6.1). Por ADR-024, o objetivo final do épico vai além de um único agente: demonstrar a posição de 'elevação a Enterprise' do E-MCP-BUS — múltiplos backends/agentes (padrão Agent Platform OCI/Hoshikawa), cada um com seu próprio MCP Client e entitlement, orquestrados por um Global Supervisor acima do bus, sem que a troca de backend amplie privilégio (EP-13-T05)." + secoes_readme: ["§6.1", "§14", "§29"] + tarefas: + - id: EP-13-T01 + titulo: "Agente conversacional de exemplo" + descricao: "Agente LLM com tool-calling restrito ao catálogo retornado por tools/list do client atribuído (demonstra P1–P4 do §1: agente não escolhe entitlement). Instrumentação híbrida conforme ADR-022: span técnico automático por etapa do agente + eventos de negócio manuais e fail-open (IC/NOC/GRL, ver EP-08-T02) — um erro no observer nunca derruba a jornada." + componente: "example_agent" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-05-T02", "EP-05-T04"] + entregaveis: ["agents/example_agent/"] + criterios_aceite: ["Cenário do §29 (prompt injection pedindo finance.createPayment) reproduzido: o agente tenta, o Gateway nega (Teste 3, §45)."] + padroes_abertos: ["MCP"] + usa_langgraph: false + usa_langfuse: true + justificativa_langgraph_langfuse: "Chamadas reais a LLM (tokens, custo, latência) precisam de tracing de generation — critério explícito da seção 6 do superprompt para uso de Langfuse." + riscos: [] + rastreabilidade: ["§6.1", "§14", "§29", "ADR-022"] + + - id: EP-13-T02 + titulo: "Langfuse self-hosted no compose" + descricao: "Deploy da edição OSS do Langfuse (perfil `llm-observability`) com exportação de traces via OTel a partir do agente e do Orchestrator. Stack de referência confirmada em `docs/research/hoshikawa-agent-platform-oci.md` (ADR-022): langfuse-web + langfuse-worker + PostgreSQL + ClickHouse + Redis + MinIO. Habilitar instrumentação automática de chamadas LLM compatíveis com OpenAI (equivalente a `ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION`) para captar tokens/custo/latência sem código manual por chamada." + componente: "example_agent" + tipo: infra + prioridade: must + estimativa: M + depende_de: ["EP-08-T01"] + entregaveis: ["deploy/langfuse/docker-compose.override.yml"] + criterios_aceite: ["Uma execução do agente de exemplo aparece na UI do Langfuse com spans de generation, tokens e latência."] + padroes_abertos: ["OpenTelemetry"] + usa_langgraph: false + usa_langfuse: true + justificativa_langgraph_langfuse: "Infra de suporte à instrumentação definida em EP-13-T01." + riscos: ["[NÃO-OSS]: recursos de billing/RBAC avançado do Langfuse Cloud não são usados — apenas a edição self-hosted/OSS."] + rastreabilidade: ["§6 (seção Langfuse do superprompt)", "ADR-022"] + + - id: EP-13-T03 + titulo: "Redação de PII/segredos antes do trace" + descricao: "Camada de mascaramento aplicada a prompts/outputs antes de enviar ao Langfuse — obrigatória por regra da seção 6 do superprompt." + componente: "example_agent" + tipo: seguranca + prioridade: must + estimativa: M + depende_de: ["EP-13-T02"] + entregaveis: ["src/emcp_bus/common/pii_redaction.py"] + criterios_aceite: ["Dado um output de tool contendo um e-mail/CPF sintético, quando exportado ao Langfuse, então aparece mascarado no trace."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: true + justificativa_langgraph_langfuse: "Pré-processamento obrigatório antes de qualquer envio a Langfuse." + riscos: [] + rastreabilidade: ["§27 ('Não registrar')"] + + - id: EP-13-T04 + titulo: "Versionamento de prompt no Langfuse" + descricao: "Gestão do system prompt do agente de exemplo via Langfuse Prompt Management, versionado e referenciado por versão nas traces." + componente: "example_agent" + tipo: feature + prioridade: could + estimativa: P + depende_de: ["EP-13-T02"] + entregaveis: ["agents/example_agent/prompts/"] + criterios_aceite: ["Uma mudança de prompt gera nova versão rastreável, sem sobrescrever a anterior."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: true + justificativa_langgraph_langfuse: "Gestão/versionamento de prompts — caso de uso explícito de Langfuse na seção 6 do superprompt." + riscos: [] + rastreabilidade: [] + + - id: EP-13-T05 + titulo: "Modo A — Multiagente governado: Global Supervisor externo sobre o E-MCP-BUS" + descricao: "Materializa o modo A do ADR-024 (governança-somente) — a demonstração central de 'elevação a Enterprise' pedida pelo autor. Dois backends de agente distintos (`sales_agent`, `finance_agent`, cada um seu próprio processo/StateGraph, padrão Agent Platform OCI), cada um com seu próprio MCP Client (EP-01) vinculado a um client profile/entitlement diferente (Sales.Read/Write via EP-04 vs. Finance.Read/Payments). Um Global Supervisor **externo** (padrão Agent Gateway do ADR-022, decidindo por conta própria qual backend/NBA usar) roteia a conversa entre os dois backends, incluindo handoff, mas TODA autorização de tools continua exclusivamente no PEP/PDP do E-MCP-BUS — o Global Supervisor nunca decide nem amplia entitlement, e nosso Service Orchestrator (EP-11) não participa da decisão de NBA neste modo." + componente: "example_agent" + tipo: feature + prioridade: should + estimativa: G + depende_de: ["EP-12-T01", "EP-12-T03", "EP-13-T01", "EP-04-T02"] + entregaveis: ["agents/example_agent/backends/sales_agent/", "agents/example_agent/backends/finance_agent/", "src/emcp_bus/agent_runtime/global_supervisor.py"] + criterios_aceite: ["Dado uma conversa iniciada em sales_agent (MCP Client=Sales.Read/Write) que sofre handoff para finance_agent, quando o handoff ocorre, então finance_agent resolve seu próprio MaximumEntitlement (Finance.*) do zero — nunca herda nem amplia o de sales_agent (Axiom 4/9).", "Uma injeção de prompt em sales_agent tentando, via handoff, alcançar finance.payment.execute fora do entitlement de sales_agent é negada pelo Gateway, mesmo que o Global Supervisor tenha roteado a conversa."] + padroes_abertos: ["MCP"] + usa_langgraph: true + usa_langfuse: true + justificativa_langgraph_langfuse: "Orquestração multiagente com handoff entre backends distintos (padrão Global Supervisor do ADR-022) e necessidade de correlacionar em uma única trace toda a jornada cross-backend (Langfuse) — critérios explícitos da seção 6 do superprompt para ambos." + riscos: ["Escopo maior que um agente único; não bloqueia M0-M2. É a tarefa que melhor demonstra o valor 'Enterprise' do bus (ADR-024) — priorizar dentro de M3/M4 mesmo não sendo `must`."] + rastreabilidade: ["ADR-024", "§18 (shared client rule)", "§31 (blast-radius containment)", "Axiom 9"] + + - id: EP-13-T06 + titulo: "Modo B — Service Orchestrator como único cérebro de NBA/NBO" + descricao: "Materializa o modo B do ADR-024 (substituição completa): a mesma cena de dois backends (Sales/Finance) de EP-13-T05, mas agora o Global Supervisor externo é dispensado — nosso próprio Service Orchestrator (EP-11-T03) calcula a NBA/NBO diretamente sobre `FilteredOfferings` combinando ofertas de Sales e Finance (ex.: apresentar um benefício vs. cobrar uma pendência), e o Agent Runtime (EP-12) apenas despacha o resultado para o backend/canal indicado — os backends de agente tornam-se executores puros, sem lógica própria de decisão de qual agir. Demonstra que a mesma stack de segurança (EP-01/EP-03/EP-05) não muda entre os dois modos, apenas quem decide a NBA/NBO." + componente: "example_agent" + tipo: feature + prioridade: could + estimativa: M + depende_de: ["EP-11-T03", "EP-13-T05"] + entregaveis: ["agents/example_agent/mode_b_orchestrator_driven/"] + criterios_aceite: ["Dado o mesmo cenário de EP-13-T05 (subject elegível a um benefício de Sales e com pendência em Finance), quando rodado no modo B, então a NBA/NBO escolhida e o backend acionado vêm do nosso Service Orchestrator, não de um Global Supervisor externo.", "O conjunto de testes de segurança de EP-15-T02 passa igualmente nos modos A e B, sem nenhuma alteração no PEP/PDP entre eles."] + padroes_abertos: [] + usa_langgraph: true + usa_langfuse: true + justificativa_langgraph_langfuse: "Mesmo grafo de jornada de EP-11-T02 agora dirigindo diretamente múltiplos backends de execução; Langfuse correlaciona a decisão única do Orchestrator com a execução em qualquer um dos backends." + riscos: ["Prioridade `could` — demonstra a modularidade do ADR-024, mas não é necessária para nenhum critério de aceite de segurança; pode ser adiada sem risco para o núcleo da RI."] + rastreabilidade: ["ADR-024", "RF-15"] + + - epico_id: EP-14 + titulo: "Human-in-the-loop / Approval workflow" + objetivo: "Aprovação adicional para operações de alto risco, fora da influência exclusiva do LLM (§26)." + secoes_readme: ["§26", "§17 (R3/R4)"] + tarefas: + - id: EP-14-T01 + titulo: "Contrato de aprovação" + descricao: "Objeto de aprovação out-of-band, criptograficamente associado à operação, com prazo de validade e single-use (§26)." + componente: "approval" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-03-T04"] + entregaveis: ["schemas/approval-request.schema.json", "src/emcp_bus/approval/models.py"] + criterios_aceite: ["Uma aprovação usada duas vezes na segunda tentativa é rejeitada (single-use, Threat: approval replay, §46)."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-10"] + + - id: EP-14-T02 + titulo: "Serviço de aprovação" + descricao: "API/CLI mínima que gate a execução de REQUIRE_APPROVAL do PDP/Orchestrator, com resumo dos argumentos relevantes exibido ao aprovador." + componente: "approval" + tipo: feature + prioridade: must + estimativa: M + depende_de: ["EP-14-T01", "EP-11-T05"] + entregaveis: ["src/emcp_bus/approval/service.py"] + criterios_aceite: ["Dado payment.execute >= 10k, quando solicitado, então a execução só prossegue após aprovação explícita registrada (§26 exemplo)."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-10", "§26"] + + - id: EP-14-T03 + titulo: "Fail-closed do serviço de aprovação" + descricao: "Se o serviço de aprovação estiver indisponível, toda operação high-risk é negada, nunca liberada por omissão (§38)." + componente: "approval" + tipo: seguranca + prioridade: must + estimativa: P + depende_de: ["EP-14-T02"] + entregaveis: ["src/emcp_bus/approval/failure_policy.py"] + criterios_aceite: ["Dado o serviço de aprovação fora do ar, quando uma R3/R4 é solicitada, então DENY, não timeout silencioso."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§38"] + + - epico_id: EP-15 + titulo: "Testes de segurança e adversariais" + objetivo: "Validar formalmente os 10 critérios de aceite de segurança (§45) e os cenários adversariais (§46) contra a stack completa." + secoes_readme: ["§45", "§46", "§28"] + tarefas: + - id: EP-15-T01 + titulo: "Testes de contrato dos MCP Servers" + descricao: "Testes que validam cada tool exposta contra o input/output schema declarado em seu manifesto (EP-02)." + componente: "tests" + tipo: teste + prioridade: must + estimativa: M + depende_de: ["EP-06-T02", "EP-06-T03", "EP-02-T05"] + entregaveis: ["tests/contract/test_sales_domain.py", "tests/contract/test_finance_domain.py"] + criterios_aceite: ["Uma resposta de tool que viola seu output schema falha o teste de contrato."] + padroes_abertos: ["JSON Schema"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["RF-06"] + + - id: EP-15-T02 + titulo: "Suíte de aceite de segurança (Testes 1–10, §45)" + descricao: "Automação pytest dos 10 testes formais do §45 rodando contra o docker-compose completo." + componente: "tests" + tipo: teste + prioridade: must + estimativa: G + depende_de: ["EP-05-T02", "EP-05-T03", "EP-05-T04", "EP-05-T07", "EP-01-T03", "EP-04-T03"] + entregaveis: ["tests/e2e/test_security_acceptance.py"] + criterios_aceite: ["Os 10 testes do §45 (unauthorized discovery, direct unauthorized call, prompt injection, role spoofing, cache isolation, policy revocation, gateway bypass, token audience, backend isolation, client compromise) passam de ponta a ponta."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: ["Suíte grande — dividir execução por marco em vez de tentar completar tudo de uma vez"] + rastreabilidade: ["§45", "todos os RF de segurança"] + + - id: EP-15-T03 + titulo: "Testes adversariais de prompt/tool injection" + descricao: "Cenários do §46: direct/indirect prompt injection, malicious RAG chunk, tool output injection, tool description poisoning, hidden tool invocation, tool name spoofing. Inclui um cenário adicional derivado do achado do ADR-023 (payload-declared identity spoofing): forjar `agent_id`/`tenant_id`/`business_context` no payload de entrada do canal, tentando `tools/call` de uma capability fora do MaximumEntitlement do MCP Client real — variação prática do Teste 4 (§45, agent role spoofing), motivada por uma vulnerabilidade real observada em pesquisa de referência (docs/research/hoshikawa-agent-platform-oci.md)." + componente: "tests" + tipo: teste + prioridade: must + estimativa: G + depende_de: ["EP-13-T01"] + entregaveis: ["tests/adversarial/test_prompt_injection.py", "tests/adversarial/test_tool_poisoning.py", "tests/adversarial/test_payload_identity_spoofing.py"] + criterios_aceite: ["Nenhum cenário simulado resulta em EffectiveCapabilities excedendo ClientEntitlement (Axiom 4).", "Um `agent_id`/`tenant_id` forjado no payload de entrada nunca altera a identidade usada pelo PDP para resolver entitlement (ADR-023)."] + padroes_abertos: ["OWASP GenAI LLM01"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§14", "§28", "§46", "ADR-023"] + + - id: EP-15-T04 + titulo: "Testes adversariais de protocolo/identidade" + descricao: "Cenários do §46: replay, stolen/expired/wrong-audience token, header/body mismatch, shared-cache poisoning, entitlement race condition, approval replay, bulk exfiltration via tools legítimas." + componente: "tests" + tipo: teste + prioridade: must + estimativa: G + depende_de: ["EP-05-T08", "EP-05-T03", "EP-14-T01", "EP-05-T05"] + entregaveis: ["tests/adversarial/test_protocol_identity.py"] + criterios_aceite: ["Cada um dos 8 cenários resulta em DENY/rejeição determinística, sem exceção não tratada."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§46", "§28"] + + - id: EP-15-T05 + titulo: "Testes de política de falha (fail-closed)" + descricao: "Chaos tests derrubando PDP/IdP/Registry/Approval individualmente e validando o comportamento esperado da tabela do §38." + componente: "tests" + tipo: teste + prioridade: must + estimativa: M + depende_de: ["EP-05-T06", "EP-14-T03"] + entregaveis: ["tests/e2e/test_fail_closed.py"] + criterios_aceite: ["Para cada componente da tabela §38, o comportamento observado corresponde exatamente ao recomendado."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: ["§38", "RNF-Disponibilidade"] + + - epico_id: EP-16 + titulo: "Deploy final e documentação" + objetivo: "Consolidar a stack completa, ADRs e documentação de uso da RI." + secoes_readme: ["Todo o documento — integração final"] + tarefas: + - id: EP-16-T01 + titulo: "docker-compose completo" + descricao: "Expandir EP-00-T04 para incluir todos os serviços (Keycloak, OPA, Registry store, Gateway, Fabric, servidores de domínio, Orchestrator, Agent Runtime, agente de exemplo, OTel/Jaeger, Langfuse) com perfis coerentes." + componente: "deploy" + tipo: infra + prioridade: must + estimativa: M + depende_de: ["EP-05-T08", "EP-11-T05", "EP-12-T02", "EP-13-T04", "EP-14-T03", "EP-15-T05"] + entregaveis: ["RI/docker-compose.yml (final)"] + criterios_aceite: ["`docker compose up` sobe a stack completa e a suíte EP-15-T02 passa contra ela."] + padroes_abertos: ["OCI"] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: [] + + - id: EP-16-T02 + titulo: "README da RI" + descricao: "Quickstart, mapa de volta para as seções do README de arquitetura, como rodar cada suíte de testes." + componente: "docs" + tipo: docs + prioridade: must + estimativa: P + depende_de: ["EP-16-T01"] + entregaveis: ["RI/README.md"] + criterios_aceite: ["Uma pessoa nova consegue subir a stack e rodar a suíte M0 seguindo apenas o README da RI."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: [] + + - id: EP-16-T03 + titulo: "ADRs formalizados" + descricao: "Um arquivo por ADR (ADR-001..021 da seção 8.4 deste documento) em docs/adr/, no formato Contexto/Decisão/Alternativas/Consequências." + componente: "docs" + tipo: docs + prioridade: should + estimativa: M + depende_de: ["EP-16-T01"] + entregaveis: ["docs/adr/ADR-001-*.md ... ADR-021-*.md"] + criterios_aceite: ["Todos os 21 ADRs listados na seção 8.4 deste documento existem como arquivo individual."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: [] + rastreabilidade: [] + + - id: EP-16-T04 + titulo: "Spike: verificação de versões estáveis" + descricao: "Confirmar, imediatamente antes de fixar em pyproject.toml, os números exatos de versão estável de: SDK `mcp` (Python v2), LangGraph, Langfuse (edição OSS), OPA (v1.x, sintaxe Rego v1), Keycloak. Parcialmente pré-verificado em 2026-09-11 via busca web: a revisão MCP 2026-07-28 é real e GA (não é invenção do README); o SDK Python `mcp` v2 já suporta nativamente `cache_hints`/ttlMs/cacheScope (SEP-2549) e os headers Mcp-Method/Mcp-Name (SEP-2243); OPA está em v1.0+ com Rego v1 como sintaxe padrão. Falta apenas fixar os números de patch/minor exatos no pyproject.toml no momento da implementação." + componente: "docs" + tipo: spike + prioridade: must + estimativa: P + depende_de: [] + entregaveis: ["docs/adr/ADR-018-version-pins.md (registro das versões confirmadas e data da verificação)"] + criterios_aceite: ["Nenhuma versão é citada em código/config sem ter sido confirmada nesta tarefa."] + padroes_abertos: [] + usa_langgraph: false + usa_langfuse: false + justificativa_langgraph_langfuse: "" + riscos: ["Sem esta tarefa, há risco de alucinação de versão — por isso priorizada `must` e sem dependências, executável a qualquer momento."] + rastreabilidade: ["Regra 4 do superprompt (sem alucinação de versões)"] +``` + +--- + +## 8.7 Sequenciamento e marcos + +| Marco | Tarefas | Critério de "pronto" | +|---|---|---| +| **M0 — Walking skeleton** | EP-00-T01, EP-00-T02, EP-00-T04, EP-06-T02, EP-05-T01, EP-08-T01 | Cliente MCP → Gateway → (roteamento estático) → MCP Server de exemplo (Sales) → resposta, com `trace_id` correlacionado visível em log/coletor OTel. Sem enforcement real ainda. | +| **M1 — Segurança núcleo (Fase 1 do README)** | EP-01 completo, EP-03 completo, EP-04 completo, EP-05-T02/T03/T04/T06/T07/T08, EP-02-T01/T02/T03/T05, EP-06-T01/T03/T04, EP-08-T02/T03/T04 | `tools/list` filtrado por entitlement real (Keycloak+OPA), `tools/call` reautorizado, cache privado, fail-closed, anti-bypass, auditoria completa. Cobre RF-01 a RF-09 e Fases 0–2 do roadmap do README. | +| **M2 — Governança e downstream** | EP-02-T04, EP-04-T04, EP-07 completo, EP-14 completo | Publishing pipeline com lifecycle completo, outbound identity sem passthrough, aprovação humana funcionando fail-closed. Cobre RF-10, RF-11, Fase 3 parcial. | +| **M3 — Personalização e orquestração (Fase 4 do README)** | EP-09, EP-10, EP-11, EP-12, EP-13-T01..T04 | Perfil→entitlement→ofertas filtradas→NBA→dispatch→execução MCP funcionando ponta a ponta com LangGraph no Orchestrator e Langfuse no agente de exemplo. Cobre RF-12 a RF-17. | +| **M4 — Hardening e entrega** | EP-13-T05, EP-13-T06 (could), EP-15 completo, EP-16 completo | Suíte de 10 testes de aceite (§45) e cenários adversariais (§46, incluindo o de EP-15-T03/ADR-023) passando contra a stack completa; **os dois modos de integração do ADR-024 demonstrados** — Modo A (EP-13-T05: Global Supervisor externo, dois backends/MCP Clients, handoff sem elevação de privilégio) e, se o tempo permitir, Modo B (EP-13-T06: nosso Service Orchestrator como único cérebro de NBA/NBO, mesma stack de segurança); ADRs documentados; README da RI publicado. | + +**Caminho crítico:** EP-00 → EP-01/EP-06 (paralelos) → EP-05 (núcleo do PEP) → EP-02/EP-03/EP-04 (entitlement real) → EP-08 (correlação) → EP-09/EP-10 → EP-11 (Orchestrator) → EP-12/EP-13 → EP-15 (validação) → EP-16 (entrega). EP-07 e EP-14 podem ser paralelizados a partir de M1 sem bloquear o caminho principal até M3. + +--- + +## 8.8 Riscos principais + +| # | Risco | Probabilidade | Impacto | Mitigação | +|---|---|---|---|---| +| 1 | Curva de aprendizado de OPA/Rego atrasa EP-03 | Média | Médio | Começar com poucas policies R1 (Fase 1 do roadmap); usar `opa test` desde o primeiro commit. | +| 2 | Complexidade do realm Keycloak para ambiente local | Baixa | Médio | Usar realm export versionado e reproduzível (EP-01-T01), não configuração manual. | +| 3 | LangGraph aplicado além do necessário, contaminando componentes determinísticos | Média | Baixo | Critério da seção 6 aplicado rigorosamente por task (ver campo `justificativa_langgraph_langfuse` de cada tarefa). | +| 4 | Cache privado mal configurado causa leakage entre clients (§35.8) | Baixa | Alto | EP-15-T04 testa isolamento de cache explicitamente antes de M4. | +| 5 | Profile Intelligence vira tentativa de "IA de verdade" sem dataset real | Média | Baixo | P7 fixa o escopo como stub baseado em regras; revisão de escopo em EP-09-T02. | +| 6 | Langfuse self-hosted (Postgres+ClickHouse+Redis+S3-compatible) torna o compose pesado | Alta | Médio | Perfil `llm-observability` opcional e desacoplado do `core` (M0–M2 não dependem dele). | +| 7 | Divergência entre stack do README (OPA/Keycloak citados) e stack Python definida pelo superprompt | Baixa | Médio | G1/P1 explicitados; confirmar com autor do README assim que possível. | +| 8 | Suíte adversarial (EP-15) subestimada — cobre 18+ cenários | Alta | Médio | Dividida em EP-15-T03/T04 por categoria, incrementável por marco em vez de "big bang" em M4. | +| 9 | Token exchange (RFC 8693) não suportado por todo backend mock | Média | Baixo | Fallback documentado para service account por adapter (P5/ADR-020). | +| 10 | Stakeholders cobrarem federação multi-domínio/multi-cloud antes do previsto | Média | Médio | Roadmap explicita (8.7) que federação e ambientes híbridos são pós-RI (Fase 5 do README), não M0–M4. | + +--- + +## 8.9 Resultado do checklist de autovalidação + +- ✅ Todo componente do README aparece em pelo menos uma tarefa (ver matriz 8.2); nenhuma tarefa implementa algo ausente do README sem `[PREMISSA]` declarada (ver 8.3). +- ✅ O YAML do backlog é sintaticamente válido; todos os IDs em `depende_de` referenciam tarefas existentes no próprio backlog (verificação manual cruzada durante a escrita — recomenda-se rodar um linter YAML/schema antes do uso operacional). +- ✅ O grafo de dependências é acíclico por construção: dependências apontam sempre para tarefas de épico igual ou numericamente anterior, exceto EP-06→EP-02 (fabric depende do registry, EP-02 < EP-06, sem ciclo) e EP-05-T07→EP-06-T01 (mesmo sentido). M0 (seção 8.7) é executável isoladamente com as 6 tarefas listadas, todas com dependências internas ao próprio conjunto M0. +- ✅ Cada tarefa tem ao menos um entregável e um critério de aceite testável (ver backlog 8.6). +- ✅ Todo uso de LangGraph está restrito a EP-11 (Service Orchestrator — estado, ciclos, checkpointing, HITL) e EP-12-T03 (handoff multiagente); todo uso de Langfuse está restrito a EP-13 (única camada com chamadas reais a LLM). Nenhum roteamento simples (Gateway EP-05, Fabric EP-06, Offering Filter EP-10) usa LangGraph — justificativa explícita em cada tarefa. +- ✅ Nenhuma dependência proprietária entrou sem marca `[NÃO-OSS]`; Keycloak e OPA foram verificados como Apache-2.0, Langfuse usado apenas em sua edição self-hosted/OSS (nota em EP-13-T02). +- ✅ Todo arquivo YAML de configuração previsto (client registration, capability manifest, client profile, backend credentials) tem tarefa correspondente de JSON Schema + modelo Pydantic + validação em CI (EP-00-T02/T03 fornecem o framework genérico; cada schema específico é entregável de sua tarefa de domínio). +- ✅ Há tarefas explícitas para: autenticação/autorização (EP-01), allowlist de tools por client/tenant (EP-04, EP-05-T02), defesa contra prompt injection via conteúdo de tools (EP-15-T03, governança de publicação em EP-02-T02), gestão de segredos (EP-00-T05, EP-07-T02), auditoria (EP-08) e mascaramento de PII (EP-13-T03, EP-09-T03). +- ✅ Há tarefas de testes de contrato para os servidores MCP (EP-15-T01) e testes end-to-end dos fluxos principais do README (EP-15-T02, cobrindo os 10 testes formais do §45). +- ✅ Nenhuma versão de biblioteca ou API foi afirmada sem certeza — todas as menções a versões usam "verificar" (ex.: EP-05-T08, referência à revisão MCP 2026-07-28) e uma tarefa dedicada (EP-16-T04) confirma versões antes de fixá-las em `pyproject.toml`. + +--- + +*Documento gerado seguindo o SUPERPROMPT de planejamento da RI. Confirmado pelo autor em 2026-09-11: G1 (`RI/` é criado do zero por este backlog, a partir de EP-00-T01), G3 (OPA é decisão técnica definitiva de PDP, por ser OSS), G4 (Keycloak é decisão técnica definitiva de identidade do MCP Client, por ser OSS), G2/G8 (Python/LangGraph/Langfuse seguem o padrão operacional do "Agent Platform OCI" de Christiano Hoshikawa — ver `docs/research/hoshikawa-agent-platform-oci.md` e ADR-022/ADR-023) e G11 (§6.2 do README corresponde ao papel de uma plataforma multiagente externa e consumidora do bus, não a um componente da RI — ADR-024, "elevação a Enterprise": o E-MCP-BUS é a camada de governança de catálogo/entitlement sob a qual qualquer plataforma de orquestração como o Agent Platform OCI se conecta, com o "MCP Gateway" interno dela substituído pelo nosso PEP/PDP. Ampliado em seguida pelo autor: nosso Service Orchestrator calcula NBA **e NBO** com modelo de decisão pluggable — modularidade + compatibilidade = escolha — habilitando dois modos de integração: A, governança-somente sob um orquestrador externo, e B, substituição completa da camada de decisão externa pelo nosso próprio Orchestrator; ambos demonstrados em EP-13-T05/T06). Também verificado via busca web em 2026-09-11 (não apenas suposto): a revisão MCP 2026-07-28 é real/GA e o SDK Python `mcp` v2 já suporta nativamente os recursos usados em EP-05-T03/T08; OPA está em v1.0+ com Rego v1. Restam abertas G5–G7, G9–G10 (ver 8.3), nenhuma delas bloqueando o início de M0/M1.* diff --git a/docs/research/hoshikawa-agent-platform-oci.md b/docs/research/hoshikawa-agent-platform-oci.md new file mode 100644 index 0000000..fc18c9f --- /dev/null +++ b/docs/research/hoshikawa-agent-platform-oci.md @@ -0,0 +1,135 @@ +# Pesquisa: Agent Platform OCI (Christiano Hoshikawa) como referência Python/LangGraph/Langfuse + +> Fonte: https://github.com/hoshikawa2/agent_platform_oci (README + SPEC-001, 002, 003, 004, 007, 012, 018). Analisado em 2026-09-11 para fechar as lacunas G2/G8 do [`../RI-PLANNING.md`](../RI-PLANNING.md). Referenciado por ADR-022/ADR-023. +> +> **Escopo desta pesquisa**: extrair o padrão *operacional* de LangGraph/Langfuse/MCP daquele projeto para a camada de implementação Python da nossa RI. O núcleo de segurança (PEP/PDP/Client-Bound Entitlement) da nossa arquitetura permanece 100% definido pelo `README.md` na raiz deste repositório — nunca pelo projeto pesquisado aqui. + +--- + +## 1. Padrão LangGraph + +**StateGraph — nós e shape do state.** O grafo por backend é fixo e corporativo: + +```text +START → input_guardrails → routing_decision → [agente(s) de domínio] → output_supervisor + → output_guardrails → judge → supervisor_review → persist → END +routing_decision → handoff → routing_decision (aresta condicional de retorno) +``` + +`AgentState` é um `TypedDict(total=False)` com: `user_text, sanitized_input, response_text, tenant_id, agent_id, channel, session_id, conversation_key, message_id, route, intent, context, business_context, tool_arguments, mcp_tools, mcp_results, rag_context, rag_metadata, guardrails, judges, metadata, errors`, mais `active_transaction`/`last_transaction` na extensão transacional (contrato durável de estado, sobrevive a checkpoint/resume). + +Registrar um novo agente é mecânico: import da classe → instanciar no `__init__` do workflow (recebendo dependências compartilhadas: telemetry, tool_router, rag_service, cache, observer, memory) → wrapper assíncrono com span de telemetria → `builder.add_node(...)` → entrada em `add_conditional_edges("routing_decision", ...)` → ligação ao `output_supervisor`. + +**Duas camadas de roteamento, explicitamente separadas:** +- **Supervisor/router local do backend** — decide o fluxo interno de um agente (`routing.yaml`, `ROUTING_MODE=router|supervisor`). +- **Global Supervisor / Agent Gateway** — serviço deployável separado que decide **qual backend inteiro** trata a conversa (`GLOBAL_ROUTING_MODE=router|supervisor|hybrid`, `backends.yaml`). Roteamento HTTP entre backends, não é um segundo LangGraph. + +**Checkpointing**: providers `memory` (testes), `sqlite` (dev local), `mongodb` (distribuído), `autonomous` (produção OCI). Contrato: `{conversation_key, checkpoint_id, state, pending_writes, created_at}`. + +**Handoff**: intra-backend via aresta condicional `routing_decision → handoff → routing_decision`; inter-backend via `metadata.handover_backend` na resposta, detectado pelo Agent Gateway, que mantém `global_session_id` separado de `backend_session_id`. + +--- + +## 2. Padrão Langfuse + +**Taxonomia IC / NOC / GRL** (não é "Informational Context" — é evento de **negócio/jornada**, **operacional** e **guardrail**, respectivamente): + +| Família | Significado | Exemplos | +|---|---|---| +| IC | evento de negócio/jornada do agente | `IC.GATEWAY_RECEIVED`, `IC.AGENT_STARTED/COMPLETED`, `IC.MCP_CACHE_HIT/MISS/SET/BYPASS/NOT_STORED`, `IC.MCP_TOOL_DEDUPED`, `IC.GLOBAL_BACKEND_SELECTED` | +| NOC | erro/indisponibilidade/timeout/degradação | `NOC.RUNTIME_FAILED`, `NOC.MCP_TIMEOUT`, `NOC.LLM_FAILED` | +| GRL | governança/guardrail/bloqueio/sanitização | `GRL.INPUT_BLOCKED`, `GRL.OUTPUT_BLOCKED`, `GRL.MASK_APPLIED` | + +Regra prática: não criar evento por linha de código — só para decisões relevantes. + +**Instrumentação híbrida**: spans técnicos automáticos por nó do grafo; chamadas LLM capturadas automaticamente via `ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true` (providers compatíveis com OpenAI); eventos de negócio (IC/NOC/GRL) sempre manuais via `_emit_ic/_emit_noc/_emit_grl`, **fail-open** (observer ausente/com erro nunca quebra a jornada). Modo de trace `LANGFUSE_TRACE_MODE=verbose|compact`. + +**Prompt management**: **não usa** o CMS de prompts do Langfuse — versiona prompt localmente em YAML (`prompt_policy.yaml`, campos `id`/`version`). Langfuse entra como fonte de dados para avaliação em lote (`EvaluationRun.source=langfuse`, apps `evals/offline`/`evals/certification`), não como o motor de julgamento síncrono (esse é um nó do próprio grafo, usando profile LLM `judge`). + +**Setup local (docker-compose)**: `langfuse-web`, `langfuse-worker`, PostgreSQL, ClickHouse, Redis, MinIO, mais MongoDB (memória do framework, não do Langfuse). Portas: Web/API 3005, MinIO S3 9090/Console 9091, MongoDB 27017, Postgres 5433, Redis 6379, ClickHouse 8124/9002. Fluxo: `docker compose up -d` → criar org/projeto → gerar par de chaves `pk-lf-.../sk-lf-...` em Settings > API Keys → configurar `.env` do backend. + +--- + +## 3. Padrão de integração MCP + +**MCP Gateway**: app deployável único — `/v1/tools`, `/v1/tools/{name}`, `/v1/tools/{name}/invoke`, `/v1/servers`, `/health`, `/ready`. Contratos `ToolInvocation`/`ToolResult` carregando `tenant_id`, `agent_id`, `business_context`, `metadata.request_id/trace_id`. + +**MCP Parameter Mapping** (`mcp_parameter_mapping.yaml`): traduz chaves canônicas de negócio (`customer_key`, `contract_key`, `session_key`) para o nome de parâmetro esperado por cada tool. + +**Cache MCP com eventos Langfuse**: chave `tenant_id:agent_id:tool_name:hash(arguments)`, só tools idempotentes, TTL por tool. Fluxo de eventos: miss → `IC.MCP_CACHE_MISS → IC.MCP_TOOL_EXECUTING → IC.MCP_TOOL_EXECUTED → IC.MCP_CACHE_SET`; hit → só `IC.MCP_CACHE_HIT`. + +**FastMCP** como padrão de servidor MCP oficial: +```python +from mcp.server.fastmcp import FastMCP +mcp = FastMCP("telecom_mcp_server") + +@mcp.tool() +def consultar_fatura(msisdn: str | None = None) -> dict: ... + +mcp.run(transport="streamable-http") +``` + +### ⚠️ Achado crítico: autorização rasa e potencialmente insegura + +O bloco `authorization` do MCP Gateway deles é um **allowlist estático por `agent_id`**: + +```yaml +authorization: + default_policy: deny + agents: + telecom_contas: + allowed_tools: [consultar_fatura, consultar_pagamentos] +``` + +O campo `agent_id` que governa essa allowlist **chega como dado do payload de entrada da conversa** (`GatewayRequest`/`ToolInvocation`), não como claim de uma identidade de MCP Client autenticada e verificada independentemente. A própria SPEC-004 deles admite: *"a política conversacional não substitui autenticação, autorização, idempotência nem atomicidade no MCP Server."* Não há PDP/OPA/Cedar, não há `policy_decision_id`/`entitlement_version`, não há separação discovery/execution como dois enforcement points deliberadamente independentes. + +**Isto é estruturalmente o anti-padrão que o ADR-001 da nossa RI proíbe** ("autorização por role autodeclarado do agente"). Ver ADR-023 para a decisão de como reaproveitar a engenharia de proxy sem herdar esse problema. + +--- + +## 4. Convenções de configuração YAML + +| Arquivo | Propósito | +|---|---| +| `agents.yaml` | Registro de agentes (agent_id, paths de config isolada, metadados de domínio) | +| `routing.yaml` | Intents, keywords/examples/priority, `mcp_tools` por intent, `state_policies` | +| `tools.yaml` | Catálogo de tools: server destino, schema de args, cache, `allowed_agents` | +| `guardrails.yaml` | Guardrails de entrada/saída habilitáveis por código | +| `judges.yaml` | Critérios de avaliação pós-resposta (deterministic/llm), threshold, sample_rate | +| `llm_profiles.yaml` | Perfis de modelo/temperatura por componente (router, judge, supervisor, RAG) | +| `identity.yaml` | Aliases de nomes de canal → chaves canônicas de negócio (Identity Resolver) | +| `mcp_parameter_mapping.yaml` | Tradução de chave canônica → nome de parâmetro esperado por tool | +| `mcp_servers.yaml` (+ `.docker.yaml`) | Endereços/transporte dos MCP servers (local vs. Docker) | +| `backends.yaml` | Cadastro de backends de agente + regras de roteamento global do Agent Gateway | +| `prompt_policy.yaml` | Prompt base/persistente do agente, versionado (`id`, `version`) | +| `tool_policies.yaml` | Classifica tool como `read_only`/`transactional`, exige confirmação | +| `authorization` (bloco em config do MCP Gateway) | Allowlist de tools por `agent_id` — **não reaproveitar como fonte de autorização** (ver seção 3) | + +--- + +## 5. Modelo de segurança/identidade (SPEC-018) — compatibilidade + +- **Autenticação de workload** (5 modos: config_file/instance_principal/workload_identity/resource_principal/api_key) — trata de como o *framework* se autentica perante recursos OCI (Vault, GenAI, ADB). **Ortogonal** ao nosso problema de entitlement de tools. +- **Autorização** — listada como 5 perguntas a responder, sem PDP formal; na prática resolvida pelo allowlist raso descrito na seção 3. +- **Secrets, proteção de dados, segurança de canal** — compatíveis/complementares, não tocam em entitlement. +- **Auditoria** — bom modelo de trilha (usuário/canal, agent_id, tenant_id, tool, modelo, guardrail, judge score, trace_id), mas sem equivalente a `decisionId`/`policyDecisionId`/`entitlementVersion` (nossos ADR-017), porque não existe PDP separado gerando essas decisões. + +**Onde é claramente compatível/ortogonal**: `identity.yaml`/`BusinessContext` — normalização de identidade de **negócio** (cliente final, contrato), nunca decide autorização de tool. Adotável sem tensão com o núcleo de segurança. + +**Veredito**: SPEC-018 não deve ser usada como spec de segurança normativa para a nossa RI — mistura autenticação de workload com autorização de tool sem separar os dois problemas nem propor um PDP. + +--- + +## 6. Deployment + +Não é amarrado a OCI na camada de aplicação — roda 100% local via Docker Compose com serviços genéricos self-hostáveis (Redis, MongoDB, PostgreSQL, ClickHouse, MinIO, Langfuse). Integrações OCI (GenAI, ADB, Vault, workload identity) são opções plugáveis entre várias, não requisito de desenvolvimento. + +--- + +## Recomendações de reuso (aplicadas na RI via ADR-022/ADR-023) + +**Adotar como estão** (não tocam no núcleo de segurança): `identity.yaml`/BusinessContext · taxonomia IC/NOC/GRL + EventEnvelope · setup local do Langfuse via docker-compose · `ConversationSummaryMemory` · heurística RAG-vs-MCP (§30.3-30.4 do Hoshikawa: sistemas → MCP, documentos → RAG) · `mcp_parameter_mapping.yaml` · FastMCP como padrão de servidor MCP. + +**Adotar com adaptação**: a *forma* de proxy do MCP Gateway (catálogo, `ToolInvocation`/`ToolResult`, cache com eventos, retry/timeout/circuit breaker) — mas a decisão ALLOW/DENY é sempre delegada ao nosso PEP/PDP, nunca ao bloco `authorization` deles. A camada Agent Gateway/Global Supervisor (dois níveis de sessão, handoff) informa o desenho de EP-11/EP-12, mantendo-a estritamente como roteamento de UX/negócio, nunca como autoridade de autorização. `tool_policies.yaml` (read_only/transactional + confirmação) é conveniência conversacional, não controle de segurança. + +**Não copiar como fonte de verdade de segurança**: o bloco `authorization` por `agent_id` autodeclarado no payload; SPEC-018 como normativa de autorização.