ADR 020 — Structural agent capability manifests and advisor-only gate
- Status: Proposed (2026-06-19) — substantially implemented in
alphaswarm_agentsthe same day:assert_advisor_onlyruns inregistry.persist_spec(rollout step 2) and theStrategyPromotionRequestenforcing gate exists asalphaswarm/promotion/gate.py+alphaswarm/promotion/service.py(rollout step 3). As built, the advisor gate classifies mutating tools viaToolRef.scopesmutating-suffix matching rather than a fully backfilledCapabilityManifestper tool (rollout step 1) — the gate's own docstring notes this narrows onceCapabilityManifest(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) and10-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.
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 viaCapabilityMeta(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.- Enforce at registration and dispatch.
CapabilityMetavalidates the manifest at definition time;AgentRuntimechecksrequired_scopesand the side-effect class against the grantedMCPToolContextbefore every dispatch (extending today'spolicy.enforce_required_scopes). - Advisor-only gate. At spec-validation time,
assert_advisor_only(spec)rejects any spec whosetemplate_target ∈ {research, selection, analysis}that binds a tool withread_only=False. Wire it intoregistry.persist_spec. - Single write path. The typed
StrategyPromotionRequest, gated byValidationVerdict+PreTradeVerdict+ out-of-band human approval, becomes the only research→live mutation. Agents propose; the gate disposes. - 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
CapabilityManifestfor ~55 CrewAI tools and typing the Platform-Context MCP IO; do it incrementally, defaulting unmanifested tools toread_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
CapabilityManifest+CapabilityMeta(with ADR 019); default-deny for unmanifested tools.assert_advisor_onlyinregistry.persist_spec; backfill manifests.- Build the
StrategyPromotionRequestenforcing gate as the single write path.