BUG: agents actor run returns empty output — ReactiveConfigParser synthesises agents but no execution route for type:llm actors #10807

Closed
opened 2026-04-21 07:55:10 +00:00 by hurui200320 · 2 comments
Member

Metadata

  • Commit Message: fix(reactive): synthesise execution route for type:llm actors in ReactiveConfigParser
  • Branch: bugfix/m3-actor-run-missing-llm-route

Background

This is a follow-up gap discovered after the combined fix branch was assembled from four in-review PRs addressing the bug: "agents actor run silently returns empty output":

  • #5869 (merged)agents actor add --config uses v2 ActorConfiguration parser — v3 ActorConfigSchema not validated on registration
  • #4466 (In Review / PR #10796)ActorRegistry.add() rejects spec-compliant actor YAML (flat vs actors: block mismatch at registry layer)
  • #6283 (In Review / PR #9921) — Actor CLI ignores v3 Actor YAML schema (CLI still uses legacy reactive format) — the critical Layer 2 fix
  • #6511 (In Review / PR #10795) — All graph node stream subscriptions have no-op on_next handlers — node execution pipeline entirely disconnected

Even with all four fixes applied, agents actor run local/<actor> "<prompt>" still exits 0 with empty output and makes no LLM call.


Root Cause

Two compounding defects in ReactiveConfigParser (src/cleveragents/reactive/config_parser.py):

Defect 1 — _build_from_v3() creates agents but no route for type:llm actors

When _is_v3_format() detects a flat v3 YAML (top-level type: llm), it calls _build_from_v3(). For type:llm actors, this method synthesises a single AgentConfig and adds it to rc.agents, but creates no RouteConfig in rc.routes:

# _build_from_v3() — current (broken) path for type:llm
if actor_type in ("llm", "tool"):
    rc.agents[actor_name] = AgentConfig(name=actor_name, type=actor_type, config=agent_config)
    # ← rc.routes stays empty — no route is created

In run_single_shot():

  1. No graph route → _get_graph_route() returns None
  2. Falls through to the RxPY stream path
  3. __input__ stream has no subscribers (no routes → _build_routes() is a no-op)
  4. Prompt is published but nothing reads it
  5. result_container stays empty → returns ""

Confirmed with a direct Python console test on temp-bug2-combined:

parser = ReactiveConfigParser()
flat_v3_blob = {"type": "llm", "name": "test", "model": "anthropic/claude-sonnet-4-5", "description": "..."}
rc = parser._build(flat_v3_blob)
# rc.agents = {'test': AgentConfig(...)},  rc.routes = {}   ← routes is empty

Defect 2 — Nested actors: map format (spec-compliant test-plan YAML) never reaches the v3 path

The spec-compliant YAML format used by the rune-actor.md test plan places type: llm inside an actors: block, not at the top level:

name: local/rune-strategist
cleveragents:
  version: "3.0"
  default_actor: strategist
actors:
  strategist:
    type: llm
    config:
      actor: anthropic/claude-sonnet-4-5
      temperature: 0.3

_is_v3_format() only checks data.get("type") at the top level → returns False. The legacy _build() path is taken, which does parse the actors: key and creates an agent, but also produces routes = {} for the same reason as Defect 1.

parser = ReactiveConfigParser()
rc = parser._build(nested_actors_blob)
# rc.agents = {'strategist': AgentConfig(...)},  rc.routes = {}   ← routes is empty

Both defects result in the identical symptom: the RxPY __input__ stream receives the prompt but has no downstream subscribers, so the LLM is never invoked.

Why the existing four fixes did not catch this

The unit tests for #6283 (PR #9921) validate config parsing correctness (agent fields, provider inference, skills propagation) but do not assert that rc.routes is non-empty for type:llm actors, nor do they exercise the full run_single_shot() execution path through to an actual LLM response. The gap was predicted in the original bug report under "Sufficiency assessment":

"the fix is complete only if #6283's implementation loads the stored flat config_blob directly via ActorConfigSchema.model_validate() in _resolve_actor.py, rather than serialising it back to YAML and re-feeding it to ReactiveConfigParser. If #6283 only adds validation at actor add time without replacing the _resolve_actor.py → ReactiveCleverAgentsApp execution pipeline, a gap remains and a 5th issue targeting _resolve_actor.py will be needed."


Current Behaviour

After registering a v3 type:llm actor (either flat or nested actors: map format) and running:

agents actor run local/rune-strategist "Describe a concise strategy..."

Actual output: (empty — no output at all)
Exit code: 0

No LLM call is made. The RxPY stream receives the prompt but drops it immediately because no route connects __input__ → agent → __output__.


Expected Behaviour

Run Summary
  Actor: local/rune-strategist
  Temperature: 0.3
  Provider: anthropic
  Model: claude-sonnet-4-5

[LLM response text here]

Result Metrics
  Input Tokens: ~200
  Output Tokens: ~300
  Duration: ~2s
  Cost: ~$0.0010

[OK] Run completed

Exit code: 0. Response is non-empty. An LLM call is made.


Proposed Fix

Two targeted changes in src/cleveragents/reactive/config_parser.py:

Fix A — Synthesise a default single-node graph route in _build_from_v3() for type:llm

After creating the agent, add a minimal RouteConfig of type GRAPH with one node so that GraphExecutor can directly invoke the LLM:

if actor_type in ("llm", "tool"):
    rc.agents[actor_name] = AgentConfig(...)
    # NEW: add a single-node graph route so run_single_shot() can call the LLM
    rc.routes[f"{actor_name}_run"] = RouteConfig(
        name=f"{actor_name}_run",
        type=RouteType.GRAPH,
        agents=[actor_name],
        operators=[
            {
                "type": "graph_node",
                "params": {
                    "entry_node": actor_name,
                    "nodes": [{"id": actor_name, "actor": actor_name}],
                    "edges": [],
                },
            }
        ],
    )

(Exact RouteConfig structure to be confirmed against GraphExecutor._parse_topology().)

Fix B — Handle the nested actors: map format (cleveragents version: "3.0")

After the existing actors: map agent loop in _build(), if rc.agents is non-empty and rc.routes is still empty, synthesise the same default route using cleveragents.default_actor (or the first registered agent). This handles both the nested actors: map path and any other format that produces agents without routes:

# After the agents loop in _build():
if rc.agents and not rc.routes:
    cleveragents_meta = data.get("cleveragents") or {}
    default_actor = str(cleveragents_meta.get("default_actor") or "")
    agent_name = (
        default_actor if default_actor and default_actor in rc.agents
        else next(iter(rc.agents))
    )
    rc.routes["__default_run__"] = RouteConfig(
        name="__default_run__",
        type=RouteType.GRAPH,
        agents=[agent_name],
        ...  # same single-node structure as Fix A
    )

Acceptance Criteria

  • agents actor run local/rune-strategist "<prompt>" produces a non-empty LLM response and exits 0 when the actor is registered from the spec-compliant nested actors: map YAML format
  • agents actor run with a flat v3 YAML (type: llm at top level) also produces non-empty output
  • ReactiveConfigParser._build() with any single-agent v3 config produces rc.routes != {}
  • ReactiveConfigParser._build_from_v3() for type:llm produces exactly one route entry in rc.routes
  • Existing actor_v3_schema.feature, actor_registry_spec_yaml.feature, and consolidated_langgraph.feature tests continue to pass without modification
  • agents actor run --output <file> writes non-empty content to the output file
  • agents actor run --temperature 0.1 <name> "<prompt>" correctly overrides temperature

Supporting Information

  • Discovered during functional smoke-test of temp-bug2-combined branch (assembled 2026-04-21) after all four predecessor PRs were merged into a single integration branch
  • Confirmed via direct ReactiveConfigParser._build() console test — both YAML formats produce routes=[]
  • Primary file: src/cleveragents/reactive/config_parser.py — functions _is_v3_format, _build_from_v3, _build
  • Secondary file: src/cleveragents/cli/commands/_resolve_actor.py — serialises the stored actor blob back to YAML and re-feeds it to ReactiveCleverAgentsApp; confirm no additional transform hides the route gap
  • Related test plan: rune-actor.md Calls 17 & 18 (agents actor run invocations)
  • Integration branch: temp-bug2-combined (pushed to remote as temp-bug2-combined)
  • Predecessor tickets: #5869 (merged), #4466 (PR #10796), #6283 (PR #9921), #6511 (PR #10795)

Subtasks

  • Add failing BDD scenario to actor_v3_schema.feature: "v3 llm actor build produces non-empty routes" — assert rc.routes != {} after _build_from_v3() for type:llm
  • Add failing BDD scenario for the nested actors: map format: assert rc.routes != {} after _build() processes a cleveragents.version: "3.0" YAML
  • Implement Fix A: synthesise a default single-node graph route in _build_from_v3() for type:llm and type:tool actors
  • Implement Fix B: synthesise a default route after the agent loop in _build() when rc.agents != {} and rc.routes == {}
  • Add end-to-end BDD scenario using FakeListLLM: register a v3 type:llm actor → run it → assert non-empty output (no real API key required)
  • Add end-to-end BDD scenario for the nested actors: map format using FakeListLLM
  • Verify agents actor run --output <file> and --temperature flags work correctly after the fix
  • Inspect _resolve_actor.py to confirm no additional serialisation step strips routes before they reach run_single_shot()
  • Run nox (all default sessions), fix any regressions
  • Verify coverage ≥ 97% via nox -s coverage_report

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line and implementation details.
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done.
  • agents actor run with a v3 type:llm actor (both flat v3 and nested actors: map formats) produces a non-empty LLM response and exits 0.
## Metadata - **Commit Message**: `fix(reactive): synthesise execution route for type:llm actors in ReactiveConfigParser` - **Branch**: `bugfix/m3-actor-run-missing-llm-route` --- ## Background This is a follow-up gap discovered after the combined fix branch was assembled from four in-review PRs addressing the bug: "agents actor run silently returns empty output": - **#5869** *(merged)* — `agents actor add --config` uses v2 `ActorConfiguration` parser — v3 `ActorConfigSchema` not validated on registration - **#4466** *(In Review / PR #10796)* — `ActorRegistry.add()` rejects spec-compliant actor YAML (flat vs `actors:` block mismatch at registry layer) - **#6283** *(In Review / PR #9921)* — Actor CLI ignores v3 Actor YAML schema (CLI still uses legacy reactive format) — **the critical Layer 2 fix** - **#6511** *(In Review / PR #10795)* — All graph node stream subscriptions have no-op `on_next` handlers — node execution pipeline entirely disconnected Even with all four fixes applied, `agents actor run local/<actor> "<prompt>"` still exits 0 with empty output and makes no LLM call. --- ## Root Cause **Two compounding defects in `ReactiveConfigParser` (`src/cleveragents/reactive/config_parser.py`):** ### Defect 1 — `_build_from_v3()` creates agents but no route for `type:llm` actors When `_is_v3_format()` detects a flat v3 YAML (top-level `type: llm`), it calls `_build_from_v3()`. For `type:llm` actors, this method synthesises a single `AgentConfig` and adds it to `rc.agents`, but **creates no `RouteConfig`** in `rc.routes`: ```python # _build_from_v3() — current (broken) path for type:llm if actor_type in ("llm", "tool"): rc.agents[actor_name] = AgentConfig(name=actor_name, type=actor_type, config=agent_config) # ← rc.routes stays empty — no route is created ``` In `run_single_shot()`: 1. No graph route → `_get_graph_route()` returns `None` 2. Falls through to the RxPY stream path 3. `__input__` stream has no subscribers (no routes → `_build_routes()` is a no-op) 4. Prompt is published but nothing reads it 5. `result_container` stays empty → returns `""` Confirmed with a direct Python console test on `temp-bug2-combined`: ```python parser = ReactiveConfigParser() flat_v3_blob = {"type": "llm", "name": "test", "model": "anthropic/claude-sonnet-4-5", "description": "..."} rc = parser._build(flat_v3_blob) # rc.agents = {'test': AgentConfig(...)}, rc.routes = {} ← routes is empty ``` ### Defect 2 — Nested `actors:` map format (spec-compliant test-plan YAML) never reaches the v3 path The spec-compliant YAML format used by the `rune-actor.md` test plan places `type: llm` inside an `actors:` block, not at the top level: ```yaml name: local/rune-strategist cleveragents: version: "3.0" default_actor: strategist actors: strategist: type: llm config: actor: anthropic/claude-sonnet-4-5 temperature: 0.3 ``` `_is_v3_format()` only checks `data.get("type")` at the **top level** → returns `False`. The legacy `_build()` path is taken, which does parse the `actors:` key and creates an agent, but also produces `routes = {}` for the same reason as Defect 1. ```python parser = ReactiveConfigParser() rc = parser._build(nested_actors_blob) # rc.agents = {'strategist': AgentConfig(...)}, rc.routes = {} ← routes is empty ``` Both defects result in the identical symptom: the RxPY `__input__` stream receives the prompt but has no downstream subscribers, so the LLM is never invoked. ### Why the existing four fixes did not catch this The unit tests for #6283 (PR #9921) validate config **parsing** correctness (agent fields, provider inference, skills propagation) but do not assert that `rc.routes` is non-empty for `type:llm` actors, nor do they exercise the full `run_single_shot()` execution path through to an actual LLM response. The gap was predicted in the original bug report under "Sufficiency assessment": > *"the fix is complete only if #6283's implementation loads the stored flat config_blob directly via `ActorConfigSchema.model_validate()` in `_resolve_actor.py`, rather than serialising it back to YAML and re-feeding it to `ReactiveConfigParser`. If #6283 only adds validation at `actor add` time without replacing the `_resolve_actor.py → ReactiveCleverAgentsApp` execution pipeline, a gap remains and a 5th issue targeting `_resolve_actor.py` will be needed."* --- ## Current Behaviour After registering a v3 `type:llm` actor (either flat or nested `actors:` map format) and running: ```bash agents actor run local/rune-strategist "Describe a concise strategy..." ``` **Actual output:** *(empty — no output at all)* **Exit code:** 0 No LLM call is made. The RxPY stream receives the prompt but drops it immediately because no route connects `__input__` → agent → `__output__`. --- ## Expected Behaviour ``` Run Summary Actor: local/rune-strategist Temperature: 0.3 Provider: anthropic Model: claude-sonnet-4-5 [LLM response text here] Result Metrics Input Tokens: ~200 Output Tokens: ~300 Duration: ~2s Cost: ~$0.0010 [OK] Run completed ``` Exit code: 0. Response is non-empty. An LLM call is made. --- ## Proposed Fix Two targeted changes in `src/cleveragents/reactive/config_parser.py`: ### Fix A — Synthesise a default single-node graph route in `_build_from_v3()` for `type:llm` After creating the agent, add a minimal `RouteConfig` of type `GRAPH` with one node so that `GraphExecutor` can directly invoke the LLM: ```python if actor_type in ("llm", "tool"): rc.agents[actor_name] = AgentConfig(...) # NEW: add a single-node graph route so run_single_shot() can call the LLM rc.routes[f"{actor_name}_run"] = RouteConfig( name=f"{actor_name}_run", type=RouteType.GRAPH, agents=[actor_name], operators=[ { "type": "graph_node", "params": { "entry_node": actor_name, "nodes": [{"id": actor_name, "actor": actor_name}], "edges": [], }, } ], ) ``` *(Exact `RouteConfig` structure to be confirmed against `GraphExecutor._parse_topology()`.)* ### Fix B — Handle the nested `actors:` map format (cleveragents `version: "3.0"`) After the existing `actors:` map agent loop in `_build()`, if `rc.agents` is non-empty and `rc.routes` is still empty, synthesise the same default route using `cleveragents.default_actor` (or the first registered agent). This handles both the nested `actors:` map path and any other format that produces agents without routes: ```python # After the agents loop in _build(): if rc.agents and not rc.routes: cleveragents_meta = data.get("cleveragents") or {} default_actor = str(cleveragents_meta.get("default_actor") or "") agent_name = ( default_actor if default_actor and default_actor in rc.agents else next(iter(rc.agents)) ) rc.routes["__default_run__"] = RouteConfig( name="__default_run__", type=RouteType.GRAPH, agents=[agent_name], ... # same single-node structure as Fix A ) ``` --- ## Acceptance Criteria - [ ] `agents actor run local/rune-strategist "<prompt>"` produces a non-empty LLM response and exits 0 when the actor is registered from the spec-compliant nested `actors:` map YAML format - [ ] `agents actor run` with a flat v3 YAML (`type: llm` at top level) also produces non-empty output - [ ] `ReactiveConfigParser._build()` with any single-agent v3 config produces `rc.routes != {}` - [ ] `ReactiveConfigParser._build_from_v3()` for `type:llm` produces exactly one route entry in `rc.routes` - [ ] Existing `actor_v3_schema.feature`, `actor_registry_spec_yaml.feature`, and `consolidated_langgraph.feature` tests continue to pass without modification - [ ] `agents actor run --output <file>` writes non-empty content to the output file - [ ] `agents actor run --temperature 0.1 <name> "<prompt>"` correctly overrides temperature --- ## Supporting Information - **Discovered** during functional smoke-test of `temp-bug2-combined` branch (assembled 2026-04-21) after all four predecessor PRs were merged into a single integration branch - **Confirmed** via direct `ReactiveConfigParser._build()` console test — both YAML formats produce `routes=[]` - **Primary file**: `src/cleveragents/reactive/config_parser.py` — functions `_is_v3_format`, `_build_from_v3`, `_build` - **Secondary file**: `src/cleveragents/cli/commands/_resolve_actor.py` — serialises the stored actor blob back to YAML and re-feeds it to `ReactiveCleverAgentsApp`; confirm no additional transform hides the route gap - **Related test plan**: `rune-actor.md` Calls 17 & 18 (`agents actor run` invocations) - **Integration branch**: `temp-bug2-combined` (pushed to remote as `temp-bug2-combined`) - **Predecessor tickets**: #5869 (merged), #4466 (PR #10796), #6283 (PR #9921), #6511 (PR #10795) --- ## Subtasks - [ ] Add failing BDD scenario to `actor_v3_schema.feature`: "v3 llm actor build produces non-empty routes" — assert `rc.routes != {}` after `_build_from_v3()` for `type:llm` - [ ] Add failing BDD scenario for the nested `actors:` map format: assert `rc.routes != {}` after `_build()` processes a `cleveragents.version: "3.0"` YAML - [ ] Implement Fix A: synthesise a default single-node graph route in `_build_from_v3()` for `type:llm` and `type:tool` actors - [ ] Implement Fix B: synthesise a default route after the agent loop in `_build()` when `rc.agents != {}` and `rc.routes == {}` - [ ] Add end-to-end BDD scenario using `FakeListLLM`: register a v3 `type:llm` actor → run it → assert non-empty output (no real API key required) - [ ] Add end-to-end BDD scenario for the nested `actors:` map format using `FakeListLLM` - [ ] Verify `agents actor run --output <file>` and `--temperature` flags work correctly after the fix - [ ] Inspect `_resolve_actor.py` to confirm no additional serialisation step strips routes before they reach `run_single_shot()` - [ ] Run `nox` (all default sessions), fix any regressions - [ ] Verify coverage ≥ 97% via `nox -s coverage_report` --- ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line and implementation details. - The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly. - The commit is submitted as a pull request to `master`, reviewed, and **merged** before this issue is marked done. - `agents actor run` with a v3 `type:llm` actor (both flat v3 and nested `actors:` map formats) produces a non-empty LLM response and exits 0.
hurui200320 added this to the v3.2.0 milestone 2026-04-21 07:55:21 +00:00
Author
Member

Implementation Notes

Approach

WIP PR #10818 — This branch merges 3 unmerged dependency PRs (#9921, #10796, #10795) as scaffolding, then implements the fix on top. Once those PRs merge to master, this branch will be rebased and cleaned up.

Design Decisions

  1. Route structure: The synthesised route uses a message_router node (catch-all rule targeting the actor) + an actor node + an edge to end. This was necessary because GraphExecutor.execute() requires router_rules to be non-empty — a single "agent" node without a router would cause execute() to return "" immediately. This matches the existing graph route structure used by working test scenarios in reactive_application_coverage_steps.py.

  2. Shared helper: _synthesise_single_node_route() is a module-level function used by both Fix A (_build_from_v3) and Fix B (_build). This avoids duplication and ensures consistent route structure.

  3. Fix B default_actor resolution: When the nested actors: map format is used, the route agent is chosen from cleveragents.default_actor if set and valid, otherwise falls back to the first agent in rc.agents. This matches the spec's intent for default_actor.

  4. No changes to _resolve_actor.py: Confirmed that _resolve_actor.py serialises config_blob to YAML and re-parses it through ReactiveConfigParser._build(). Routes are synthesised at parse time, not stored in config_blob, so no serialisation step strips them.

Merge Artifacts Fixed

  • ActorRegistry.add() had a duplicate allow_unsafe parameter from the merge of PRs #9921 and #10796
  • _add_legacy() was missing the unsafe parameter (function was split from add() by PR #10796)
  • config.py line-too-long from combining v3 unsafe flag resolution

Quality Gate Results

Gate Result
lint
typecheck 0 errors
unit_tests 15,424 scenarios, 0 failed
integration_tests 1,990 tests, 0 failed
e2e_tests ⚠️ 3 pre-existing M6 failures
coverage ≥97%

Key Locations

  • cleveragents.reactive.config_parser._synthesise_single_node_route (commit b0795e14) — shared route synthesis helper
  • cleveragents.reactive.config_parser.ReactiveConfigParser._build_from_v3 (commit b0795e14) — Fix A call site
  • cleveragents.reactive.config_parser.ReactiveConfigParser._build (commit b0795e14) — Fix B insertion point
  • features/actor_v3_route_synthesis.feature — 10 BDD scenarios
## Implementation Notes ### Approach **WIP PR #10818** — This branch merges 3 unmerged dependency PRs (#9921, #10796, #10795) as scaffolding, then implements the fix on top. Once those PRs merge to master, this branch will be rebased and cleaned up. ### Design Decisions 1. **Route structure**: The synthesised route uses a `message_router` node (catch-all rule targeting the actor) + an `actor` node + an edge to `end`. This was necessary because `GraphExecutor.execute()` requires `router_rules` to be non-empty — a single "agent" node without a router would cause `execute()` to return `""` immediately. This matches the existing graph route structure used by working test scenarios in `reactive_application_coverage_steps.py`. 2. **Shared helper**: `_synthesise_single_node_route()` is a module-level function used by both Fix A (`_build_from_v3`) and Fix B (`_build`). This avoids duplication and ensures consistent route structure. 3. **Fix B `default_actor` resolution**: When the nested `actors:` map format is used, the route agent is chosen from `cleveragents.default_actor` if set and valid, otherwise falls back to the first agent in `rc.agents`. This matches the spec's intent for `default_actor`. 4. **No changes to `_resolve_actor.py`**: Confirmed that `_resolve_actor.py` serialises `config_blob` to YAML and re-parses it through `ReactiveConfigParser._build()`. Routes are synthesised at parse time, not stored in `config_blob`, so no serialisation step strips them. ### Merge Artifacts Fixed - `ActorRegistry.add()` had a duplicate `allow_unsafe` parameter from the merge of PRs #9921 and #10796 - `_add_legacy()` was missing the `unsafe` parameter (function was split from `add()` by PR #10796) - `config.py` line-too-long from combining v3 unsafe flag resolution ### Quality Gate Results | Gate | Result | |------|--------| | lint | ✅ | | typecheck | ✅ 0 errors | | unit_tests | ✅ 15,424 scenarios, 0 failed | | integration_tests | ✅ 1,990 tests, 0 failed | | e2e_tests | ⚠️ 3 pre-existing M6 failures | | coverage | ✅ ≥97% | ### Key Locations - `cleveragents.reactive.config_parser._synthesise_single_node_route` (commit `b0795e14`) — shared route synthesis helper - `cleveragents.reactive.config_parser.ReactiveConfigParser._build_from_v3` (commit `b0795e14`) — Fix A call site - `cleveragents.reactive.config_parser.ReactiveConfigParser._build` (commit `b0795e14`) — Fix B insertion point - `features/actor_v3_route_synthesis.feature` — 10 BDD scenarios
Author
Member

Implementation Notes — Rebase + Fix C

Rebase onto latest master

Rebuilt the branch fresh from latest master (b3bfbc1d). Dependency PR status:

  • PR #10795 (issue #6511) — Now merged to master; its scaffolding merge is no longer needed.
  • PR #9921 (issue #6283) — Still open; merged as scaffolding commit.
  • PR #10796 (issue #4466) — Still open; merged as scaffolding commit with conflict resolution.

Merge conflicts in actor/config.py (unsafe flag coercion) and actor/registry.py (duplicate allow_unsafe parameter, missing unsafe in _add_legacy()) were resolved by combining PR #9921's v3_unsafe flag with PR #10796's safer is True or == 1 boolean coercion pattern.

Fix C — actor: "provider/model" translation (ReactiveConfigParser._build())

Root cause: The _build() agents loop passed the raw v3 config dict as-is to AgentConfig. The v3 spec stores the model as actor: "anthropic/claude-sonnet-4-5" inside each actor's config: block. SimpleLLMAgent._resolve_llm() expects separate provider and model keys. When the actor key is present but provider/model are absent, the LLM provider defaults to None → OpenAI → 401 error.

Fix: In ReactiveConfigParser._build(), after extracting raw_config from agent_data.get("config", {}), check for the actor key. When present and provider is absent:

  • If the value contains /, split into provider and model
  • If no /, set model only (no provider inference)

This is a ~10-line change entirely within _build(), does not affect the _build_from_v3() path (which already correctly sets provider and model from the top-level flat v3 format).

Key code location: ReactiveConfigParser._build() in src/cleveragents/reactive/config_parser.py, the agents loop (comment: Fix #10807-C).

Tests

3 new BDD scenarios added to features/actor_v3_route_synthesis.feature:

  • Nested actors map with actor key infers provider and model — verifies actor: "anthropic/claude-sonnet-4-5"provider: "anthropic", model: "claude-sonnet-4-5"
  • Nested actors map with actor key without slash sets model only — verifies actor: "gpt-4"model: "gpt-4", no provider
  • Nested actors map with explicit provider keeps it unchanged — verifies explicit provider/model are not overwritten

Coverage boost

Added 17 BDD scenarios for A2aStdioTransport (features/a2a_stdio_transport.feature) to compensate for the coverage impact of the newly merged feat(a2a): implement A2A stdio transport for local mode commit on master.

Quality gates

All gates pass: lint , typecheck , 15,445 unit tests , 1,990 integration tests , coverage ≥97% .

## Implementation Notes — Rebase + Fix C ### Rebase onto latest master Rebuilt the branch fresh from latest master (`b3bfbc1d`). Dependency PR status: - **PR #10795** (issue #6511) — ✅ Now merged to master; its scaffolding merge is no longer needed. - **PR #9921** (issue #6283) — Still open; merged as scaffolding commit. - **PR #10796** (issue #4466) — Still open; merged as scaffolding commit with conflict resolution. Merge conflicts in `actor/config.py` (unsafe flag coercion) and `actor/registry.py` (duplicate `allow_unsafe` parameter, missing `unsafe` in `_add_legacy()`) were resolved by combining PR #9921's `v3_unsafe` flag with PR #10796's safer `is True or == 1` boolean coercion pattern. ### Fix C — `actor: "provider/model"` translation (`ReactiveConfigParser._build()`) **Root cause**: The `_build()` agents loop passed the raw v3 config dict as-is to `AgentConfig`. The v3 spec stores the model as `actor: "anthropic/claude-sonnet-4-5"` inside each actor's `config:` block. `SimpleLLMAgent._resolve_llm()` expects separate `provider` and `model` keys. When the `actor` key is present but `provider`/`model` are absent, the LLM provider defaults to `None` → OpenAI → 401 error. **Fix**: In `ReactiveConfigParser._build()`, after extracting `raw_config` from `agent_data.get("config", {})`, check for the `actor` key. When present and `provider` is absent: - If the value contains `/`, split into `provider` and `model` - If no `/`, set `model` only (no provider inference) This is a ~10-line change entirely within `_build()`, does not affect the `_build_from_v3()` path (which already correctly sets `provider` and `model` from the top-level flat v3 format). **Key code location**: `ReactiveConfigParser._build()` in `src/cleveragents/reactive/config_parser.py`, the agents loop (comment: `Fix #10807-C`). ### Tests 3 new BDD scenarios added to `features/actor_v3_route_synthesis.feature`: - `Nested actors map with actor key infers provider and model` — verifies `actor: "anthropic/claude-sonnet-4-5"` → `provider: "anthropic"`, `model: "claude-sonnet-4-5"` - `Nested actors map with actor key without slash sets model only` — verifies `actor: "gpt-4"` → `model: "gpt-4"`, no provider - `Nested actors map with explicit provider keeps it unchanged` — verifies explicit `provider`/`model` are not overwritten ### Coverage boost Added 17 BDD scenarios for `A2aStdioTransport` (`features/a2a_stdio_transport.feature`) to compensate for the coverage impact of the newly merged `feat(a2a): implement A2A stdio transport for local mode` commit on master. ### Quality gates All gates pass: lint ✅, typecheck ✅, 15,445 unit tests ✅, 1,990 integration tests ✅, coverage ≥97% ✅.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveragents-core#10807
No description provided.