fix(v3.7.0): resolve issue #1432 #1488

Merged
HAL9000 merged 3 commits from fix/1432-lsp into master 2026-05-30 05:00:40 +00:00
7 changed files with 144 additions and 1 deletions
+6
View File
@@ -6,6 +6,12 @@ Changed `wf10_batch.robot` to be less likely to create files, and
`plan_generation_graph.robot` to give more test answers.
## [Unreleased]
- **Fix actor compiler to read LSP bindings from typed `lsp_binding` field** (#1488): Fixed
`_extract_lsp_bindings()` in `actor/compiler.py` to read from `node.lsp_binding` (the typed
`NodeLspBinding` Pydantic field on `NodeDefinition`) as the primary path, with
backward-compatible fallback to the legacy `lsp_bindings` config dict key. Per-node LSP
bindings specified via the `lsp_binding:` YAML key are no longer silently dropped. Includes
Behave BDD and Robot Framework regression tests (#1432).
- **TDD regression tests for automation_profile DI bypass** (#1031): Added BDD scenarios
and Robot Framework integration tests verifying that ``_get_service()`` in
``automation_profile.py`` resolves ``AutomationProfileService`` through the DI container
+5
View File
@@ -48,4 +48,9 @@ Below are some of the specific details of various contributions.
* HAL 9000 has contributed the DecisionService wiring for PlanExecutor strategize persistence fix (#10813): added decision_service to the PlanExecutor constructor and wired it from the CLI dependency-injection container in `_get_plan_executor()`, plus implemented `_persist_strategy_decisions()` to persist strategy decisions as domain `Decision` objects.
* HAL 9000 has contributed the A2A module rename standardization BDD tests (PR #10583 / issue #8615): comprehensive Behave test suite validating that all 22 A2A symbols are properly exported from `cleveragents.a2a`, no legacy ACP references remain in the module source, and documentation uses correct A2A naming conventions — fixing inline imports, unused behave symbols, cross-scenario context dependencies, and missing type annotations.
* HAL 9000 has contributed the `ActorSelectionOverlay._render``_refresh_display` rename fix (PR #11176 / issue #11039, Epic #8174): renamed `_render()` method to `_refresh_display()` to avoid shadowing Textual's `Widget._render()`, fixing a crash in textual >=1.0 where `get_content_height()` would receive `None` and raise `AttributeError: 'NoneType' object has no attribute 'get_height'`.
* HAL 9000 has contributed the actor compiler LSP binding fix (PR #1488 / issue #1432): fixed
`_extract_lsp_bindings()` to read `node.lsp_binding` (the typed `NodeLspBinding` field on
`NodeDefinition`) as the primary extraction path instead of only checking the untyped config
dict, so per-node LSP bindings specified via `lsp_binding:` YAML key are no longer silently
dropped. Includes Behave BDD and Robot Framework regression tests.
* HAL 9000 has contributed the config-actor combined-format support fix (PR #11232 / issue #11189): added ``_detect_nested_config_actor()``, ``_flatten_config_actor()``, and handling in ``ActorConfiguration.from_blob()`` to transparently flatten the nested ``config.actor`` block from both compact-string and nested-dict forms so v3 detection, schema validation, and canonicalisation see flat data — eliminating the ``"provider is required"`` crash.
+22
View File
@@ -0,0 +1,22 @@
Feature: Actor compiler reads LSP bindings from NodeDefinition.lsp_binding typed field
As a CleverAgents developer
I want the actor compiler to read LSP bindings from the typed lsp_binding field
So that per-node LSP bindings are not silently dropped when specified via lsp_binding: YAML key
Background:
Given the actor compiler is available
@tdd_issue @tdd_issue_1432
Scenario: Node with lsp_binding typed field populates CompilationMetadata.lsp_bindings
Given a GRAPH actor config with a node using the lsp_binding typed field
When I compile the actor config
Then the compilation should succeed
And the compilation metadata should have 1 LSP binding
And the LSP binding should reference server "local/pyright"
@tdd_issue @tdd_issue_1432
Scenario: Node without lsp_binding field produces empty lsp_bindings in metadata
Given a GRAPH actor config with a single node
When I compile the actor config
Then the compilation should succeed
And the compilation metadata should have 0 LSP bindings
+22
View File
@@ -23,6 +23,7 @@ from cleveragents.actor.schema import (
ActorType,
EdgeDefinition,
NodeDefinition,
NodeLspBinding,
NodeType,
RouteDefinition,
)
@@ -133,6 +134,27 @@ def step_given_lsp_bindings(context: Context) -> None:
)
@given("a GRAPH actor config with a node using the lsp_binding typed field")
def step_given_lsp_typed_field(context: Context) -> None:
nodes = [
NodeDefinition(
id="coder",
type=NodeType.AGENT,
name="Coder",
description="Writes code with LSP support",
config={"agent": "coder_agent"},
lsp_binding=NodeLspBinding(
server="local/pyright",
languages=["python"],
auto=False,
),
),
]
context.actor_config = _build_graph_config(
"workflows/lsp_typed", nodes, [], "coder", ["coder"]
)
@given("a GRAPH actor config with a conditional routing node")
def step_given_conditional(context: Context) -> None:
nodes = [
+13
View File
@@ -54,3 +54,16 @@ Detect Cross-Actor Subgraph Cycle Via actor_ref Field
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} actor-compiler-cycle-detected
Extract LSP Binding From Typed lsp_binding Field
[Documentation] Verify that a node with a typed lsp_binding field populates
... CompilationMetadata.lsp_bindings — regression test for issue #1432:
... _extract_lsp_bindings() was reading only node.config.get("lsp_bindings")
... instead of the NodeLspBinding typed field on NodeDefinition.
[Tags] slow
${result}= Run Process ${PYTHON} ${HELPER} lsp-binding-field cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} actor-compiler-lsp-binding-ok
Should Contain ${result.stdout} lsp-server: local/pyright
+49
View File
@@ -142,6 +142,55 @@ def main() -> int:
print(f"actor-compiler-unexpected-error: {exc}")
return 1
if command == "lsp-binding-field":
# Regression test for issue #1432: _extract_lsp_bindings() must read
# from node.lsp_binding (NodeLspBinding typed field), not only config dict.
from cleveragents.actor.schema import (
ActorType,
NodeDefinition,
NodeLspBinding,
NodeType,
RouteDefinition,
)
node = NodeDefinition(
id="coder",
type=NodeType.AGENT,
name="Coder",
description="Code writing node with typed LSP binding",
config={"agent": "coder_agent"},
lsp_binding=NodeLspBinding(
server="local/pyright",
languages=["python"],
auto=False,
),
)
route = RouteDefinition(
nodes=[node],
edges=[],
entry_node="coder",
exit_nodes=["coder"],
)
config = ActorConfigSchema(
name="workflows/lsp_typed",
type=ActorType.GRAPH,
description="Test actor with typed lsp_binding field",
model="gpt-4",
route=route,
)
try:
compiled = compile_actor(config)
bindings = compiled.metadata.lsp_bindings
if len(bindings) == 1 and bindings[0].lsp_server_name == "local/pyright":
print("actor-compiler-lsp-binding-ok")
print(f"lsp-server: {bindings[0].lsp_server_name}")
return 0
print(f"actor-compiler-lsp-binding-fail: got {bindings}")
return 1
except Exception as exc:
print(f"actor-compiler-fail: {exc}")
return 1
print(f"Unknown command: {command}")
return 1
+27 -1
View File
@@ -156,11 +156,36 @@ def _map_edge(edge: EdgeDefinition) -> lg_nodes.Edge:
def _extract_lsp_bindings(node: NodeDefinition) -> list[LspBinding]:
Review

test

test
Review

Suggestion: add @tdd_issue_1432 Behave regression test for per-node LSP binding extraction. Required by subtask 3.

Suggestion: add @tdd_issue_1432 Behave regression test for per-node LSP binding extraction. Required by subtask 3.
"""Extract LSP bindings from a node config block."""
"""Extract LSP bindings from a node's dedicated lsp_binding field.
Reads from the typed ``node.lsp_binding`` (NodeLspBinding) field and
converts to LspBinding records. Also checks config dict for backward
compatibility with legacy ``lsp_bindings`` config key.
Args:
node: The node definition to extract bindings from.
Returns:
List of LspBinding records for this node.
"""
Outdated
Review

Suggestion: add Robot Framework integration test verifying CompilationMetadata.lsp_bindings population. Required by subtask 4.

Suggestion: add Robot Framework integration test verifying CompilationMetadata.lsp_bindings population. Required by subtask 4.
bindings: list[LspBinding] = []
# Primary path: read from the dedicated typed field (NodeLspBinding)
if node.lsp_binding is not None:
bindings.append(
LspBinding(
node_name=node.id,
lsp_server_name=node.lsp_binding.server or "",
languages=node.lsp_binding.languages or [],
auto_detect=node.lsp_binding.auto,
)
)
# Fallback path: check config dict for backward compatibility
raw_bindings = node.config.get("lsp_bindings", [])
if not isinstance(raw_bindings, list):
return bindings
for entry in raw_bindings:
if not isinstance(entry, dict):
continue
@@ -175,6 +200,7 @@ def _extract_lsp_bindings(node: NodeDefinition) -> list[LspBinding]:
auto_detect=entry.get("auto_detect", True),
)
)
return bindings