fix(actor): Report the number of nodes and edges in the system

Extract nodes and edges from the route block as top-level keys in
graph_descriptor within _extract_v3_actor() in config.py. The CLI
layer reads graph_descriptor.get("nodes") / .get("edges") directly,
so those keys must be present at the top level — not nested under route.

Reverts the incorrect CLI-layer workaround (drilling into route) that
broke existing tests relying on flat graph_descriptor structures.

Also fixes two regressions introduced by prior attempts:
- Restored key/value and filter/value table headers in the ACMS
  index_data_model_and_traversal feature file
- Reverted the ACMS step name from "the count should be" back to
  "idx the index count should be" to eliminate the ambiguous step
  conflict with security_audit_steps.py

Adds @tdd_issue @tdd_issue_10860 BDD regression scenarios verifying
that a v3 type:graph actor YAML with route.nodes and route.edges
produces a graph_descriptor with correct top-level node/edge counts.

ISSUES CLOSED: #10860
This commit is contained in:
2026-06-10 05:02:32 -04:00
committed by Forgejo
parent 8d6c6a79e5
commit e9bfeca0bc
4 changed files with 73 additions and 4 deletions
+17
View File
@@ -0,0 +1,17 @@
@tdd_issue @tdd_issue_10860
Feature: Actor graph_descriptor exposes nodes and edges from route block
As a developer using graph actors
I want nodes and edges from the route block to appear as top-level keys in graph_descriptor
So that 'agents actor add' correctly reports node and edge counts
Scenario: v3 graph actor with nodes and edges in route reports correct counts
Given a v3 graph actor blob with a route containing 2 nodes and 1 edge
When I parse the actor configuration from the v3 blob
Then the parsed graph_descriptor node count should be 2
And the parsed graph_descriptor edge count should be 1
Scenario: v3 graph actor route with no nodes or edges defaults to empty lists
Given a v3 graph actor blob with a route containing no nodes or edges
When I parse the actor configuration from the v3 blob
Then the parsed graph_descriptor node count should be 0
And the parsed graph_descriptor edge count should be 0
@@ -0,0 +1,50 @@
"""Step definitions for actor_config_nodes_edges.feature — regression for issue #10860."""
from __future__ import annotations
from behave import given, then, when
from behave.runner import Context # type: ignore[import-untyped]
from cleveragents.actor.config import ActorConfiguration
@given("a v3 graph actor blob with a route containing 2 nodes and 1 edge")
def step_graph_actor_blob_with_nodes(context: Context) -> None:
context.blob = {
"type": "graph",
"model": "openai/gpt-4",
"route": {
"nodes": ["start", "end"],
"edges": [["start", "end"]],
},
}
@given("a v3 graph actor blob with a route containing no nodes or edges")
def step_graph_actor_blob_empty(context: Context) -> None:
context.blob = {
"type": "graph",
"model": "openai/gpt-4",
"route": {},
}
@when("I parse the actor configuration from the v3 blob")
def step_parse_actor_config(context: Context) -> None:
context.actor_config = ActorConfiguration.from_blob(blob=context.blob)
@then("the parsed graph_descriptor node count should be {count:d}")
def step_check_graph_descriptor_nodes(context: Context, count: int) -> None:
gd = context.actor_config.graph_descriptor
assert gd is not None, "graph_descriptor is None"
nodes = gd.get("nodes", [])
assert len(nodes) == count, f"Expected {count} nodes, got {len(nodes)}: {nodes}"
@then("the parsed graph_descriptor edge count should be {count:d}")
def step_check_graph_descriptor_edges(context: Context, count: int) -> None:
gd = context.actor_config.graph_descriptor
assert gd is not None, "graph_descriptor is None"
edges = gd.get("edges", [])
assert len(edges) == count, f"Expected {count} edges, got {len(edges)}: {edges}"
+4
View File
@@ -408,10 +408,14 @@ class ActorConfiguration(BaseModel):
if isinstance(actor_type, str) and actor_type.lower() == "graph":
route_raw = data.get("route")
if isinstance(route_raw, dict):
nodes_raw = route_raw.get("nodes", [])
edges_raw = route_raw.get("edges", [])
graph_descriptor = {
"type": "graph",
"route": route_raw,
"model": model_value or "",
"nodes": nodes_raw,
"edges": edges_raw,
}
return provider_value or "custom", model_value, graph_descriptor, unsafe_flag
+2 -4
View File
@@ -526,10 +526,8 @@ def _print_actor(
options_count = len(blob.get("options", {}) or {})
graph_descriptor = actor.graph_descriptor or {}
route = graph_descriptor.get("route", {}) or {}
nodes_count = len(route.get("nodes", []) or [])
edges_count = len(route.get("edges", []) or [])
nodes_count = len(graph_descriptor.get("nodes", []) or [])
edges_count = len(graph_descriptor.get("edges", []) or [])
config_details = (
f"[bold]Path:[/bold] {path_str}\n"