- Fix invariant glossary entry: reconciliation now documented as occurring at each phase boundary (before Strategize, Execute, Apply, and after Apply) rather than only at start of Strategize (closes #9899) - Add Multi-Phase Invariant Enforcement note to Plan Lifecycle section - Add ACMS Thread Safety subsection documenting threading.RLock concurrency contract for ContextAssemblyPipeline and all context stores (closes #9859) - Append M7: Advanced Concepts and Deferred Features (v3.6.0) milestone spec section covering advanced invariant lifecycle, ACMS observability, plan hierarchy enhancements, and CLI communication pattern migration - Add ADR-049: CLI Communication Pattern documenting local CLI exemption from A2A boundary requirement and M9 migration path
6.6 KiB
ADR-049: CLI Communication Pattern
Status: Accepted Date: 2026-04-16 Issues: #9899, #9859
Context
The CleverAgents CLI is the primary user-facing interface for the platform. It is implemented in the Presentation layer and communicates with the Application layer to execute commands.
Current State: Direct Import Pattern
The current CLI implementation uses direct Python imports to call Application-layer services:
# Current pattern (Presentation layer)
from cleveragents.application.plan_service import PlanService
from cleveragents.application.project_service import ProjectService
def plan_execute(plan_id: str):
service = PlanService()
service.execute(plan_id)
This creates a reverse dependency: the Presentation layer directly imports from the Application layer, bypassing the A2A protocol boundary defined in ADR-001 (Layered Architecture) and ADR-047 (A2A Standard Adoption).
The A2A Specification
ADR-047 establishes that all client-server interaction must flow through A2A. The A2A protocol is the sole communication contract between the Presentation and Application layers. In server mode, this is already enforced -- CLI commands route through A2A over HTTP. In local mode, the intent is for CLI commands to route through A2A over stdio (via A2aLocalFacade).
The Problem
The direct import pattern creates several issues:
- Architectural violation: The Presentation layer has a compile-time dependency on the Application layer, violating the layered boundary defined in ADR-001.
- Import-linter failures: The
import-lintertool flags these imports as boundary violations in CI. - Reverse dependency: If the Application layer changes its internal API, the CLI breaks -- even if the A2A contract is unchanged.
- Testing complexity: CLI tests must mock Application-layer internals rather than the A2A protocol boundary.
- Inconsistency: Server-mode CLI correctly uses A2A; local-mode CLI bypasses it.
Decision
Immediate: Local CLI Exemption
The direct import pattern is permitted under a documented local CLI exemption until M9 (v3.8.0). This exemption is granted because:
- The full A2A migration requires significant refactoring across all CLI command handlers.
- The local-mode A2A transport (
A2aLocalFacadeover stdio) is not yet fully implemented. - The performance impact of routing all local CLI commands through A2A needs measurement.
Exemption Rules:
- The exemption applies only to the local (non-server) CLI execution path.
- All server-mode CLI commands must route through A2A (already enforced; no exemption granted).
- The
import-linterconfiguration is updated to explicitly allow the exempted imports with a# cli-exemption: local-onlyannotation in the import-linter config file. - No new direct imports may be added without a corresponding issue tracking the M9 migration.
- All exempted imports must be documented in
docs/adr/ADR-049-cli-communication-pattern.md(this document).
Import-Linter Configuration:
[importlinter:contract:cli-to-application]
name = CLI must not import Application layer directly
type = forbidden
source_modules = cleveragents.cli
forbidden_modules = cleveragents.application
ignore_imports =
# cli-exemption: local-only -- tracked for M9 migration
cleveragents.cli.commands.plan:cleveragents.application.plan_service
cleveragents.cli.commands.project:cleveragents.application.project_service
cleveragents.cli.commands.actor:cleveragents.application.actor_service
cleveragents.cli.commands.resource:cleveragents.application.resource_service
cleveragents.cli.commands.action:cleveragents.application.action_service
cleveragents.cli.commands.invariant:cleveragents.application.invariant_service
cleveragents.cli.commands.session:cleveragents.application.session_service
cleveragents.cli.commands.config:cleveragents.application.config_service
cleveragents.cli.commands.skill:cleveragents.application.skill_service
cleveragents.cli.commands.tool:cleveragents.application.tool_service
cleveragents.cli.commands.validation:cleveragents.application.validation_service
cleveragents.cli.commands.automation_profile:cleveragents.application.automation_profile_service
cleveragents.cli.commands.lsp:cleveragents.application.lsp_service
M9 Migration: Full A2A Adoption
In M9 (v3.8.0), the CLI will be fully migrated to use A2aLocalFacade for all local-mode commands:
# Target pattern (M9)
from cleveragents.cli.a2a_local_facade import A2aLocalFacade
def plan_execute(plan_id: str):
facade = A2aLocalFacade()
facade.call("_cleveragents/plan.execute", {"plan_id": plan_id})
Migration Steps:
- Implement
A2aLocalFacadewith full coverage of all_cleveragents/extension methods. - Measure performance overhead of local A2A routing (target: < 5ms per command).
- Replace all direct service imports in CLI command handlers with
facade.call(). - Remove all
# cli-exemption: local-onlyannotations from import-linter config. - Remove the
ignore_importsblock from the import-linter contract. - Validate with full CLI integration test suite.
- Update this ADR status to "Superseded" and reference the M9 implementation PR.
Consequences
Positive
- Architectural consistency: Local and server-mode CLI use the same protocol boundary.
- Testability: CLI tests mock the A2A facade rather than Application-layer internals.
- Decoupling: Application-layer internal API changes do not break the CLI as long as the A2A contract is preserved.
- Import-linter clean: No boundary violations in CI after M9 migration.
Negative
- Performance overhead: Routing through A2A adds latency (estimated < 5ms for local stdio transport). Acceptable for interactive CLI use.
- Migration effort: All CLI command handlers must be refactored in M9.
- Temporary inconsistency: Until M9, local-mode CLI bypasses the A2A boundary while server-mode enforces it.
Neutral
- The exemption is explicitly documented and tracked, preventing silent accumulation of additional violations.
- The import-linter configuration makes the exemption visible in CI output.
References
- ADR-001: Layered Architecture
- ADR-047: A2A Standard Adoption
- ADR-048: Server Application Architecture
- Spec: M7 CLI Communication Pattern Migration
- Issue #9899: Invariant reconciliation multi-phase enforcement
- Issue #9859: ACMS thread-safety model documentation