reflector(a2a): execute ACP to A2A module rename and symbol standardization #11165

Merged
HAL9000 merged 3 commits from feature/acp-a2a-rename-fix into master 2026-06-15 07:16:09 +00:00
4 changed files with 195 additions and 191 deletions
+1
View File
@@ -46,6 +46,7 @@ Below are some of the specific details of various contributions.
* HAL 9000 has contributed the PureGraph BDD coverage suite (PR #9601 / issue #9531): wired the previously orphaned `features/steps/pure_graph_coverage_steps.py` definitions through the existing `features/consolidated_langgraph.feature` (topological ordering, function execution, missing function fallback, and non-functional node handling); created Robot Framework integration tests in `robot/langgraph/pure_graph.robot` backed by the `robot/langgraph/pure_graph_lib.py` Python library; and implemented ASV benchmarks in `benchmarks/pure_graph_bench.py` measuring execution throughput across varying node counts.
* HAL 9000 has contributed comprehensive milestone documentation for v3.2.0 (Decisions + Validations + Invariants) and v3.3.0 (Corrections + Subplans + Checkpoints), including CLI command reference, decision system guide, and subplan/checkpoint documentation (PR #9796).
* Rui Hu has contributed the v3 actor YAML schema validation fix (#5869): added `ActorConfigSchema` validation to the `agents actor add --config` CLI command, covering cycle detection, required field validation, and enum validation for v3 YAML actor definitions.
* HAL 9000 has contributed the ACP → A2A BDD test suite (#10995 / issue #8615): added `a2a_module_rename_standardization.feature` with 3 scenarios validating all 22 exported symbol exports, zero legacy ACP references across the a2a module, and documentation accuracy per ADR-047 naming conventions.
* HAL 9000 has contributed the agent-evolution-pool-supervisor PR metadata assignment (#7888): the supervisor now automatically looks up the Type/Automation label and earliest open milestone before dispatching improvement PR creation workers, ensuring all generated improvement PRs have correct Type labels and milestone assignments.
* HAL 9000 has contributed the decision recording hook for the Strategize phase (issue #8522): captures every decision point with question, chosen option, alternatives, confidence, rationale, and full context snapshot for replay and correction.
* HAL 9000 has contributed the ContextStrategy protocol and StrategyRegistry plugin registration system (PR #10590 / issue #8616): implemented the pluggable context assembly strategy protocol with proper type-safe method signatures, created the central thread-safe StrategyRegistry supporting registration, lookup, entry-point discovery, and per-strategy configuration (timeout, fragments limits, workers, circuit breaker threshold). Six built-in strategies implemented and documented: simple-keyword, semantic-embedding, breadth-depth-navigator, arce, temporal-archaeology, and plan-decision-context. Full BDD test coverage including thread safety, boundary validation, and error handling tests. (Part of Epic #8505)
@@ -1,40 +1,49 @@
Feature: A2A Module Rename and Symbol Standardization
As a developer
I want all A2A symbols to be standardized
So that the codebase uses consistent A2A naming conventions
@refactor_v360_acp_to_a2a_rename @epic_8569
Feature: ACP to A2A module rename and symbol standardization
As a CleverAgents developer
I want the ACP A2A module rename to be fully validated via BDD tests
So that no legacy ACP references remain and all exports follow ADR-047 naming
Scenario: A2A module exports all required symbols
When I import from cleveragents.a2a
Then I should have access to A2aError
And I should have access to A2aErrorDetail
And I should have access to A2aEvent
And I should have access to A2aEventQueue
And I should have access to A2aHttpTransport
And I should have access to A2aLocalFacade
And I should have access to A2aNotAvailableError
And I should have access to A2aOperationNotFoundError
And I should have access to A2aRequest
And I should have access to A2aResponse
And I should have access to A2aStdioTransport
And I should have access to A2aVersion
And I should have access to A2aVersionMismatchError
And I should have access to A2aVersionNegotiator
And I should have access to AuthClient
And I should have access to RemoteExecutionClient
And I should have access to ServerClient
And I should have access to ServerConnectionConfig
And I should have access to StubAuthClient
And I should have access to StubRemoteExecutionClient
And I should have access to StubServerClient
And I should have access to TransportSelector
Background:
Given the "a2a" Python package is importable from "cleveragents.a2a"
Scenario: A2A module has no ACP references
When I check the A2A module for ACP references
Then there should be no ACP imports
And there should be no ACP class names
And there should be no ACP function names
Scenario: The 22 ACP-to-A2A rename symbols are exported via a2a package
When I import "cleveragents.a2a"
And all of the following 22 symbols should be importable from it:
| symbol |
| A2aError |
| A2aErrorDetail |
| A2aEvent |
| A2aEventQueue |
| A2aHttpTransport |
| A2aLocalFacade |
| A2aNotAvailableError |
| A2aOperationNotFoundError |
| A2aRequest |
| A2aResponse |
| A2aStdioTransport |
| A2aVersion |
| A2aVersionMismatchError |
| A2aVersionNegotiator |
| AuthClient |
| RemoteExecutionClient |
| ServerClient |
| ServerConnectionConfig |
| StubAuthClient |
| StubRemoteExecutionClient |
| StubServerClient |
| TransportSelector |
Then every symbol should resolve to a non-None object
And every listed symbol should appear in __all__
Scenario: A2A module is properly documented
When I check the A2A module documentation
Then the module docstring should mention A2A
And the module docstring should not mention ACP
Scenario: Zero ACP references remain in the a2a module source files
When I recursively scan all Python files under "src/cleveragents/a2a"
And I search for the legacy prefix string "ACP"
Then zero instances of "ACP" should be found
And this confirms the complete ACP A2A rename is clean
Scenario: Documentation strings use A2A naming per ADR-047
When I read the docstring of the "cleveragents.a2a" package
Then it should contain the term "A2A (Agent-to-Agent Protocol)"
And it should reference "ADRs" or "ADR-047" for standard adoption
And the docstring should NOT contain any mentions of "ACP protocol"
@@ -1,179 +1,171 @@
"""Step definitions for A2A module rename and symbol standardization.
"""Step definitions for ACP → A2A module rename standardization BDD scenarios."""
Validates that all A2A symbols are properly exported from the ``cleveragents.a2a``
module following the JSON-RPC 2.0 specification naming convention, that no legacy
ACP references remain in the codebase, and that documentation is accurate.
"""
from __future__ import annotations
import inspect
import os
import re
from typing import Any
from pathlib import Path
from behave import when, then
import cleveragents.a2a as a2a_module
from cleveragents.a2a import (
A2aError,
A2aErrorDetail,
A2aEvent,
A2aEventQueue,
A2aHttpTransport,
A2aLocalFacade,
A2aNotAvailableError,
A2aOperationNotFoundError,
A2aRequest,
A2aResponse,
A2aStdioTransport,
A2aVersion,
A2aVersionMismatchError,
A2aVersionNegotiator,
AuthClient,
RemoteExecutionClient,
ServerClient,
ServerConnectionConfig,
StubAuthClient,
StubRemoteExecutionClient,
StubServerClient,
TransportSelector,
)
from behave import given, then, when
@when("I import from cleveragents.a2a")
def step_import_a2a(context):
"""Import from cleveragents.a2a."""
context.a2a_symbols = {
"A2aError": A2aError,
"A2aErrorDetail": A2aErrorDetail,
"A2aEvent": A2aEvent,
"A2aEventQueue": A2aEventQueue,
"A2aHttpTransport": A2aHttpTransport,
"A2aLocalFacade": A2aLocalFacade,
"A2aNotAvailableError": A2aNotAvailableError,
"A2aOperationNotFoundError": A2aOperationNotFoundError,
"A2aRequest": A2aRequest,
"A2aResponse": A2aResponse,
"A2aStdioTransport": A2aStdioTransport,
"A2aVersion": A2aVersion,
"A2aVersionMismatchError": A2aVersionMismatchError,
"A2aVersionNegotiator": A2aVersionNegotiator,
"AuthClient": AuthClient,
"RemoteExecutionClient": RemoteExecutionClient,
"ServerClient": ServerClient,
"ServerConnectionConfig": ServerConnectionConfig,
"StubAuthClient": StubAuthClient,
"StubRemoteExecutionClient": StubRemoteExecutionClient,
"StubServerClient": StubServerClient,
"TransportSelector": TransportSelector,
}
# ── Helpers ────────────────────────────────────────────────────────────────
@then("I should have access to {symbol_name}")
def step_have_access_to_symbol(context, symbol_name):
"""Verify access to a specific symbol."""
assert symbol_name in context.a2a_symbols
assert context.a2a_symbols[symbol_name] is not None
def _resolve_repo_root(context: Any) -> Path:
"""Return the repository root path from the feature context."""
repo_root = getattr(context, "repo_root", None)
if repo_root is not None:
return Path(repo_root)
candidate = Path(os.getcwd())
while candidate != candidate.parent:
if (candidate / ".git").exists():
context.repo_root = str(candidate)
return candidate
candidate = candidate.parent
raise RuntimeError("Could not find repository root")
@when("I check the A2A module for ACP references")
def step_check_acp_references(context):
"""Recursively scan all .py files in the A2A module for ACP references."""
context.acp_references = {
"imports": [],
"class_names": [],
"function_names": [],
}
# Recursively scan the entire a2a directory
a2a_dir = os.path.dirname(inspect.getfile(a2a_module))
for root, _dirs, files in os.walk(a2a_dir):
for fname in files:
if not fname.endswith(".py"):
continue
filepath = os.path.join(root, fname)
with open(filepath, encoding="utf-8") as f:
content = f.read()
# Check imports
acp_import = (
"from cleveragents.acp" in content
or "import cleveragents.acp" in content
)
if acp_import:
context.acp_references["imports"].append(filepath)
# Check class/function names for ACP prefix remnants
acp_patterns = re.findall(r"\s+def\s+(_?[Aa][Cc][Pp]\w*)", content)
acp_patterns += re.findall(r"\(\s*(_?[Aa][Cc][Pp]\w*)\)", content)
for match in acp_patterns:
context.acp_references["function_names"].append(match)
acp_class_patterns = re.findall(r"class\s+(_?[Aa][Cc][Pp]\w*)", content)
for match in acp_class_patterns:
context.acp_references["class_names"].append(match)
def _scan_dir_for_pattern(root: Path, pattern_str: str) -> int:
"""Recursively count occurrences of *pattern_str* in all .py files."""
count = 0
py_files = list(root.rglob("*.py"))
for py_file in py_files:
try:
text = py_file.read_text(encoding="utf-8", errors="replace")
except OSError:
continue
count += text.count(pattern_str)
return count
@then("there should be no ACP imports")
def step_no_acp_imports(context):
"""Verify no ACP imports."""
assert len(context.acp_references["imports"]) == 0
# ── Given steps ────────────────────────────────────────────────────────────
@then("there should be no ACP class names")
def step_no_acp_class_names(context):
"""Verify no ACP class names."""
assert len(context.acp_references["class_names"]) == 0
@given('the "a2a" Python package is importable from "cleveragents.a2a"')
def step_a2a_importable(context: Any) -> None:
"""Ensure cleveragents.a2a can be imported."""
try:
import cleveragents.a2a # noqa: F401
except ImportError as exc:
raise AssertionError(f"cleveragents.a2a could not be imported: {exc}") from exc
@then("there should be no ACP function names")
def step_no_acp_function_names(context):
"""Verify no ACP function names."""
assert len(context.acp_references["function_names"]) == 0
# ── When steps ─────────────────────────────────────────────────────────────
# Symbol definitions used by Step 3 to validate A2A documentation.
# Self-contained: does not depend on context set by other scenarios.
_A2A_DOCUMENTATION_SYMBOLS = {
"A2aError": A2aError,
"A2aErrorDetail": A2aErrorDetail,
"A2aEvent": A2aEvent,
"A2aEventQueue": A2aEventQueue,
"A2aHttpTransport": A2aHttpTransport,
"A2aLocalFacade": A2aLocalFacade,
"A2aNotAvailableError": A2aNotAvailableError,
"A2aOperationNotFoundError": A2aOperationNotFoundError,
"A2aRequest": A2aRequest,
"A2aResponse": A2aResponse,
"A2aStdioTransport": A2aStdioTransport,
"A2aVersion": A2aVersion,
"A2aVersionMismatchError": A2aVersionMismatchError,
"A2aVersionNegotiator": A2aVersionNegotiator,
"AuthClient": AuthClient,
"RemoteExecutionClient": RemoteExecutionClient,
"ServerClient": ServerClient,
"ServerConnectionConfig": ServerConnectionConfig,
"StubAuthClient": StubAuthClient,
"StubRemoteExecutionClient": StubRemoteExecutionClient,
"StubServerClient": StubServerClient,
"TransportSelector": TransportSelector,
}
@when('I import "cleveragents.a2a"')
def step_import_a2a(context: Any) -> None:
"""Import the a2a package and store it on context."""
import cleveragents.a2a
context._a2a_module = cleveragents.a2a
@when("I check the A2A module documentation")
def step_check_a2a_documentation(context):
"""Check A2A module documentation."""
context.module_doc = a2a_module.__doc__
context.class_docs = {}
for name, symbol in _A2A_DOCUMENTATION_SYMBOLS.items():
if inspect.isclass(symbol):
context.class_docs[name] = symbol.__doc__
@when("all of the following {count:d} symbols should be importable from it:")
def step_symbols_importable(context: Any, count: int) -> None:
"""Store all expected symbol names on context for later assertion.
Reads the Gherkin data table as the single source of truth so the feature
file documents the exact 22 symbols the ACPA2A rename must export.
"""
assert hasattr(context._a2a_module, "__all__"), (
"cleveragents.a2a must define __all__"
)
symbols = [row[0].strip() for row in context.table.rows]
context._expected_symbols = symbols
assert len(symbols) == count, f"Expected {count} symbols, got {len(symbols)}"
@then("the module docstring should mention A2A")
def step_module_doc_mentions_a2a(context):
"""Verify module docstring mentions A2A."""
assert context.module_doc is not None
assert "A2A" in context.module_doc
@when('I recursively scan all Python files under "src/cleveragents/a2a"')
def step_scan_a2a_python_files(context: Any) -> None:
"""Set context for ACP-remnant scanning."""
root = _resolve_repo_root(context)
a2a_dir = root / "src" / "cleveragents" / "a2a"
assert a2a_dir.is_dir(), f"a2a module directory not found at {a2a_dir}"
context._a2a_scan_root = str(a2a_dir)
@then("the module docstring should not mention ACP")
def step_module_doc_no_acp(context):
"""Verify module docstring doesn't mention ACP."""
assert context.module_doc is not None
assert "ACP" not in context.module_doc
@when('I search for the legacy prefix string "ACP"')
def step_search_acp_pattern(context: Any) -> None:
"""Perform recursive scan and store results."""
count = _scan_dir_for_pattern(Path(context._a2a_scan_root), "ACP")
context._acp_references_found = count
Outdated
Review

BLOCKING — Data table is ignored; hardcoded list used instead

This step receives a 22-row data table from the feature file (via context.table) but never reads it. _ALL_SYMBOLS is a hardcoded module-level list used instead, making the table in the .feature file dead code.

Problem: If someone updates the symbol table in the .feature file but forgets to update _ALL_SYMBOLS, the test passes silently with the wrong symbol list. The Gherkin table appears to be the source of truth but is not.

Fix: Replace symbols = list(_ALL_SYMBOLS) with:

symbols = [row[0].strip() for row in context.table.rows]

This makes the feature file the authoritative symbol list, ensuring the Gherkin table and the test implementation remain in sync.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKING — Data table is ignored; hardcoded list used instead** This step receives a 22-row data table from the feature file (via `context.table`) but never reads it. `_ALL_SYMBOLS` is a hardcoded module-level list used instead, making the table in the .feature file dead code. **Problem:** If someone updates the symbol table in the .feature file but forgets to update `_ALL_SYMBOLS`, the test passes silently with the wrong symbol list. The Gherkin table appears to be the source of truth but is not. **Fix:** Replace `symbols = list(_ALL_SYMBOLS)` with: ```python symbols = [row[0].strip() for row in context.table.rows] ``` This makes the feature file the authoritative symbol list, ensuring the Gherkin table and the test implementation remain in sync. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
@when('I read the docstring of the "cleveragents.a2a" package')
def step_read_a2a_docstring(context: Any) -> None:
"""Store the a2a package docstring on context."""
import cleveragents.a2a as a2a_pkg
assert a2a_pkg.__doc__ is not None, "cleveragents.a2a has no __doc__"
context._a2a_docstring = a2a_pkg.__doc__
# ── Then steps ─────────────────────────────────────────────────────────────
@then("every symbol should resolve to a non-None object")
def step_all_symbols_resolved(context: Any) -> None:
"""Assert every expected symbol is present and non-None."""
module = context._a2a_module
missing = []
for name in context._expected_symbols:
obj = getattr(module, name, None)
if obj is None:
missing.append(name)
assert not missing, f"Missing symbols: {missing}"
@then("every listed symbol should appear in __all__")
def step_listed_symbols_in_all(context: Any) -> None:
"""Assert every symbol named in the feature data table is present in __all__.
Subset check rather than exact-count match ``__all__`` may legitimately
grow with additional unrelated exports (AgentCard*, Sync*, etc.) without
invalidating the ACPA2A rename contract.
"""
actual_all = set(context._a2a_module.__all__)
missing = [s for s in context._expected_symbols if s not in actual_all]
assert not missing, f"Listed symbols absent from __all__: {missing}"
@then('zero instances of "ACP" should be found')
def step_zero_acp_references(context: Any) -> None:
"""Assert zero ACP remnants were found."""
assert context._acp_references_found == 0, (
f"Found {context._acp_references_found} ACP references — rename is incomplete"
)
@then("this confirms the complete ACP → A2A rename is clean")
def step_rename_clean(context: Any) -> None:
"""No additional assertion needed; marker step for clarity."""
pass
@then('it should contain the term "A2A (Agent-to-Agent Protocol)"')
def step_docstring_has_descriptive_term(context: Any) -> None:
"""Assert docstring contains the canonical descriptive term."""
assert "A2A (Agent-to-Agent Protocol)" in context._a2a_docstring, (
f"Docstring missing 'A2A (Agent-to-Agent Protocol)':\n{context._a2a_docstring[:500]}"
)
@then('it should reference "ADRs" or "ADR-047" for standard adoption')
def step_docstring_references_adr(context: Any) -> None:
"""Assert docstring mentions an ADR (ADR-047 for A2A std)."""
pattern = re.compile(r"(ADR-?0*47|A2A Standard Adoption)", re.IGNORECASE)
assert pattern.search(context._a2a_docstring), (
f"Docstring does not reference ADR-047 or A2A Standard Adoption:\n{context._a2a_docstring[:500]}"
)
@then('the docstring should NOT contain any mentions of "ACP protocol"')
def step_no_acp_protocol_mention(context: Any) -> None:
"""Assert docstring does not reference ACP."""
assert (
"ACP(protocol)" not in context._a2a_docstring
and "ACP protocol" not in context._a2a_docstring
), f"Docstring contains legacy 'ACP protocol':\n{context._a2a_docstring[:500]}"
+2
View File
@@ -18,6 +18,8 @@ compatibility. :class:`ServerConnectionConfig` validates connection parameters.
The :class:`TransportSelector` chooses the appropriate transport based on
configuration: stdio for local mode, HTTP for server mode.
Naming follows ADR-047 (A2A Standard Adoption).
"""
from __future__ import annotations