Skip to main content

ADR 020 — Structural agent capability manifests and advisor-only gate

  • Status: Proposed (2026-06-19) — substantially implemented in alphaswarm_agents the same day: assert_advisor_only runs in registry.persist_spec (rollout step 2) and the StrategyPromotionRequest enforcing gate exists as alphaswarm/promotion/gate.py + alphaswarm/promotion/service.py (rollout step 3). As built, the advisor gate classifies mutating tools via ToolRef.scopes mutating-suffix matching rather than a fully backfilled CapabilityManifest per tool (rollout step 1) — the gate's own docstring notes this narrows once CapabilityManifest (ADR 019) is applied per-tool. Gated on the Architecture Enhancement Guide roadmap (enhancement E7, Wave 3)
  • Disposition (2026-08-03, ARP review): superseded-in-place by ARP-ADR-011 when it lands — CapabilityManifest becomes the ToolContract sub-shape inside AgentSystemVersion; advisor-only becomes the authority-ceiling floor; the unified AuthorityGrant/PolicyDecision/Approval model delivers this ADR's intent with issue/attenuate/revoke lifecycle added. Flip to Superseded when ARP-ADR-011 ships. See alphaswarm_internal docs/architecture/agent-first-research-platform/02-context-map-and-ownership.md §6 (binding disposition table) and 10-adrs.md.
  • Authors: Platform team
  • Related: Enhancement Guide §6/E7, ADR 019; Hard Rules 22 (no direct data access), 39 (AST sandbox), 49 (MCP RFC 9728/8707), 54 (delegated tokens)

Context​

The platform already runs agents as advisors: Hard Rule 22 forbids direct Postgres/Iceberg access (reads go through registered DataMCPTools), role prompts forbid order/execution tools, and the trader emits signals only. But the guarantee is behavioral, not structural — it rests on tool wiring and prompt text. Nothing rejects an AgentSpec (even one with template_target="research") that binds a mutating tool. Tool-IO schema versioning exists only for Data MCP (descriptor SHA-256 → mcp_tool_versions); the ~55 CrewAI registry tools and the Platform-Context MCP (raw JSON-Schema dicts) lack capability metadata and versioning. The typed money-plane crossing StrategyPromotionRequest is specified in alphaswarm_core/contracts/ but has no enforcing gate.

Industry consensus (OWASP LLM06:2025 Excessive Agency; "agent proposes, system disposes"; plan-level governance) is to assume prompt injection succeeds and ensure a compromised agent cannot take an irreversible action. The MCP spec (2025-11-25) already defines the side-effect taxonomy we need: readOnlyHint / destructiveHint / idempotentHint / openWorldHint.

Decision​

Make advisor-only behavior structural, enforced by types and registration.

  1. CapabilityManifest (Pydantic, frozen). Every agent tool carries a manifest aligned to MCP annotations: read_only, destructive, idempotent, open_world, required_scopes, input_schema_id, output_schema_id, schema_version. Carried via CapabilityMeta (ADR 019) so it is attached at class-definition time and uniform across the CrewAI registry, the monolith Data MCP, and the Platform-Context MCP.
  2. Enforce at registration and dispatch. CapabilityMeta validates the manifest at definition time; AgentRuntime checks required_scopes and the side-effect class against the granted MCPToolContext before every dispatch (extending today's policy.enforce_required_scopes).
  3. Advisor-only gate. At spec-validation time, assert_advisor_only(spec) rejects any spec whose template_target ∈ {research, selection, analysis} that binds a tool with read_only=False. Wire it into registry.persist_spec.
  4. Single write path. The typed StrategyPromotionRequest, gated by ValidationVerdict + PreTradeVerdict + out-of-band human approval, becomes the only research→live mutation. Agents propose; the gate disposes.
  5. Version all tool IO. Extend descriptor-hash versioning (already on Data MCP) to every surface; Pydantic-type the Platform-Context MCP IO.

Consequences​

Positive

  • "Advisors, not operators" becomes a structural guarantee, not a convention; a mis-wired advisory agent fails to register rather than mutating state at run time. Auditable, replayable, injection-resilient.
  • Uniform, versioned, machine-readable tool contracts across all MCP surfaces.

Negative / risks

  • Requires backfilling CapabilityManifest for ~55 CrewAI tools and typing the Platform-Context MCP IO; do it incrementally, defaulting unmanifested tools to read_only=False (fail-closed) so the gate is conservative until backfilled.
  • Depends on ADR 019 (CapabilityMeta).

Explicitly rejected

  • Relying on prompt text / tool wiring for the advisor-only guarantee.
  • Per-action human approval as the primary control (consent fatigue); use plan-level governance + the single typed promotion gate instead.

Rollout order​

  1. CapabilityManifest + CapabilityMeta (with ADR 019); default-deny for unmanifested tools.
  2. assert_advisor_only in registry.persist_spec; backfill manifests.
  3. Build the StrategyPromotionRequest enforcing gate as the single write path.