From f8d9b6cdcd04ce7d1ae533db92eccf1c860f14ae Mon Sep 17 00:00:00 2001 From: Marcos Lohmann Date: Mon, 14 Sep 2026 14:54:28 -0300 Subject: [PATCH] =?UTF-8?q?revis=C3=A3o=20e=20complementa=C3=A7=C3=A3o=20d?= =?UTF-8?q?a=20documenta=C3=A7=C3=A3o=20do=20projeto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en-us.md | 2 +- README.md | 2 +- RI/docs/AS-BUILT.en-us.md | 2 +- RI/docs/AS-BUILT.md | 2 +- RI/docs/Assumptions.en-us.md | 2 +- RI/docs/Assumptions.md | 2 +- RI/docs/Production-Recommendations.en-us.md | 2 +- RI/docs/Production-Recommendations.md | 2 +- .../ADR-001-agent-privilege-ceiling.en-us.md | 20 +++ ...DR-002-enforcement-at-the-gateway.en-us.md | 20 +++ ...003-reauthorization-of-tools-call.en-us.md | 20 +++ ...-context-never-elevates-privilege.en-us.md | 20 +++ ...-005-private-cache-for-tools-list.en-us.md | 20 +++ .../adr/ADR-006-no-token-passthrough.en-us.md | 20 +++ ...-007-backend-not-directly-exposed.en-us.md | 20 +++ ...8-segmentation-by-domain-and-risk.en-us.md | 20 +++ ...R-009-risk-tiers-for-capabilities.en-us.md | 20 +++ docs/adr/ADR-010-registry-governance.en-us.md | 20 +++ docs/adr/ADR-011-publishing-pipeline.en-us.md | 20 +++ ...-entitlement-manager-as-authority.en-us.md | 20 +++ ...013-profile-intelligence-contract.en-us.md | 20 +++ ...profile-never-expands-entitlement.en-us.md | 20 +++ .../ADR-015-nba-is-not-authorization.en-us.md | 20 +++ ...ADR-016-dispatch-handoff-contract.en-us.md | 20 +++ .../ADR-017-end-to-end-correlation.en-us.md | 20 +++ .../adr/ADR-018-implementation-stack.en-us.md | 58 ++++++++ docs/adr/ADR-019-extensao-de-identidade.md | 106 ++++++++++++++ ...md => ADR-019-identity-extension.en-us.md} | 0 docs/adr/ADR-020-downstream-identity.en-us.md | 20 +++ .../ADR-021-langgraph-langfuse-scope.en-us.md | 20 +++ ...ence-hoshikawa-agent-platform-oci.en-us.md | 20 +++ ...rization-never-comes-from-payload.en-us.md | 20 +++ .../ADR-024-multiagent-positioning.en-us.md | 20 +++ docs/adr/ADR-025-pdp-engine.en-us.md | 20 +++ .../hoshikawa-agent-platform-oci.en-us.md | 135 ++++++++++++++++++ 35 files changed, 767 insertions(+), 8 deletions(-) create mode 100644 docs/adr/ADR-001-agent-privilege-ceiling.en-us.md create mode 100644 docs/adr/ADR-002-enforcement-at-the-gateway.en-us.md create mode 100644 docs/adr/ADR-003-reauthorization-of-tools-call.en-us.md create mode 100644 docs/adr/ADR-004-context-never-elevates-privilege.en-us.md create mode 100644 docs/adr/ADR-005-private-cache-for-tools-list.en-us.md create mode 100644 docs/adr/ADR-006-no-token-passthrough.en-us.md create mode 100644 docs/adr/ADR-007-backend-not-directly-exposed.en-us.md create mode 100644 docs/adr/ADR-008-segmentation-by-domain-and-risk.en-us.md create mode 100644 docs/adr/ADR-009-risk-tiers-for-capabilities.en-us.md create mode 100644 docs/adr/ADR-010-registry-governance.en-us.md create mode 100644 docs/adr/ADR-011-publishing-pipeline.en-us.md create mode 100644 docs/adr/ADR-012-entitlement-manager-as-authority.en-us.md create mode 100644 docs/adr/ADR-013-profile-intelligence-contract.en-us.md create mode 100644 docs/adr/ADR-014-profile-never-expands-entitlement.en-us.md create mode 100644 docs/adr/ADR-015-nba-is-not-authorization.en-us.md create mode 100644 docs/adr/ADR-016-dispatch-handoff-contract.en-us.md create mode 100644 docs/adr/ADR-017-end-to-end-correlation.en-us.md create mode 100644 docs/adr/ADR-018-implementation-stack.en-us.md create mode 100644 docs/adr/ADR-019-extensao-de-identidade.md rename docs/adr/{ADR-019-identity-extension.md => ADR-019-identity-extension.en-us.md} (100%) create mode 100644 docs/adr/ADR-020-downstream-identity.en-us.md create mode 100644 docs/adr/ADR-021-langgraph-langfuse-scope.en-us.md create mode 100644 docs/adr/ADR-022-reference-hoshikawa-agent-platform-oci.en-us.md create mode 100644 docs/adr/ADR-023-authorization-never-comes-from-payload.en-us.md create mode 100644 docs/adr/ADR-024-multiagent-positioning.en-us.md create mode 100644 docs/adr/ADR-025-pdp-engine.en-us.md create mode 100644 docs/research/hoshikawa-agent-platform-oci.en-us.md diff --git a/README.en-us.md b/README.en-us.md index 66049b2..d47ff9f 100644 --- a/README.en-us.md +++ b/README.en-us.md @@ -2672,7 +2672,7 @@ The architectural vision is consistent, but a concrete implementation needs to d - workload identity? - mTLS? - cloud IAM? - - → **Decision adopted in the RI**: OAuth 2.1 `client_credentials` via Keycloak, with mTLS/workload identity documented as a production extension not implemented (`docs/adr/ADR-019-identity-extension.md`). + - → **Decision adopted in the RI**: OAuth 2.1 `client_credentials` via Keycloak, with mTLS/workload identity documented as a production extension not implemented (`docs/adr/ADR-019-extensao-de-identidade.md`). 2. **What will be the granularity of client profiles?** - domain? diff --git a/README.md b/README.md index efc5392..78b8ab1 100644 --- a/README.md +++ b/README.md @@ -2672,7 +2672,7 @@ 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/adr/ADR-019-identity-extension.md`). + - → **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/adr/ADR-019-extensao-de-identidade.md`). 2. **Qual será a granularidade dos client profiles?** - domínio? diff --git a/RI/docs/AS-BUILT.en-us.md b/RI/docs/AS-BUILT.en-us.md index f93a7c3..49edbe7 100644 --- a/RI/docs/AS-BUILT.en-us.md +++ b/RI/docs/AS-BUILT.en-us.md @@ -77,7 +77,7 @@ local JWKS HTTP server as a stand-in for the rest of the e2e suite (valid for va not for real protocol integration with the IdP). **Production extension documented, not implemented:** mTLS/workload identity — -`docs/adr/ADR-019-identity-extension.md`. +`docs/adr/ADR-019-extensao-de-identidade.md`. ### PDP / Policy Engine (EP-03) diff --git a/RI/docs/AS-BUILT.md b/RI/docs/AS-BUILT.md index 232f206..802440a 100644 --- a/RI/docs/AS-BUILT.md +++ b/RI/docs/AS-BUILT.md @@ -75,7 +75,7 @@ HTTP local mais leve como stand-in para o resto da suíte e2e (válido para a l não para a integração de protocolo real com o IdP). **Extensão de produção documentada, não implementada:** mTLS/workload identity — -`docs/adr/ADR-019-identity-extension.md`. +`docs/adr/ADR-019-extensao-de-identidade.md`. ### PDP / Policy Engine (EP-03) diff --git a/RI/docs/Assumptions.en-us.md b/RI/docs/Assumptions.en-us.md index cf8c08a..932dce8 100644 --- a/RI/docs/Assumptions.en-us.md +++ b/RI/docs/Assumptions.en-us.md @@ -24,7 +24,7 @@ point being tested. and adherent to open standards** (OAuth 2.1/OIDC, Rego), not out of arbitrary preference. This resolves two gaps that the reference architecture left open (`README.md` §48, questions 1 and 3) with a definitive technical decision, not an assumption — they were never revisited -throughout the implementation. See `docs/adr/ADR-019-identity-extension.md`, +throughout the implementation. See `docs/adr/ADR-019-extensao-de-identidade.md`, `docs/adr/ADR-025-engine-do-pdp.md`. **We rejected** a custom PDP engine in Python (more control, but reinvents policy evaluation diff --git a/RI/docs/Assumptions.md b/RI/docs/Assumptions.md index e3e03b7..37337e8 100644 --- a/RI/docs/Assumptions.md +++ b/RI/docs/Assumptions.md @@ -24,7 +24,7 @@ próprio ponto que estava sendo testado. aderentes a padrões abertos** (OAuth 2.1/OIDC, Rego), não por preferência arbitrária. Isso resolve duas lacunas que a arquitetura de referência deixava abertas (`README.md` §48, perguntas 1 e 3) com uma decisão técnica definitiva, não uma suposição — nunca foram revisitadas ao longo -da implementação. Ver `docs/adr/ADR-019-identity-extension.md`, +da implementação. Ver `docs/adr/ADR-019-extensao-de-identidade.md`, `docs/adr/ADR-025-engine-do-pdp.md`. **Rejeitamos** um engine de PDP próprio em Python (mais controle, mas reinventa avaliação de diff --git a/RI/docs/Production-Recommendations.en-us.md b/RI/docs/Production-Recommendations.en-us.md index 43631e0..41b71d1 100644 --- a/RI/docs/Production-Recommendations.en-us.md +++ b/RI/docs/Production-Recommendations.en-us.md @@ -13,7 +13,7 @@ Target audience: whoever is going to **evaluate or adapt** this RI for a real sc A real, highly available IdP (Keycloak cluster, or a managed equivalent), not the single instance from `docker-compose.yml`. The extension to mTLS/workload identity — covering cases where `client_credentials` is not strong enough (e.g.: workloads in a service mesh) — is -already documented as an extension point in `docs/adr/ADR-019-identity-extension.md`; production +already documented as an extension point in `docs/adr/ADR-019-extensao-de-identidade.md`; production would implement that extension, not reinvent the authentication middleware from scratch. ## PDP diff --git a/RI/docs/Production-Recommendations.md b/RI/docs/Production-Recommendations.md index 682520f..e3c1a0f 100644 --- a/RI/docs/Production-Recommendations.md +++ b/RI/docs/Production-Recommendations.md @@ -13,7 +13,7 @@ Público-alvo: quem vai **avaliar ou adaptar** esta RI para um cenário real. Um IdP real em alta disponibilidade (cluster Keycloak, ou equivalente gerenciado), não a instância única do `docker-compose.yml`. A extensão para mTLS/workload identity — cobrindo casos em que `client_credentials` não é forte o suficiente (ex.: workloads em malha de serviço) — já -está documentada como ponto de extensão em `docs/adr/ADR-019-identity-extension.md`; produção +está documentada como ponto de extensão em `docs/adr/ADR-019-extensao-de-identidade.md`; produção implementaria essa extensão, não reinventaria o middleware de autenticação do zero. ## PDP diff --git a/docs/adr/ADR-001-agent-privilege-ceiling.en-us.md b/docs/adr/ADR-001-agent-privilege-ceiling.en-us.md new file mode 100644 index 0000000..e1fe43c --- /dev/null +++ b/docs/adr/ADR-001-agent-privilege-ceiling.en-us.md @@ -0,0 +1,20 @@ +# ADR-001: MaximumEntitlement Boundary at the MCP Client + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Who defines the agent's privilege ceiling. + +## Decision + +MCP Client (authenticated identity) is the `MaximumEntitlement` boundary, never the agent/prompt. + +## Alternatives Considered + +Authorization by self-declared role of the agent. + +## Consequences + +All EP-01/EP-04 design revolves around the client's identity, not the agent's. diff --git a/docs/adr/ADR-002-enforcement-at-the-gateway.en-us.md b/docs/adr/ADR-002-enforcement-at-the-gateway.en-us.md new file mode 100644 index 0000000..3ec36e2 --- /dev/null +++ b/docs/adr/ADR-002-enforcement-at-the-gateway.en-us.md @@ -0,0 +1,20 @@ +# ADR-002: MCP Gateway as the sole PEP + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Where to apply enforcement. + +## Decision + +MCP Gateway is the sole PEP; it never delegates the ALLOW/DENY decision to the LLM. + +## Alternatives Considered + +Distributed enforcement in each MCP Server. + +## Consequences + +Centralizes EP-05; domain servers (EP-06) do not reimplement authz. diff --git a/docs/adr/ADR-003-reauthorization-of-tools-call.en-us.md b/docs/adr/ADR-003-reauthorization-of-tools-call.en-us.md new file mode 100644 index 0000000..55983b7 --- /dev/null +++ b/docs/adr/ADR-003-reauthorization-of-tools-call.en-us.md @@ -0,0 +1,20 @@ +# ADR-003: Independent reauthorization on tools/call + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Is filtered `tools/list` sufficient? + +## Decision + +No — `tools/call` must be reauthorized independently of discovery. + +## Alternatives Considered + +Relying only on filtered discovery. + +## Consequences + +EP-05 implements two enforcement points, not one. diff --git a/docs/adr/ADR-004-context-never-elevates-privilege.en-us.md b/docs/adr/ADR-004-context-never-elevates-privilege.en-us.md new file mode 100644 index 0000000..a02d21e --- /dev/null +++ b/docs/adr/ADR-004-context-never-elevates-privilege.en-us.md @@ -0,0 +1,20 @@ +# ADR-004: Agentic context never elevates privilege + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Can agentic context elevate privilege? + +## Decision + +Never — context can only subtract (`Context may subtract, must never add`). + +## Alternatives Considered + +Allowing elevation based on the model's "confidence." + +## Consequences + +EP-15 tests (Test 3/4) validate the invariant. diff --git a/docs/adr/ADR-005-private-cache-for-tools-list.en-us.md b/docs/adr/ADR-005-private-cache-for-tools-list.en-us.md new file mode 100644 index 0000000..116a8c1 --- /dev/null +++ b/docs/adr/ADR-005-private-cache-for-tools-list.en-us.md @@ -0,0 +1,20 @@ +# ADR-005: Private cache for tools/list + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +`tools/list` cache shared across clients. + +## Decision + +`cacheScope=private`, key = identity+context+policy_version. + +## Alternatives Considered + +Shared cache for performance. + +## Consequences + +EP-05-T03; dedicated isolation test (EP-15). diff --git a/docs/adr/ADR-006-no-token-passthrough.en-us.md b/docs/adr/ADR-006-no-token-passthrough.en-us.md new file mode 100644 index 0000000..e23397b --- /dev/null +++ b/docs/adr/ADR-006-no-token-passthrough.en-us.md @@ -0,0 +1,20 @@ +# ADR-006: Prohibition of token passthrough + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Can the agent's token be forwarded to the backend? + +## Decision + +No — blind token passthrough is prohibited; inbound ≠ outbound identity. + +## Alternatives Considered + +Direct passthrough of the client's token. + +## Consequences + +EP-07 implements exchange/service account per backend. diff --git a/docs/adr/ADR-007-backend-not-directly-exposed.en-us.md b/docs/adr/ADR-007-backend-not-directly-exposed.en-us.md new file mode 100644 index 0000000..e72a4d8 --- /dev/null +++ b/docs/adr/ADR-007-backend-not-directly-exposed.en-us.md @@ -0,0 +1,20 @@ +# ADR-007: Backend never exposed directly to the client + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Is the backend directly accessible by the client? + +## Decision + +No — network/identity restrict access to the Gateway/Fabric only. + +## Alternatives Considered + +Exposing the Fabric publicly with "best effort" authz. + +## Consequences + +EP-05-T07, EP-15 (bypass test). diff --git a/docs/adr/ADR-008-segmentation-by-domain-and-risk.en-us.md b/docs/adr/ADR-008-segmentation-by-domain-and-risk.en-us.md new file mode 100644 index 0000000..892b38d --- /dev/null +++ b/docs/adr/ADR-008-segmentation-by-domain-and-risk.en-us.md @@ -0,0 +1,20 @@ +# ADR-008: Client segmentation by domain and risk tier + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +How to segment clients. + +## Decision + +By domain + risk tier (R0–R4), not a single global client. + +## Alternatives Considered + +A single "enterprise admin" client. + +## Consequences + +Defines P6 (granularity) and EP-04. diff --git a/docs/adr/ADR-009-risk-tiers-for-capabilities.en-us.md b/docs/adr/ADR-009-risk-tiers-for-capabilities.en-us.md new file mode 100644 index 0000000..722804f --- /dev/null +++ b/docs/adr/ADR-009-risk-tiers-for-capabilities.en-us.md @@ -0,0 +1,20 @@ +# ADR-009: Risk tiers R0-R4 for capabilities + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +How to classify capabilities. + +## Decision + +Risk tiers R0–R4 in the Registry, controlling required approval/observability. + +## Alternatives Considered + +No formal classification. + +## Consequences + +EP-02-T01 includes mandatory `risk_tier` in the manifest schema. diff --git a/docs/adr/ADR-010-registry-governance.en-us.md b/docs/adr/ADR-010-registry-governance.en-us.md new file mode 100644 index 0000000..cfd52a3 --- /dev/null +++ b/docs/adr/ADR-010-registry-governance.en-us.md @@ -0,0 +1,20 @@ +# ADR-010: Governance of the Global Capability Registry + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Governance of the Global Capability Registry (title and number inherited from §49 of the README — "Global Capability Registry Governance"). + +## Decision + +The Registry is a control-plane asset (§6.6) with its own authority and lifecycle, separate from the catalog view delivered to each client. In the RI: dedicated storage and read API (EP-02-T03), consumed by Gateway/Fabric/Offering Filter but never edited by them; formal lifecycle nominate→review→register→publish→observe→(change|deprecate→retire) (§23) implemented as a state machine (EP-02-T04); every change goes through the publishing pipeline (EP-02-T02) — no direct manual registration in production (RF-11). The Registry's organizational owner (who approves schema/policy changes to the control plane itself) remains a corporate governance decision outside the technical scope of the RI (§48, question 4, still open). + +## Alternatives Considered + +Leaving the Registry as a table editable ad hoc by any service; merging the control view (Registry) with the execution view (catalog filtered per client), which would violate the control plane/data plane separation of §24. + +## Consequences + +The entire EP-02; reinforces that EP-05 (Gateway) and EP-10 (Offering Filter) only read the Registry, never write to it. diff --git a/docs/adr/ADR-011-publishing-pipeline.en-us.md b/docs/adr/ADR-011-publishing-pipeline.en-us.md new file mode 100644 index 0000000..a012ea7 --- /dev/null +++ b/docs/adr/ADR-011-publishing-pipeline.en-us.md @@ -0,0 +1,20 @@ +# ADR-011: Mandatory pipeline for capability publication + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Capability publication cycle. + +## Decision + +Mandatory pipeline with schema/risk/policy/health gates before entering the Registry — no manual registration in production. + +## Alternatives Considered + +Direct registration via administrative API without a pipeline. + +## Consequences + +EP-02-T02 implements the gate; no capability is "born" already published. diff --git a/docs/adr/ADR-012-entitlement-manager-as-authority.en-us.md b/docs/adr/ADR-012-entitlement-manager-as-authority.en-us.md new file mode 100644 index 0000000..990d05f --- /dev/null +++ b/docs/adr/ADR-012-entitlement-manager-as-authority.en-us.md @@ -0,0 +1,20 @@ +# ADR-012: Entitlement Manager as the sole authority for MaximumEntitlement + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, where applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Authority over the maximum menu. + +## Decision + +Entitlement Manager is the sole authority for `MaximumEntitlement`; it does not rank nor trust self-declared attributes from the LLM. + +## Alternatives Considered + +Merging Entitlement Manager and Offering Filter into a single component. + +## Consequences + +Keeps EP-04 and EP-10 as distinct services/modules with their own contracts. diff --git a/docs/adr/ADR-013-profile-intelligence-contract.en-us.md b/docs/adr/ADR-013-profile-intelligence-contract.en-us.md new file mode 100644 index 0000000..a56948d --- /dev/null +++ b/docs/adr/ADR-013-profile-intelligence-contract.en-us.md @@ -0,0 +1,20 @@ +# ADR-013: Pluggable Profile Intelligence contract + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Profile Intelligence contract. + +## Decision + +Generic, replaceable interface (versioned `ProfileView`, with `reasonCodes`), pluggable implementation. + +## Alternatives Considered + +Coupling to a specific vendor/taxonomy for segmentation. + +## Consequences + +EP-09 defines only the contract + reference stub (see P7). diff --git a/docs/adr/ADR-014-profile-never-expands-entitlement.en-us.md b/docs/adr/ADR-014-profile-never-expands-entitlement.en-us.md new file mode 100644 index 0000000..2081767 --- /dev/null +++ b/docs/adr/ADR-014-profile-never-expands-entitlement.en-us.md @@ -0,0 +1,20 @@ +# ADR-014: Profile never expands entitlement + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Can profile expand entitlement? + +## Decision + +Never — `FilteredOfferings ⊆ MaximumEntitlement`, profile only orders/reduces. + +## Alternatives Considered + +High-engagement profile unlocking new tools. + +## Consequences + +EP-10-T02 brings a property test validating the invariant for random inputs. diff --git a/docs/adr/ADR-015-nba-is-not-authorization.en-us.md b/docs/adr/ADR-015-nba-is-not-authorization.en-us.md new file mode 100644 index 0000000..ba6b4ee --- /dev/null +++ b/docs/adr/ADR-015-nba-is-not-authorization.en-us.md @@ -0,0 +1,20 @@ +# ADR-015: NBA as an orchestration decision, not an authorization decision + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Is NBA an authorization decision? + +## Decision + +No — it is orchestration intent; Gateway/PDP reauthorize every execution. + +## Alternatives Considered + +Orchestrator dispatching directly to the backend without going through the Gateway. + +## Consequences + +EP-11-T04 implements the DENY/recalculation branch of §10.1. diff --git a/docs/adr/ADR-016-dispatch-handoff-contract.en-us.md b/docs/adr/ADR-016-dispatch-handoff-contract.en-us.md new file mode 100644 index 0000000..0db6096 --- /dev/null +++ b/docs/adr/ADR-016-dispatch-handoff-contract.en-us.md @@ -0,0 +1,20 @@ +# ADR-016: Agent Runtime dispatch/handoff contract + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Agent Runtime dispatch/handoff contract. + +## Decision + +Versioned dispatch object (`decisionId`, `expiresAt`, `reasonCodes`) that does not replace the MCP Client's token. + +## Alternatives Considered + +Runtime injecting its own credentials into the agent. + +## Consequences + +EP-12-T01 consumes exclusively the dispatch object + the already-existing MCP Client. diff --git a/docs/adr/ADR-017-end-to-end-correlation.en-us.md b/docs/adr/ADR-017-end-to-end-correlation.en-us.md new file mode 100644 index 0000000..448359e --- /dev/null +++ b/docs/adr/ADR-017-end-to-end-correlation.en-us.md @@ -0,0 +1,20 @@ +# ADR-017: End-to-end correlation via versioned IDs + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +End-to-end correlation. + +## Decision + +`profileVersion`+`entitlementVersion`+`decisionId`+`policyDecisionId`+`mcpRequestId` propagated via OTel/trace context across all components. + +## Alternatives Considered + +Correlation only via free-text logs with no standard. + +## Consequences + +EP-08-T03 formalizes the fields; basis for EP-15 (auditability). diff --git a/docs/adr/ADR-018-implementation-stack.en-us.md b/docs/adr/ADR-018-implementation-stack.en-us.md new file mode 100644 index 0000000..47cab8c --- /dev/null +++ b/docs/adr/ADR-018-implementation-stack.en-us.md @@ -0,0 +1,58 @@ +# ADR-018: RI implementation stack + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +RI implementation stack (closes G2). + +## Decision + +Python 3.12+, `uv`, Pydantic v2, declarative YAML with JSON Schema, official `mcp` SDK. + +## Alternatives Considered + +Node/TypeScript (the MCP SDK is also official there); Go. + +## Consequences + +Defines the entire repository structure (8.5). + +## Confirmed Versions (EP-16-T04) + +Dependency-free spike, runnable at any time (EP-16-T04): confirm, +immediately before pinning in `pyproject.toml`/ +`docker-compose.yml`, the exact stable version numbers of each piece of the +stack — no version cited in code/config without real confirmation +(`pip index versions`/direct download/actual install), never from memory. +Consolidated record, with the date each group was verified (see +`RI/README.md`, "Versões fixadas", for the same list maintained alongside +`pyproject.toml`): + +Confirmed on 2026-09-11: + +- `mcp` 2.2.0 (Python SDK v2 — the API changed significantly from v1; see + comments in `src/emcp_bus/gateway/server.py`) +- `pydantic` 2.13.x, `opentelemetry-sdk` 1.44.x, `uvicorn` 0.52.x, + `pyjwt[crypto]` 2.14.x, `ruff` 0.16.x, `mypy` 2.3.x, `pytest` 9.1.x +- `OPA` 1.20.2 (static binary, Rego v1 syntax as default) +- `pytest-forked` 1.7.x + +Confirmed on 2026-09-14 (M3/M4, via actual install/`docker pull`): + +- `langgraph` 0.6.x + `langgraph-checkpoint-sqlite` 2.x (EP-11) +- `hypothesis` 6.1xx.x (dev, EP-10-T02) +- `openai` 1.109.x (EP-13-T01) +- `python-dotenv` 1.2.x (dev) +- `types-jsonschema` 4.26.x (dev, stubs for `mypy --strict`; `jsonschema` + itself was already a transitive dependency of `mcp[cli]`, EP-15-T01) +- Langfuse self-hosted 4.35.0 (`docker.langfuse.com/langfuse/langfuse:4`, + official image, EP-13-T02) +- Keycloak 26.7.3 (`quay.io/keycloak/keycloak:26.7.3`, Quarkus; Standard + Token Exchange GA since 26.2 — EP-01-T01/EP-07) +- `opentelemetry-collector-contrib` 0.114.0 and `jaeger` (all-in-one) 1.62.0 + (`observability` profile of `docker-compose.yml`, EP-08-T04) + +None of the versions above have changed since the original confirmation — this +record is the single source of truth that `RI/README.md` links back to. diff --git a/docs/adr/ADR-019-extensao-de-identidade.md b/docs/adr/ADR-019-extensao-de-identidade.md new file mode 100644 index 0000000..d114941 --- /dev/null +++ b/docs/adr/ADR-019-extensao-de-identidade.md @@ -0,0 +1,106 @@ +# ADR-019: Identidade do MCP Client -- ponto de extensão de produção (mTLS / workload identity) + +**Status:** Documentado (spike), não implementado na RI (EP-01-T04, `could`). +**Fonte:** este ADR é o registro canônico desta decisão. Para contexto de implementação e validação, ver `RI/docs/AS-BUILT.md` e, quando aplicável, o contrato relevante em `RI/docs/interfaces/identity-oidc.md`. Também relevante: README.md §19, RF-02, Axiom 2. + +## Contexto + +A fronteira de identidade da RI (`src/emcp_bus/identity/authn.py`, EP-01-T03) é +OAuth 2.1 `client_credentials` contra o Keycloak (EP-01-T01): todo MCP Client +se autentica com um client_id + secret, obtém um JWT de vida curta, e o +Gateway valida a assinatura/emissor/audience/expiração desse JWT contra o +JWKS do IdP antes de confiar na claim `azp` como a identidade do caller. + +Esta é uma decisão deliberada e confirmada para a RI (não um placeholder): OAuth +2.1 satisfaz toda propriedade que o README.md §19 de fato exige de um mecanismo +de identidade -- vida curta, revogável, vinculado a uma audience, verificável +sem um segredo de longo prazo compartilhado no caminho da requisição. +Implantações corporativas reais comumente vão além, usando mTLS (certificados +de cliente TLS mútuo) ou a federação de workload identity de um provedor de +nuvem (ex.: SPIFFE/SPIRE, AWS IAM roles for service accounts, GCP Workload +Identity, Azure Managed Identity) em vez de -- ou além de -- um JWT bearer. +Este ADR documenta como essa extensão se encaixa na costura já existente da +RI, sem exigi-la, para que um fork de produção não fique bloqueado por um +redesenho. + +## Decisão + +**Não** implementar mTLS/workload identity na RI. Documentar o ponto de +extensão em vez disso, para que o contrato do `EP-01-T03` seja +comprovadamente estável sob ele. + +## O ponto de extensão + +`TokenValidator.validate(token: str) -> AuthenticatedClient` é a *única* +função na RI que produz identidade de client para fins de autorização +(`emcp_bus.gateway.server.on_list_tools`/`on_call_tool` chamam ambos +`get_access_token()`, que é populado pelo protocolo `TokenVerifier` do SDK +envolvendo esse mesmo validator -- ver +`emcp_bus.identity.token_verifier.MCPTokenVerifier`). Tudo a jusante dela -- +`EntitlementManager.resolve`, o PDP, a camada de audit/correlação (EP-08) -- +consome apenas o `AuthenticatedClient.client_id` resultante. Nenhum desse +código se importa com *como* a identidade foi estabelecida. + +Uma implantação de produção adicionando mTLS ou workload identity faria: + +1. Implementar uma segunda fonte de identidade honrando o mesmo contrato de saída: + + ```python + class MTLSClientValidator: + def validate(self, request: ...) -> AuthenticatedClient: ... + ``` + + alimentada pelo certificado de cliente já verificado pelo proxy/load + balancer que termina a conexão (um header estilo `X-Client-Cert-CN` de um + terminador mTLS confiável, ou o `scope["extensions"]["tls"]` ASGI que um + servidor compliant expõe) em vez de um JWT. `AuthenticatedClient.client_id` + seria o Subject/SAN do certificado, mapeado 1:1 a um + `ClientRegistration.client_id` exatamente da mesma forma que o `azp` de um + JWT é hoje -- **nenhum outro modelo da RI muda**: `ClientRegistration`, + `ClientProfile`, `EntitlementManager`, o contrato do PDP e o schema de + auditoria já são todos agnósticos de mecanismo de identidade. + +2. Fornecer um equivalente alternativo de `build_auth()` em + `emcp_bus/gateway/server.py` que conecta o novo validator no lugar de + `MCPTokenVerifier`, controlado por configuração de deployment -- o mesmo + formato do `build_auth()` de hoje retornando `(None, None)` para o modo + "sem OIDC configurado". Uma implantação híbrida (mTLS para uma classe de + backend service, OAuth para agentes interativos) é possível fazendo a + seleção de `TokenVerifier`/equivalente por listener em vez de por + processo, o que os parâmetros + `streamable_http_app(auth=..., token_verifier=...)` do SDK já suportam + por instância de app. + +3. Workload identity de nuvem (SPIFFE/SPIRE, IRSA, Workload Identity, Managed + Identity) é uma variante da mesma forma: o contrato "verificar uma + credencial que o caller não pode forjar, e extrair dela um identificador + estável" é idêntico, só o formato da credencial e a biblioteca de + verificação diferem. Nada disso exige tocar no `EntitlementManager`, no + PDP, no roteador do Fabric (EP-06-T01), ou na camada de audit/correlação + (EP-08-T02/T03). + +## O que não pode mudar + +- **Formato de `AuthenticatedClient`** (`client_id`, `issuer`, `expires_at`): + todo consumidor a jusante tipa contra isso, não contra um conjunto de + claims específico de JWT. +- **Contrato fail-closed**: o caminho de falha de qualquer mecanismo de + identidade deve levantar uma exceção (no formato `AuthenticationError`) em + vez de recair para uma identidade não autenticada ou autodeclarada pelo + agente (README.md §5, Axiom 2) -- essa é a propriedade de segurança real + que o critério de aceite do EP-01-T03 testa, e ela é independente de + mecanismo. +- **Nenhum caminho privilegiado novo para `clientInfo`**: qualquer que seja o + mecanismo de identidade, apenas um valor verificado de forma independente + do corpo da requisição MCP pode popular `client_id`. + +## Consequências + +- Confirmado para a RI: reduz o esforço de implementação do EP-01 sem + enfraquecer nenhuma das propriedades de identidade desejáveis do README.md + §19 (todas satisfeitas por OAuth 2.1 `client_credentials` contra o + Keycloak). +- Um fork de produção adicionando mTLS/workload identity muda exatamente um + módulo (`identity/`) e uma função de wiring (equivalente a `build_auth`); + todo outro componente da RI (Entitlement Manager, PDP, Fabric, Registry, + audit/correlação, approval) não exige nenhuma mudança. diff --git a/docs/adr/ADR-019-identity-extension.md b/docs/adr/ADR-019-identity-extension.en-us.md similarity index 100% rename from docs/adr/ADR-019-identity-extension.md rename to docs/adr/ADR-019-identity-extension.en-us.md diff --git a/docs/adr/ADR-020-downstream-identity.en-us.md b/docs/adr/ADR-020-downstream-identity.en-us.md new file mode 100644 index 0000000..d120f56 --- /dev/null +++ b/docs/adr/ADR-020-downstream-identity.en-us.md @@ -0,0 +1,20 @@ +# ADR-020: Downstream identity: token exchange or isolated service account + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Downstream identity in the RI (closes G5/P5). + +## Decision + +Token exchange (RFC 8693) when the backend supports OIDC; otherwise a service account isolated per adapter. + +## Alternatives Considered + +Always reusing the client's token (forbidden by ADR-006). + +## Consequences + +EP-07; documented per backend in the manifest. diff --git a/docs/adr/ADR-021-langgraph-langfuse-scope.en-us.md b/docs/adr/ADR-021-langgraph-langfuse-scope.en-us.md new file mode 100644 index 0000000..475b9fc --- /dev/null +++ b/docs/adr/ADR-021-langgraph-langfuse-scope.en-us.md @@ -0,0 +1,20 @@ +# ADR-021: Scope of LangGraph and Langfuse in the RI + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Scope of LangGraph/Langfuse (closes G8/P8). + +## Decision + +LangGraph only in the Service Orchestrator (EP-11) and the Agent Runtime handoff (EP-12); Langfuse only where there is an actual LLM call (EP-13). Checkpointer: SQLite in dev. + +## Alternatives Considered + +Also using LangGraph in the Gateway/Fabric for "stack consistency." + +## Consequences + +Avoids the over-engineering vetoed by section 6 of the superprompt; Gateway/Fabric remain plain Python. diff --git a/docs/adr/ADR-022-reference-hoshikawa-agent-platform-oci.en-us.md b/docs/adr/ADR-022-reference-hoshikawa-agent-platform-oci.en-us.md new file mode 100644 index 0000000..c19d1c0 --- /dev/null +++ b/docs/adr/ADR-022-reference-hoshikawa-agent-platform-oci.en-us.md @@ -0,0 +1,20 @@ +# ADR-022: Operational reference: Agent Platform OCI (Hoshikawa) + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Concrete Python/LangGraph/Langfuse implementation reference (closes G2 — **decision confirmed by the author on 2026-09-11**). + +## Decision + +Adopt as an operational reference the "Agent Platform OCI" by Christiano Hoshikawa (`github.com/hoshikawa2/agent_platform_oci`): a StateGraph with a router/supervisor per backend + an upper routing layer across backends (our EP-11/EP-12); pluggable checkpointing (memory/sqlite/mongodb/production — we adopt SQLite in dev, see ADR-021); **IC/NOC/GRL** observability taxonomy via Langfuse (business/operational/guardrail event), with hybrid instrumentation (automatic span per node + manual fail-open emission); `identity.yaml`/`BusinessContext` to normalize business identity across channels; FastMCP as the standard MCP server. Full analysis recorded in `docs/research/hoshikawa-agent-platform-oci.md`. + +## Alternatives Considered + +Continuing with the generic pattern from section 6 of the superprompt without a concrete third-party reference (slower, more ad hoc decisions); adopting a market agent framework (CrewAI, AutoGen) instead of plain LangGraph. + +## Consequences + +Changes the form (not the security) of EP-08, EP-11, EP-12, EP-13 — see ADR-023 for the explicit boundary of what is NOT adopted. diff --git a/docs/adr/ADR-023-authorization-never-comes-from-payload.en-us.md b/docs/adr/ADR-023-authorization-never-comes-from-payload.en-us.md new file mode 100644 index 0000000..3bb64f4 --- /dev/null +++ b/docs/adr/ADR-023-authorization-never-comes-from-payload.en-us.md @@ -0,0 +1,20 @@ +# ADR-023: Tool authorization never comes from a field declared in the payload + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +Tool authorization NEVER comes from a field declared in the payload/conversation (a critical finding from the Hoshikawa analysis, reinforcing ADR-001/002/003). + +## Decision + +The Agent Platform OCI's MCP Gateway authorizes tools via a static allowlist (`tools.yaml`/`authorization.agents..allowed_tools`) evaluated over `agent_id`, a field that arrives as **data from the conversation's input payload**, not as a claim from an independently verified authenticated identity — their own SPEC-004 admits that this does not replace real authentication/authorization. Our RI reuses the proxy *form* of this MCP Gateway (`ToolInvocation`/`ToolResult` contracts, idempotent per-tool cache, `/v1/tools` catalog) **exclusively at the Fabric layer (EP-06)/conversational UX**, but the ALLOW/DENY decision remains 100% in our PEP (EP-05) consulting the PDP (EP-03) with the authenticated identity of the MCP Client (EP-01) — never with a self-declared `agent_id`/`tenant_id` in the payload. + +## Alternatives Considered + +Also reusing their `authorization` block as an implementation shortcut. + +## Consequences + +EP-15-T03 gains an explicit adversarial "payload-declared agent_id spoofing" scenario (a variation of Test 4, §45) to prove that this specific vector is closed in our RI. diff --git a/docs/adr/ADR-024-multiagent-positioning.en-us.md b/docs/adr/ADR-024-multiagent-positioning.en-us.md new file mode 100644 index 0000000..2a38880 --- /dev/null +++ b/docs/adr/ADR-024-multiagent-positioning.en-us.md @@ -0,0 +1,20 @@ +# ADR-024: E-MCP-BUS positioning relative to external multi-agent platforms + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +E-MCP-BUS positioning relative to external multi-agent orchestration platforms, such as Agent Platform OCI (**decision confirmed and expanded by the author on 2026-09-11** — resolves G11). + +## Decision + +E-MCP-BUS **does not compete** with orchestration platforms such as Agent Platform OCI — but it also **is not limited to coexisting under them**. Explicit design principle: **modularity + compatibility = choice**. Our Service Orchestrator (EP-11) is not a thin pass-through: it computes NBA **and NBO** (Next Best Action/Next Best Offer) with its own decision model (rules + guardrails +, optionally, a ranking/recommendation AI model — see EP-11-T03) over the universe of `FilteredOfferings` that the MCP Fabric exposes (services, offers, actions, campaigns — not just technical "tools"). This enables **two equally valid integration modes, chosen by the adopter according to the desired topology**: **(A) Governance-only** — an external orchestration platform (e.g., Agent Platform OCI, with its own Global Supervisor and StateGraph deciding NBA) connects to E-MCP-BUS solely for governed access to the Fabric; our Service Orchestrator does not participate, or participates only as a source of `FilteredOfferings` (as already described in the rest of this ADR and in EP-13-T05). **(B) Full replacement** — our own Service Orchestrator (EP-11) fully takes on the role of the decision "brain" (NBA/NBO), dispensing with the external platform's decision-making Global Supervisor/StateGraph; the Agent Runtime (EP-12) and the external platform's channel adapters (e.g., Hoshikawa's agent backends) become mere executors of the dispatch our Orchestrator produces. In either mode, the security core (PEP/PDP, EP-05/EP-03) never changes place — only who computes the NBA/NBO changes. In both modes, each domain backend/agent remains registered as a **distinct MCP Client** (EP-01), bound to its own client profile/entitlement per domain+risk tier (EP-04, P6), and any consuming platform's internal "MCP Gateway" (ADR-023) is always superseded by our Gateway/PEP as the single point of authorization. This is the "Enterprise elevation": centrally governed catalog/entitlement/audit, with the NBA/NBO decision able to come from inside (mode B) or outside (mode A) the bus, by the adopter's modular choice. + +## Alternatives Considered + +Forcing a single integration mode (e.g., only "governance-only"), which would underuse the Service Orchestrator and always require an external decision platform; treating Agent Platform OCI as a substitute for E-MCP-BUS (incorrect — it does not solve entitlement); reimplementing from scratch the orchestration engineering already validated in ADR-022 even when mode B is chosen (the StateGraph/checkpointing/handoff form remains reusable within our EP-11/EP-12). + +## Consequences + +Reshapes the goal of EP-11 (Service Orchestrator explicitly described as computing NBA **and NBO** over Fabric services/offers/actions, with a pluggable decision model) and of EP-13 (demonstrates the two modes — see EP-13-T05 for mode A and EP-13-T06, new, for mode B). Reinforces that EP-06/EP-05 treat any consuming platform's "MCP Gateway" as untrusted for authorization, in both modes. diff --git a/docs/adr/ADR-025-pdp-engine.en-us.md b/docs/adr/ADR-025-pdp-engine.en-us.md new file mode 100644 index 0000000..b47d36f --- /dev/null +++ b/docs/adr/ADR-025-pdp-engine.en-us.md @@ -0,0 +1,20 @@ +# ADR-025: OPA/Rego as the PDP engine + +**Status:** Accepted. +**Source:** this ADR is the canonical record of this decision. For implementation and validation context, see `RI/docs/AS-BUILT.md` and, when applicable, the relevant contract in `RI/docs/interfaces/`. + +## Context + +PDP engine (closes G3 — **decision confirmed by the author on 2026-09-11**; renumbered from ADR-010 to ADR-025 on 2026-09-11 to avoid colliding with the README's ADR-010, "Global Capability Registry Governance"). + +## Decision + +OPA/Rego, self-hosted, OSS (Apache-2.0). Final decision, not subject to review. **Verified on 2026-09-11 via web search**: OPA is at v1.0+, with Rego v1 syntax as the default (a breaking change vs. v0) — EP-03 must write policies in Rego v1 syntax from the start. + +## Alternatives Considered + +Cedar (AWS, cited in §43.2); a custom engine in Python. + +## Consequences + +The entire EP-03 becomes coupled to OPA's REST API; policies versioned as code in Rego v1. diff --git a/docs/research/hoshikawa-agent-platform-oci.en-us.md b/docs/research/hoshikawa-agent-platform-oci.en-us.md new file mode 100644 index 0000000..b3ee104 --- /dev/null +++ b/docs/research/hoshikawa-agent-platform-oci.en-us.md @@ -0,0 +1,135 @@ +# Research: Agent Platform OCI (Christiano Hoshikawa) as a Python/LangGraph/Langfuse reference + +> Source: https://github.com/hoshikawa2/agent_platform_oci (README + SPEC-001, 002, 003, 004, 007, 012, 018). Analyzed on 2026-09-11 to underpin the decisions recorded in [`ADR-022`](../adr/ADR-022-reference-hoshikawa-agent-platform-oci.en-us.md)/[`ADR-023`](../adr/ADR-023-authorization-never-comes-from-payload.en-us.md) (see also [`../../RI/docs/Assumptions.md`](../../RI/docs/Assumptions.md)). Referenced by ADR-022/ADR-023. +> +> **Scope of this research**: extract the *operational* LangGraph/Langfuse/MCP pattern from that project for our RI's Python implementation layer. The security core (PEP/PDP/Client-Bound Entitlement) of our architecture remains 100% defined by the `README.md` at the root of this repository — never by the project researched here. + +--- + +## 1. LangGraph pattern + +**StateGraph — nodes and state shape.** The per-backend graph is fixed and corporate: + +```text +START → input_guardrails → routing_decision → [domain agent(s)] → output_supervisor + → output_guardrails → judge → supervisor_review → persist → END +routing_decision → handoff → routing_decision (conditional return edge) +``` + +`AgentState` is a `TypedDict(total=False)` with: `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`, plus `active_transaction`/`last_transaction` in the transactional extension (a durable state contract that survives checkpoint/resume). + +Registering a new agent is mechanical: import the class → instantiate it in the workflow's `__init__` (receiving shared dependencies: telemetry, tool_router, rag_service, cache, observer, memory) → async wrapper with a telemetry span → `builder.add_node(...)` → entry in `add_conditional_edges("routing_decision", ...)` → wiring to `output_supervisor`. + +**Two routing layers, explicitly separated:** +- **Backend-local supervisor/router** — decides an agent's internal flow (`routing.yaml`, `ROUTING_MODE=router|supervisor`). +- **Global Supervisor / Agent Gateway** — a separate deployable service that decides **which whole backend** handles the conversation (`GLOBAL_ROUTING_MODE=router|supervisor|hybrid`, `backends.yaml`). HTTP routing between backends, not a second LangGraph. + +**Checkpointing**: `memory` (tests), `sqlite` (local dev), `mongodb` (distributed), `autonomous` (OCI production) providers. Contract: `{conversation_key, checkpoint_id, state, pending_writes, created_at}`. + +**Handoff**: intra-backend via the conditional edge `routing_decision → handoff → routing_decision`; inter-backend via `metadata.handover_backend` in the response, detected by the Agent Gateway, which keeps `global_session_id` separate from `backend_session_id`. + +--- + +## 2. Langfuse pattern + +**IC / NOC / GRL taxonomy** (not "Informational Context" — it's a **business/journey**, **operational**, and **guardrail** event, respectively): + +| Family | Meaning | Examples | +|---|---|---| +| IC | agent business/journey event | `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 | error/unavailability/timeout/degradation | `NOC.RUNTIME_FAILED`, `NOC.MCP_TIMEOUT`, `NOC.LLM_FAILED` | +| GRL | governance/guardrail/block/sanitization | `GRL.INPUT_BLOCKED`, `GRL.OUTPUT_BLOCKED`, `GRL.MASK_APPLIED` | + +Practical rule: don't create an event per line of code — only for decisions that matter. + +**Hybrid instrumentation**: automatic technical spans per graph node; LLM calls captured automatically via `ENABLE_LANGFUSE_OPENAI_AUTO_INSTRUMENTATION=true` (OpenAI-compatible providers); business events (IC/NOC/GRL) always manual via `_emit_ic/_emit_noc/_emit_grl`, **fail-open** (a missing or erroring observer never breaks the journey). Trace mode `LANGFUSE_TRACE_MODE=verbose|compact`. + +**Prompt management**: **does not use** Langfuse's prompt CMS — versions the prompt locally in YAML (`prompt_policy.yaml`, `id`/`version` fields). Langfuse comes in as a data source for batch evaluation (`EvaluationRun.source=langfuse`, `evals/offline`/`evals/certification` apps), not as the synchronous judging engine (that is a node of the graph itself, using the `judge` LLM profile). + +**Local setup (docker-compose)**: `langfuse-web`, `langfuse-worker`, PostgreSQL, ClickHouse, Redis, MinIO, plus MongoDB (the framework's own memory, not Langfuse's). Ports: Web/API 3005, MinIO S3 9090/Console 9091, MongoDB 27017, Postgres 5433, Redis 6379, ClickHouse 8124/9002. Flow: `docker compose up -d` → create org/project → generate a `pk-lf-.../sk-lf-...` key pair under Settings > API Keys → configure the backend's `.env`. + +--- + +## 3. MCP integration pattern + +**MCP Gateway**: a single deployable app — `/v1/tools`, `/v1/tools/{name}`, `/v1/tools/{name}/invoke`, `/v1/servers`, `/health`, `/ready`. `ToolInvocation`/`ToolResult` contracts carrying `tenant_id`, `agent_id`, `business_context`, `metadata.request_id/trace_id`. + +**MCP Parameter Mapping** (`mcp_parameter_mapping.yaml`): translates canonical business keys (`customer_key`, `contract_key`, `session_key`) into the parameter name each tool expects. + +**MCP cache with Langfuse events**: key `tenant_id:agent_id:tool_name:hash(arguments)`, idempotent tools only, per-tool TTL. Event flow: miss → `IC.MCP_CACHE_MISS → IC.MCP_TOOL_EXECUTING → IC.MCP_TOOL_EXECUTED → IC.MCP_CACHE_SET`; hit → just `IC.MCP_CACHE_HIT`. + +**FastMCP** as the official MCP server default: +```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") +``` + +### ⚠️ Critical finding: shallow and potentially insecure authorization + +Their MCP Gateway's `authorization` block is a **static allowlist keyed by `agent_id`**: + +```yaml +authorization: + default_policy: deny + agents: + telecom_contas: + allowed_tools: [consultar_fatura, consultar_pagamentos] +``` + +The `agent_id` field that governs this allowlist **arrives as data from the conversation's input payload** (`GatewayRequest`/`ToolInvocation`), not as a claim of an authenticated, independently verified MCP Client identity. Their own SPEC-004 admits it: *"conversational policy does not replace authentication, authorization, idempotency, or atomicity in the MCP Server."* There is no PDP/OPA/Cedar, no `policy_decision_id`/`entitlement_version`, no discovery/execution separation as two deliberately independent enforcement points. + +**This is structurally the anti-pattern our RI's ADR-001 forbids** ("authorization by the agent's self-declared role"). See ADR-023 for the decision on how to reuse the proxy engineering without inheriting this problem. + +--- + +## 4. YAML configuration conventions + +| File | Purpose | +|---|---| +| `agents.yaml` | Agent registry (agent_id, isolated config paths, domain metadata) | +| `routing.yaml` | Intents, keywords/examples/priority, `mcp_tools` per intent, `state_policies` | +| `tools.yaml` | Tool catalog: target server, args schema, cache, `allowed_agents` | +| `guardrails.yaml` | Input/output guardrails, enable/disable by code | +| `judges.yaml` | Post-response evaluation criteria (deterministic/llm), threshold, sample_rate | +| `llm_profiles.yaml` | Model/temperature profiles per component (router, judge, supervisor, RAG) | +| `identity.yaml` | Channel field name aliases → canonical business keys (Identity Resolver) | +| `mcp_parameter_mapping.yaml` | Canonical key → parameter name expected by each tool | +| `mcp_servers.yaml` (+ `.docker.yaml`) | MCP server addresses/transport (local vs. Docker) | +| `backends.yaml` | Agent backend registry + Agent Gateway's global routing rules | +| `prompt_policy.yaml` | Base/persistent agent prompt, versioned (`id`, `version`) | +| `tool_policies.yaml` | Classifies a tool as `read_only`/`transactional`, requires confirmation | +| `authorization` (block in the MCP Gateway's config) | Tool allowlist by `agent_id` — **do not reuse as an authorization source** (see section 3) | + +--- + +## 5. Security/identity model (SPEC-018) — compatibility + +- **Workload authentication** (5 modes: config_file/instance_principal/workload_identity/resource_principal/api_key) — addresses how the *framework* itself authenticates against OCI resources (Vault, GenAI, ADB). **Orthogonal** to our tool-entitlement problem. +- **Authorization** — listed as 5 questions to answer, with no formal PDP; in practice resolved by the shallow allowlist described in section 3. +- **Secrets, data protection, channel security** — compatible/complementary, do not touch entitlement. +- **Auditing** — a good audit-trail model (user/channel, agent_id, tenant_id, tool, model, guardrail, judge score, trace_id), but with no equivalent to `decisionId`/`policyDecisionId`/`entitlementVersion` (our ADR-017), because there is no separate PDP generating those decisions. + +**Where it is clearly compatible/orthogonal**: `identity.yaml`/`BusinessContext` — normalization of **business** identity (end customer, contract), never a tool-authorization decision. Adoptable without tension with the security core. + +**Verdict**: SPEC-018 should not be used as a normative security spec for our RI — it mixes workload authentication with tool authorization without separating the two problems or proposing a PDP. + +--- + +## 6. Deployment + +Not tied to OCI at the application layer — runs 100% locally via Docker Compose with generic, self-hostable services (Redis, MongoDB, PostgreSQL, ClickHouse, MinIO, Langfuse). OCI integrations (GenAI, ADB, Vault, workload identity) are pluggable options among several, not a development requirement. + +--- + +## Reuse recommendations (applied in the RI via ADR-022/ADR-023) + +**Adopt as-is** (do not touch the security core): `identity.yaml`/BusinessContext · IC/NOC/GRL taxonomy + EventEnvelope · local Langfuse setup via docker-compose · `ConversationSummaryMemory` · RAG-vs-MCP heuristic (Hoshikawa §30.3-30.4: systems → MCP, documents → RAG) · `mcp_parameter_mapping.yaml` · FastMCP as the default MCP server. + +**Adopt with adaptation**: the *shape* of the MCP Gateway's proxying (catalog, `ToolInvocation`/`ToolResult`, cache with events, retry/timeout/circuit breaker) — but the ALLOW/DENY decision is always delegated to our own PEP/PDP, never to their `authorization` block. The Agent Gateway/Global Supervisor layer (two session levels, handoff) informs the design of EP-11/EP-12, keeping it strictly as UX/business routing, never as an authorization authority. `tool_policies.yaml` (read_only/transactional + confirmation) is conversational convenience, not a security control. + +**Do not copy as a source of security truth**: the `authorization` block keyed by a self-declared `agent_id` in the payload; SPEC-018 as authorization doctrine.