feat(create_executor): implement create_executor() factory and Executor.execute() returning ActorResult #38

Merged
hurui200320 merged 1 commit from feature/create-executor-api into master 2026-06-10 10:18:35 +00:00
Member

Summary

Implement the router-facing create_executor() factory function and Executor class with async execute() method returning ActorResult. This PR completes the extraction of dispatch methods into runtime_dispatch.py to achieve file size compliance, full credential isolation, and stateless execution.

What This PR Does

Production Code

  • src/cleveractors/runtime_types.py (43 lines) — ActorResult and NodeUsage dataclasses extracted to break circular imports between runtime.py and runtime_dispatch.py (CONTRIBUTING.md §Import Guidelines). 100% coverage.
  • src/cleveractors/runtime.py (141 lines) — create_executor() factory, Executor class with execute() dispatching to module-level functions in runtime_dispatch. Imports _execute_* at module level (no function-local imports). 100% coverage.
  • src/cleveractors/runtime_dispatch.py (499 lines) — Four dispatch functions: _execute_llm, _execute_graph, _execute_tool, _execute_multi_actor. All imports at module level except from cleveractors.runtime import Executor inside _execute_multi_actor (the only remaining circular dep — Executor.execute() calls _execute_multi_actor which creates a new Executor; cannot be resolved without further restructuring). 99.69% coverage (only the if TYPE_CHECKING: guard line is uncovered — not a real gap).
  • src/cleveractors/runtime_tokens.py — Token estimation via tiktoken with heuristic fallback. 100% coverage.

Key Design Decisions

  • Circular import resolution via runtime_types.py: ActorResult and NodeUsage moved to runtime_types.py. Both runtime.py and runtime_dispatch.py import from it at module level. The only remaining function-local import is from cleveractors.runtime import Executor inside _execute_multi_actor, which is genuinely circular and cannot be avoided without further restructuring.
  • messages forwarded to _execute_llm: _execute_llm now accepts messages and builds context = {"conversation_history": [...]} passed to LLMAgent.process_message. Verified by BDD scenario asserting process_message receives the context.
  • parallel_execution aligned with PureGraphConfig default: Reads from legacy route dict or v2.0 routes.main dict; defaults to True (matching the PureGraphConfig dataclass default). Verified by BDD scenario asserting PureGraphConfig receives parallel_execution=False when configured.
  • Factory normalization: Always merges actors into agents (actors take precedence) so configs using both keys (legacy agents + v2.0 actors) work correctly.
  • Generic exception in agent creation re-raised as ConfigurationError: No double-logging; exception message preserved in ConfigurationError.
  • cleveragents_block guarded against None: Uses or {} coercion.
  • Type annotations: top_provider, top_model, top_sp, temperature_raw, max_tokens_raw in _execute_llm; config_block, tools in _execute_tool.
  • No finally in _execute_tool: Documented with comment (ToolAgent has no cleanup method).

Feature Coverage (BDD)

  • _execute_llm conversation history forwarding verified by process_message call args
  • parallel_execution=False override verified via PureGraphConfig constructor args
  • AC7 immutability for multi-actor path verified (config_dict unchanged after execution)
  • NodeType fallback scenario fixed: bad_node has no agent key so invalid type string reaches NodeType(...) and triggers the except (ValueError, TypeError) branch
  • Agent creation failure scenario: mock_factory_inst.create_agent raises RuntimeError, asserts ConfigurationError raised
  • Dead step step_rxe_invalid_node_type removed (was unreferenced and incorrect)

Quality Gates

  • nox -e lint — passes
  • nox -e format — passes
  • nox -e typecheck — passes (0 errors, 1 expected warning)
  • nox -e unit_tests — 2113 scenarios pass, 0 failures, 0 skipped
  • nox -e integration_tests — 76 tests pass
  • nox -e coverage_report97.21% (9870/10153 lines, ≥ 97% threshold)
    • runtime_types.py: 100.00%
    • runtime.py: 100.00%
    • runtime_dispatch.py: 99.69% (only TYPE_CHECKING guard line uncovered)
    • runtime_tokens.py: 100.00%

Deferred Items

  • M5 (remaining circular import): The from cleveractors.runtime import Executor inside _execute_multi_actor is the only remaining function-local import. It cannot be eliminated without extracting Executor to a separate module AND restructuring _execute_multi_actor to not create sub-executors directly — both are out of scope for this ticket.
  • n2 (estimate_tokens unused provider param): Removing it would require updating all call sites. Deferred to a follow-up cleanup ticket.

Closes #13

## Summary Implement the router-facing `create_executor()` factory function and `Executor` class with async `execute()` method returning `ActorResult`. This PR completes the extraction of dispatch methods into `runtime_dispatch.py` to achieve file size compliance, full credential isolation, and stateless execution. ## What This PR Does ### Production Code - **`src/cleveractors/runtime_types.py`** (43 lines) — `ActorResult` and `NodeUsage` dataclasses extracted to break circular imports between `runtime.py` and `runtime_dispatch.py` (CONTRIBUTING.md §Import Guidelines). **100% coverage.** - **`src/cleveractors/runtime.py`** (141 lines) — `create_executor()` factory, `Executor` class with `execute()` dispatching to module-level functions in `runtime_dispatch`. Imports `_execute_*` at module level (no function-local imports). **100% coverage.** - **`src/cleveractors/runtime_dispatch.py`** (499 lines) — Four dispatch functions: `_execute_llm`, `_execute_graph`, `_execute_tool`, `_execute_multi_actor`. All imports at module level except `from cleveractors.runtime import Executor` inside `_execute_multi_actor` (the only remaining circular dep — `Executor.execute()` calls `_execute_multi_actor` which creates a new `Executor`; cannot be resolved without further restructuring). **99.69% coverage** (only the `if TYPE_CHECKING:` guard line is uncovered — not a real gap). - **`src/cleveractors/runtime_tokens.py`** — Token estimation via tiktoken with heuristic fallback. **100% coverage.** ### Key Design Decisions - **Circular import resolution via `runtime_types.py`**: `ActorResult` and `NodeUsage` moved to `runtime_types.py`. Both `runtime.py` and `runtime_dispatch.py` import from it at module level. The only remaining function-local import is `from cleveractors.runtime import Executor` inside `_execute_multi_actor`, which is genuinely circular and cannot be avoided without further restructuring. - **`messages` forwarded to `_execute_llm`**: `_execute_llm` now accepts `messages` and builds `context = {"conversation_history": [...]}` passed to `LLMAgent.process_message`. Verified by BDD scenario asserting `process_message` receives the context. - **`parallel_execution` aligned with `PureGraphConfig` default**: Reads from legacy `route` dict or v2.0 `routes.main` dict; defaults to `True` (matching the `PureGraphConfig` dataclass default). Verified by BDD scenario asserting `PureGraphConfig` receives `parallel_execution=False` when configured. - **Factory normalization**: Always merges `actors` into `agents` (actors take precedence) so configs using both keys (legacy `agents` + v2.0 `actors`) work correctly. - **Generic exception in agent creation re-raised as `ConfigurationError`**: No double-logging; exception message preserved in `ConfigurationError`. - **`cleveragents_block` guarded against `None`**: Uses `or {}` coercion. - **Type annotations**: `top_provider`, `top_model`, `top_sp`, `temperature_raw`, `max_tokens_raw` in `_execute_llm`; `config_block`, `tools` in `_execute_tool`. - **No `finally` in `_execute_tool`**: Documented with comment (ToolAgent has no cleanup method). ### Feature Coverage (BDD) - `_execute_llm` conversation history forwarding verified by `process_message` call args - `parallel_execution=False` override verified via `PureGraphConfig` constructor args - AC7 immutability for multi-actor path verified (config_dict unchanged after execution) - NodeType fallback scenario fixed: `bad_node` has no `agent` key so invalid type string reaches `NodeType(...)` and triggers the `except (ValueError, TypeError)` branch - Agent creation failure scenario: `mock_factory_inst.create_agent` raises `RuntimeError`, asserts `ConfigurationError` raised - Dead step `step_rxe_invalid_node_type` removed (was unreferenced and incorrect) ### Quality Gates - ✅ `nox -e lint` — passes - ✅ `nox -e format` — passes - ✅ `nox -e typecheck` — passes (0 errors, 1 expected warning) - ✅ `nox -e unit_tests` — 2113 scenarios pass, 0 failures, 0 skipped - ✅ `nox -e integration_tests` — 76 tests pass - ✅ `nox -e coverage_report` — **97.21%** (9870/10153 lines, ≥ 97% threshold) - `runtime_types.py`: 100.00% - `runtime.py`: 100.00% - `runtime_dispatch.py`: 99.69% (only `TYPE_CHECKING` guard line uncovered) - `runtime_tokens.py`: 100.00% ### Deferred Items - **M5 (remaining circular import)**: The `from cleveractors.runtime import Executor` inside `_execute_multi_actor` is the only remaining function-local import. It cannot be eliminated without extracting `Executor` to a separate module AND restructuring `_execute_multi_actor` to not create sub-executors directly — both are out of scope for this ticket. - **n2 (`estimate_tokens` unused `provider` param)**: Removing it would require updating all call sites. Deferred to a follow-up cleanup ticket. Closes #13
hurui200320 added this to the v2.1.0 milestone 2026-06-08 12:27:57 +00:00
hurui200320 force-pushed feature/create-executor-api from 05cb9beb04
Some checks failed
CI / lint (pull_request) Failing after 44s
CI / security (pull_request) Successful in 48s
CI / quality (pull_request) Successful in 48s
CI / typecheck (pull_request) Successful in 51s
CI / build (pull_request) Successful in 38s
CI / integration_tests (pull_request) Successful in 59s
CI / unit_tests (pull_request) Successful in 3m39s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
to 8921a0e9bf
Some checks failed
CI / lint (pull_request) Failing after 47s
CI / quality (pull_request) Successful in 46s
CI / typecheck (pull_request) Successful in 49s
CI / security (pull_request) Successful in 51s
CI / build (pull_request) Successful in 52s
CI / integration_tests (pull_request) Successful in 1m19s
CI / unit_tests (pull_request) Successful in 3m54s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 4s
2026-06-08 15:34:12 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from 8921a0e9bf
Some checks failed
CI / lint (pull_request) Failing after 47s
CI / quality (pull_request) Successful in 46s
CI / typecheck (pull_request) Successful in 49s
CI / security (pull_request) Successful in 51s
CI / build (pull_request) Successful in 52s
CI / integration_tests (pull_request) Successful in 1m19s
CI / unit_tests (pull_request) Successful in 3m54s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 4s
to 68c3fb7f9d
Some checks failed
CI / quality (pull_request) Successful in 42s
CI / build (pull_request) Successful in 48s
CI / lint (pull_request) Failing after 1m1s
CI / typecheck (pull_request) Successful in 1m2s
CI / security (pull_request) Successful in 1m0s
CI / integration_tests (pull_request) Successful in 1m9s
CI / unit_tests (pull_request) Successful in 3m44s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
2026-06-08 16:28:10 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from 68c3fb7f9d
Some checks failed
CI / quality (pull_request) Successful in 42s
CI / build (pull_request) Successful in 48s
CI / lint (pull_request) Failing after 1m1s
CI / typecheck (pull_request) Successful in 1m2s
CI / security (pull_request) Successful in 1m0s
CI / integration_tests (pull_request) Successful in 1m9s
CI / unit_tests (pull_request) Successful in 3m44s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
to f6ce512fb5
Some checks failed
CI / lint (pull_request) Failing after 43s
CI / quality (pull_request) Successful in 51s
CI / typecheck (pull_request) Successful in 52s
CI / build (pull_request) Successful in 1m2s
CI / security (pull_request) Successful in 1m4s
CI / integration_tests (pull_request) Successful in 1m16s
CI / unit_tests (pull_request) Successful in 3m42s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
2026-06-09 03:13:10 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from f6ce512fb5
Some checks failed
CI / lint (pull_request) Failing after 43s
CI / quality (pull_request) Successful in 51s
CI / typecheck (pull_request) Successful in 52s
CI / build (pull_request) Successful in 1m2s
CI / security (pull_request) Successful in 1m4s
CI / integration_tests (pull_request) Successful in 1m16s
CI / unit_tests (pull_request) Successful in 3m42s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
to ff2658e55e
Some checks failed
CI / lint (pull_request) Failing after 1m15s
CI / typecheck (pull_request) Successful in 1m16s
CI / security (pull_request) Successful in 1m15s
CI / build (pull_request) Successful in 1m15s
CI / quality (pull_request) Successful in 1m23s
CI / integration_tests (pull_request) Successful in 1m56s
CI / unit_tests (pull_request) Successful in 4m44s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 5s
2026-06-09 05:24:53 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from ff2658e55e
Some checks failed
CI / lint (pull_request) Failing after 1m15s
CI / typecheck (pull_request) Successful in 1m16s
CI / security (pull_request) Successful in 1m15s
CI / build (pull_request) Successful in 1m15s
CI / quality (pull_request) Successful in 1m23s
CI / integration_tests (pull_request) Successful in 1m56s
CI / unit_tests (pull_request) Successful in 4m44s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 5s
to c623054253
Some checks failed
CI / quality (pull_request) Successful in 44s
CI / lint (pull_request) Failing after 53s
CI / typecheck (pull_request) Successful in 54s
CI / security (pull_request) Successful in 58s
CI / integration_tests (pull_request) Successful in 57s
CI / build (pull_request) Successful in 34s
CI / unit_tests (pull_request) Successful in 3m42s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
2026-06-09 06:52:23 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from c623054253
Some checks failed
CI / quality (pull_request) Successful in 44s
CI / lint (pull_request) Failing after 53s
CI / typecheck (pull_request) Successful in 54s
CI / security (pull_request) Successful in 58s
CI / integration_tests (pull_request) Successful in 57s
CI / build (pull_request) Successful in 34s
CI / unit_tests (pull_request) Successful in 3m42s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
to 362b30bae0
Some checks failed
CI / quality (pull_request) Successful in 1m25s
CI / lint (pull_request) Failing after 1m29s
CI / build (pull_request) Successful in 1m28s
CI / typecheck (pull_request) Successful in 1m41s
CI / security (pull_request) Successful in 1m45s
CI / integration_tests (pull_request) Successful in 1m53s
CI / unit_tests (pull_request) Successful in 4m45s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 8s
2026-06-09 07:30:20 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from 362b30bae0
Some checks failed
CI / quality (pull_request) Successful in 1m25s
CI / lint (pull_request) Failing after 1m29s
CI / build (pull_request) Successful in 1m28s
CI / typecheck (pull_request) Successful in 1m41s
CI / security (pull_request) Successful in 1m45s
CI / integration_tests (pull_request) Successful in 1m53s
CI / unit_tests (pull_request) Successful in 4m45s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 8s
to bc585e227d
Some checks failed
CI / build (pull_request) Successful in 58s
CI / quality (pull_request) Successful in 1m1s
CI / lint (pull_request) Failing after 1m15s
CI / typecheck (pull_request) Successful in 1m14s
CI / security (pull_request) Successful in 1m12s
CI / integration_tests (pull_request) Successful in 1m10s
CI / unit_tests (pull_request) Successful in 3m55s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
2026-06-09 09:41:05 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from bc585e227d
Some checks failed
CI / build (pull_request) Successful in 58s
CI / quality (pull_request) Successful in 1m1s
CI / lint (pull_request) Failing after 1m15s
CI / typecheck (pull_request) Successful in 1m14s
CI / security (pull_request) Successful in 1m12s
CI / integration_tests (pull_request) Successful in 1m10s
CI / unit_tests (pull_request) Successful in 3m55s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
to edd9af1c4f
Some checks failed
CI / lint (pull_request) Failing after 49s
CI / security (pull_request) Successful in 48s
CI / quality (pull_request) Successful in 53s
CI / build (pull_request) Successful in 47s
CI / typecheck (pull_request) Successful in 1m0s
CI / integration_tests (pull_request) Successful in 1m5s
CI / unit_tests (pull_request) Failing after 3m47s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
2026-06-09 11:01:29 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from edd9af1c4f
Some checks failed
CI / lint (pull_request) Failing after 49s
CI / security (pull_request) Successful in 48s
CI / quality (pull_request) Successful in 53s
CI / build (pull_request) Successful in 47s
CI / typecheck (pull_request) Successful in 1m0s
CI / integration_tests (pull_request) Successful in 1m5s
CI / unit_tests (pull_request) Failing after 3m47s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
to 50a7fc5f74
Some checks failed
CI / lint (pull_request) Successful in 1m9s
CI / typecheck (pull_request) Successful in 1m9s
CI / security (pull_request) Successful in 1m7s
CI / build (pull_request) Successful in 45s
CI / quality (pull_request) Successful in 56s
CI / integration_tests (pull_request) Successful in 1m22s
CI / unit_tests (pull_request) Failing after 3m54s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
2026-06-09 12:46:51 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from 50a7fc5f74
Some checks failed
CI / lint (pull_request) Successful in 1m9s
CI / typecheck (pull_request) Successful in 1m9s
CI / security (pull_request) Successful in 1m7s
CI / build (pull_request) Successful in 45s
CI / quality (pull_request) Successful in 56s
CI / integration_tests (pull_request) Successful in 1m22s
CI / unit_tests (pull_request) Failing after 3m54s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
to d1e5a8716c
Some checks failed
CI / lint (pull_request) Successful in 1m7s
CI / typecheck (pull_request) Successful in 1m7s
CI / security (pull_request) Successful in 1m5s
CI / quality (pull_request) Successful in 47s
CI / integration_tests (pull_request) Successful in 1m13s
CI / build (pull_request) Successful in 48s
CI / unit_tests (pull_request) Successful in 3m47s
CI / coverage (pull_request) Failing after 3m40s
CI / status-check (pull_request) Failing after 3s
2026-06-09 16:35:05 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from d1e5a8716c
Some checks failed
CI / lint (pull_request) Successful in 1m7s
CI / typecheck (pull_request) Successful in 1m7s
CI / security (pull_request) Successful in 1m5s
CI / quality (pull_request) Successful in 47s
CI / integration_tests (pull_request) Successful in 1m13s
CI / build (pull_request) Successful in 48s
CI / unit_tests (pull_request) Successful in 3m47s
CI / coverage (pull_request) Failing after 3m40s
CI / status-check (pull_request) Failing after 3s
to 139c99f6a3
All checks were successful
CI / quality (pull_request) Successful in 36s
CI / lint (pull_request) Successful in 38s
CI / typecheck (pull_request) Successful in 58s
CI / build (pull_request) Successful in 1m27s
CI / security (pull_request) Successful in 1m31s
CI / integration_tests (pull_request) Successful in 1m54s
CI / unit_tests (pull_request) Successful in 3m34s
CI / coverage (pull_request) Successful in 3m34s
CI / status-check (pull_request) Successful in 7s
2026-06-10 05:52:21 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from 139c99f6a3
All checks were successful
CI / quality (pull_request) Successful in 36s
CI / lint (pull_request) Successful in 38s
CI / typecheck (pull_request) Successful in 58s
CI / build (pull_request) Successful in 1m27s
CI / security (pull_request) Successful in 1m31s
CI / integration_tests (pull_request) Successful in 1m54s
CI / unit_tests (pull_request) Successful in 3m34s
CI / coverage (pull_request) Successful in 3m34s
CI / status-check (pull_request) Successful in 7s
to 25ea6774b4
All checks were successful
CI / quality (pull_request) Successful in 34s
CI / lint (pull_request) Successful in 38s
CI / typecheck (pull_request) Successful in 58s
CI / security (pull_request) Successful in 1m2s
CI / integration_tests (pull_request) Successful in 1m3s
CI / build (pull_request) Successful in 37s
CI / unit_tests (pull_request) Successful in 3m40s
CI / coverage (pull_request) Successful in 3m40s
CI / status-check (pull_request) Successful in 2s
2026-06-10 07:55:24 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from 25ea6774b4
All checks were successful
CI / quality (pull_request) Successful in 34s
CI / lint (pull_request) Successful in 38s
CI / typecheck (pull_request) Successful in 58s
CI / security (pull_request) Successful in 1m2s
CI / integration_tests (pull_request) Successful in 1m3s
CI / build (pull_request) Successful in 37s
CI / unit_tests (pull_request) Successful in 3m40s
CI / coverage (pull_request) Successful in 3m40s
CI / status-check (pull_request) Successful in 2s
to 2cd8acfc7d
Some checks failed
CI / lint (pull_request) Failing after 44s
CI / build (pull_request) Successful in 44s
CI / quality (pull_request) Successful in 46s
CI / integration_tests (pull_request) Successful in 1m15s
CI / typecheck (pull_request) Successful in 1m17s
CI / security (pull_request) Successful in 1m17s
CI / unit_tests (pull_request) Successful in 3m48s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
2026-06-10 08:46:47 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from 2cd8acfc7d
Some checks failed
CI / lint (pull_request) Failing after 44s
CI / build (pull_request) Successful in 44s
CI / quality (pull_request) Successful in 46s
CI / integration_tests (pull_request) Successful in 1m15s
CI / typecheck (pull_request) Successful in 1m17s
CI / security (pull_request) Successful in 1m17s
CI / unit_tests (pull_request) Successful in 3m48s
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
to b139cf9787
All checks were successful
CI / lint (pull_request) Successful in 39s
CI / build (pull_request) Successful in 37s
CI / quality (pull_request) Successful in 46s
CI / security (pull_request) Successful in 48s
CI / typecheck (pull_request) Successful in 54s
CI / integration_tests (pull_request) Successful in 56s
CI / unit_tests (pull_request) Successful in 3m37s
CI / coverage (pull_request) Successful in 3m36s
CI / status-check (pull_request) Successful in 4s
2026-06-10 08:56:38 +00:00
Compare
hurui200320 force-pushed feature/create-executor-api from b139cf9787
All checks were successful
CI / lint (pull_request) Successful in 39s
CI / build (pull_request) Successful in 37s
CI / quality (pull_request) Successful in 46s
CI / security (pull_request) Successful in 48s
CI / typecheck (pull_request) Successful in 54s
CI / integration_tests (pull_request) Successful in 56s
CI / unit_tests (pull_request) Successful in 3m37s
CI / coverage (pull_request) Successful in 3m36s
CI / status-check (pull_request) Successful in 4s
to 76c4c74201
All checks were successful
CI / lint (pull_request) Successful in 36s
CI / quality (pull_request) Successful in 40s
CI / build (pull_request) Successful in 44s
CI / security (pull_request) Successful in 51s
CI / typecheck (pull_request) Successful in 52s
CI / integration_tests (pull_request) Successful in 1m3s
CI / unit_tests (pull_request) Successful in 3m38s
CI / coverage (pull_request) Successful in 3m36s
CI / status-check (pull_request) Successful in 3s
CI / lint (push) Successful in 1m2s
CI / typecheck (push) Successful in 1m2s
CI / security (push) Successful in 1m2s
CI / quality (push) Successful in 40s
CI / integration_tests (push) Successful in 1m8s
CI / build (push) Successful in 49s
CI / unit_tests (push) Successful in 3m40s
CI / coverage (push) Successful in 3m36s
CI / status-check (push) Successful in 3s
2026-06-10 09:41:50 +00:00
Compare
Author
Member

Self-QA Review: Approved

This PR went through 4 automated self-QA review/fix cycles. All blocking issues have been resolved. Full implementation notes are in ticket #13.

What Was Verified

All 7 acceptance criteria from ticket #13 are satisfied:

  • AC1create_executor() is module-level, no file/env I/O
  • AC2AgentFactory(credentials=...) used; config_dict passed unmodified
  • AC3limits and pricing stored on Executor
  • AC4execute(message) returns ActorResult with estimated tokens
  • AC5_usage_log.clear() at start of execute() prevents state leaks
  • AC6create_executor exported from __init__.py and __all__
  • AC7copy.deepcopy() used in all dispatch paths; config_dict never mutated

Key Issues Fixed During Self-QA

  • actorsagents normalization for v2.0 configs (always merges, actors takes precedence)
  • system_prompt fallback uses DEFAULT_SYSTEM_MESSAGE with is None check (not or operator)
  • messages (conversation history) forwarded to _execute_llm and passed to LLMAgent.process_message
  • parallel_execution reads from both legacy route dict and v2.0 routes.main dict (defaults to True)
  • Exception chains restored (from exc) for graph and tool dispatch paths
  • ActorResult/NodeUsage extracted to runtime_types.py to break circular imports
  • Generic exception in agent creation re-raises as ConfigurationError (no silent swallow)
  • Mixed actors+agents config keys handled correctly

Quality Gates

Gate Result
nox -e lint Pass
nox -e format Pass
nox -e typecheck Pass (0 errors)
nox -e unit_tests Pass (2113 scenarios, 0 failures)
nox -e integration_tests Pass (76 tests)
nox -e coverage_report Pass (97.21% ≥ 97% threshold)
CI run #28770 Pass (8m41s)

Remaining Deferred Items

  1. Circular import for Executor inside _execute_multi_actor — genuine circular dep, documented in PR description, out of scope for this ticket.
  2. estimate_tokens unused provider parameter — deferred to a follow-up cleanup ticket.
## Self-QA Review: Approved ✅ This PR went through 4 automated self-QA review/fix cycles. All blocking issues have been resolved. Full implementation notes are in ticket #13. ### What Was Verified All 7 acceptance criteria from ticket #13 are satisfied: - **AC1** — `create_executor()` is module-level, no file/env I/O ✅ - **AC2** — `AgentFactory(credentials=...)` used; `config_dict` passed unmodified ✅ - **AC3** — `limits` and `pricing` stored on `Executor` ✅ - **AC4** — `execute(message)` returns `ActorResult` with estimated tokens ✅ - **AC5** — `_usage_log.clear()` at start of `execute()` prevents state leaks ✅ - **AC6** — `create_executor` exported from `__init__.py` and `__all__` ✅ - **AC7** — `copy.deepcopy()` used in all dispatch paths; `config_dict` never mutated ✅ ### Key Issues Fixed During Self-QA - `actors`→`agents` normalization for v2.0 configs (always merges, `actors` takes precedence) - `system_prompt` fallback uses `DEFAULT_SYSTEM_MESSAGE` with `is None` check (not `or` operator) - `messages` (conversation history) forwarded to `_execute_llm` and passed to `LLMAgent.process_message` - `parallel_execution` reads from both legacy `route` dict and v2.0 `routes.main` dict (defaults to `True`) - Exception chains restored (`from exc`) for graph and tool dispatch paths - `ActorResult`/`NodeUsage` extracted to `runtime_types.py` to break circular imports - Generic exception in agent creation re-raises as `ConfigurationError` (no silent swallow) - Mixed `actors`+`agents` config keys handled correctly ### Quality Gates | Gate | Result | |------|--------| | `nox -e lint` | ✅ Pass | | `nox -e format` | ✅ Pass | | `nox -e typecheck` | ✅ Pass (0 errors) | | `nox -e unit_tests` | ✅ Pass (2113 scenarios, 0 failures) | | `nox -e integration_tests` | ✅ Pass (76 tests) | | `nox -e coverage_report` | ✅ Pass (**97.21%** ≥ 97% threshold) | | CI run #28770 | ✅ Pass (8m41s) | ### Remaining Deferred Items 1. **Circular import for `Executor`** inside `_execute_multi_actor` — genuine circular dep, documented in PR description, out of scope for this ticket. 2. **`estimate_tokens` unused `provider` parameter** — deferred to a follow-up cleanup ticket.
Author
Member

PR #38 Review: feat(create_executor): implement create_executor() factory and Executor.execute() returning ActorResult


1. Does it implement what ticket #13 requires?

Short answer: Yes — all 7 ACs and all 10 subtasks are addressed.

AC Requirement Status
AC1 create_executor() callable without file I/O or env vars Module-level function, purely dict-driven
AC2 AgentFactory(credentials=credentials) used; unmodified config_dict passed All three credential-requiring paths use AgentFactory. Old _build_factory_config() that mutated a copy and injected creds is gone.
AC3 limits and pricing stored on Executor for future C5/C6 use Both stored in __init__
AC4 execute(message) -> ActorResult; token counts may be estimated Async, returns ActorResult, uses estimate_tokens() / estimate_graph_tokens()
AC5 No mutable state leaks between execute() calls self._usage_log.clear() at the top of every execute(). Crucially, messages is no longer stored on self (old master had self.messages = messages or [] which persisted across calls)
AC6 create_executor exported from __init__.py and __all__ Verified in __init__.py
AC7 config_dict never modified with credentials _execute_llm deep-copies executor.config.get("config", {}) before building factory_cfg. _execute_graph uses copy.deepcopy(executor.config). _execute_multi_actor passes copy.deepcopy(sub_config) to the sub-executor.

The key fix — replacing the old credential injection path — is correct. Old master's _execute_llm built agent_config with raw API keys injected and constructed LLMAgent directly. Old master's _execute_graph called _build_factory_config() which injected creds into a deep copy but still violated the spirit of AC2/AC7. The new code passes credentials to AgentFactory at construction time, cleanly, in all paths.


⚠️ 2. Do the changes break anything?

All 2113 BDD scenarios pass. However, there are 4 behavioral changes relative to master that downstream callers should be aware of:

🟡 B1 — parallel_execution default changed: FalseTrue

Where: _execute_graph in runtime_dispatch.py

Old master had an explicit comment:

parallel_execution=False,  # safer default

The PR now defaults to True (matching PureGraphConfig's dataclass default). Any graph actor that does not explicitly set parallel_execution in its config will now run with parallelism enabled. This changes execution semantics (ordering, race conditions) for existing graph actors that implicitly relied on sequential execution. This is intentional per the PR, but worth noting as a semantics change.

🟡 B2 — from/to edge keys dropped (backward compat broken)

Where: Edge parsing in _execute_graph

Old master supported both "from"/"to" (legacy) and "source"/"target":

source=edge_def.get("from", edge_def.get("source", "")),
target=edge_def.get("to", edge_def.get("target", "")),

New code only accepts "source" and "target" — missing either now raises ConfigurationError. Any config still using the legacy "from"/"to" edge format will break. The test suite clearly doesn't have such cases, but production configs might.

🟡 B3 — NodeUsage.node_id for graph actors changed

Where: _execute_graph, return value construction

Old master: node_id=entry_point (e.g., "start")
New PR: node_id="graph" (static string)

Downstream consumers that inspect ActorResult.nodes[0].node_id for graph executions (e.g., the router for logging/billing) will see "graph" instead of the actual entry-point name. Minor, but worth knowing.

🟢 B4 — _execute_tool no longer passes context to ToolAgent (probably fine)

Old master passed a context dict (possibly with conversation_history) to agent.process_message(message, context). The new code calls agent.process_message(message) — no context at all. The docstring says "ToolAgent has no cleanup() method; no finally block needed" but doesn't address the context removal. This is safe as long as no ToolAgent implementation uses context, which appears to be the case since tests pass.


🐛 Bug fixes included (good things)

Two pre-existing bugs in master are fixed by this PR:

  1. _execute_multi_actor sub-config not deep-copied — old master passed sub_config (a reference into executor.config) directly to the sub-executor, violating AC7. Fixed by copy.deepcopy(sub_config).

  2. _execute_multi_actor parent _usage_log never updated — old master mutated result.nodes in-place with prefixed IDs but never extended the parent's _usage_log. New code correctly does executor._usage_log.extend(result.nodes) before prefixing, giving accurate un-prefixed token tracking in the parent.

  3. _execute_graph silent agent creation failure — old master caught Exception during agent creation and only logged a warning (then silently continued), meaning missing agents would cause later graph execution errors. New code wraps that in ConfigurationError. Strictly speaking this is a breaking change (failure happens earlier), but it's the right behavior.


Verdict

The implementation is correct and complete. All ticket requirements are satisfied; the critical credential-injection flaw from master is properly fixed.

The main things to verify with the team before merging are B1 (parallel_execution=True by default) and B2 (dropping from/to edge aliases). If there are any graph actor configs in production (or in the router) that don't set parallel_execution explicitly, or that use legacy from/to edge keys, those would need to be updated. Everything else is clean.

## PR #38 Review: `feat(create_executor): implement create_executor() factory and Executor.execute() returning ActorResult` --- ### ✅ 1. Does it implement what ticket #13 requires? **Short answer: Yes — all 7 ACs and all 10 subtasks are addressed.** | AC | Requirement | Status | |---|---|---| | AC1 | `create_executor()` callable without file I/O or env vars | ✅ Module-level function, purely dict-driven | | AC2 | `AgentFactory(credentials=credentials)` used; unmodified `config_dict` passed | ✅ All three credential-requiring paths use `AgentFactory`. Old `_build_factory_config()` that mutated a copy and injected creds is gone. | | AC3 | `limits` and `pricing` stored on `Executor` for future C5/C6 use | ✅ Both stored in `__init__` | | AC4 | `execute(message) -> ActorResult`; token counts may be estimated | ✅ Async, returns `ActorResult`, uses `estimate_tokens()` / `estimate_graph_tokens()` | | AC5 | No mutable state leaks between `execute()` calls | ✅ `self._usage_log.clear()` at the top of every `execute()`. Crucially, `messages` is no longer stored on `self` (old master had `self.messages = messages or []` which persisted across calls) | | AC6 | `create_executor` exported from `__init__.py` and `__all__` | ✅ Verified in `__init__.py` | | AC7 | `config_dict` never modified with credentials | ✅ `_execute_llm` deep-copies `executor.config.get("config", {})` before building `factory_cfg`. `_execute_graph` uses `copy.deepcopy(executor.config)`. `_execute_multi_actor` passes `copy.deepcopy(sub_config)` to the sub-executor. | The key fix — replacing the old credential injection path — is correct. Old master's `_execute_llm` built `agent_config` with raw API keys injected and constructed `LLMAgent` directly. Old master's `_execute_graph` called `_build_factory_config()` which injected creds into a deep copy but still violated the spirit of AC2/AC7. The new code passes credentials to `AgentFactory` at construction time, cleanly, in all paths. --- ### ⚠️ 2. Do the changes break anything? All 2113 BDD scenarios pass. However, there are **4 behavioral changes relative to master** that downstream callers should be aware of: #### 🟡 B1 — `parallel_execution` default changed: `False` → `True` **Where:** `_execute_graph` in `runtime_dispatch.py` Old master had an explicit comment: ```python parallel_execution=False, # safer default ``` The PR now defaults to `True` (matching `PureGraphConfig`'s dataclass default). Any graph actor that does **not** explicitly set `parallel_execution` in its config will now run with parallelism enabled. This changes execution semantics (ordering, race conditions) for existing graph actors that implicitly relied on sequential execution. This is intentional per the PR, but worth noting as a semantics change. #### 🟡 B2 — `from`/`to` edge keys dropped (backward compat broken) **Where:** Edge parsing in `_execute_graph` Old master supported both `"from"`/`"to"` (legacy) and `"source"`/`"target"`: ```python source=edge_def.get("from", edge_def.get("source", "")), target=edge_def.get("to", edge_def.get("target", "")), ``` New code only accepts `"source"` and `"target"` — missing either now raises `ConfigurationError`. Any config still using the legacy `"from"`/`"to"` edge format will break. The test suite clearly doesn't have such cases, but production configs might. #### 🟡 B3 — `NodeUsage.node_id` for graph actors changed **Where:** `_execute_graph`, return value construction Old master: `node_id=entry_point` (e.g., `"start"`) New PR: `node_id="graph"` (static string) Downstream consumers that inspect `ActorResult.nodes[0].node_id` for graph executions (e.g., the router for logging/billing) will see `"graph"` instead of the actual entry-point name. Minor, but worth knowing. #### 🟢 B4 — `_execute_tool` no longer passes context to ToolAgent (probably fine) Old master passed a `context` dict (possibly with `conversation_history`) to `agent.process_message(message, context)`. The new code calls `agent.process_message(message)` — no context at all. The docstring says `"ToolAgent has no cleanup() method; no finally block needed"` but doesn't address the context removal. This is safe as long as no `ToolAgent` implementation uses context, which appears to be the case since tests pass. --- ### 🐛 Bug fixes included (good things) Two pre-existing bugs in master are fixed by this PR: 1. **`_execute_multi_actor` sub-config not deep-copied** — old master passed `sub_config` (a reference into `executor.config`) directly to the sub-executor, violating AC7. Fixed by `copy.deepcopy(sub_config)`. 2. **`_execute_multi_actor` parent `_usage_log` never updated** — old master mutated `result.nodes` in-place with prefixed IDs but never extended the parent's `_usage_log`. New code correctly does `executor._usage_log.extend(result.nodes)` before prefixing, giving accurate un-prefixed token tracking in the parent. 3. **`_execute_graph` silent agent creation failure** — old master caught `Exception` during agent creation and only logged a `warning` (then silently continued), meaning missing agents would cause later graph execution errors. New code wraps that in `ConfigurationError`. Strictly speaking this is a breaking change (failure happens earlier), but it's the right behavior. --- ### Verdict **The implementation is correct and complete.** All ticket requirements are satisfied; the critical credential-injection flaw from master is properly fixed. The main things to verify with the team before merging are **B1** (`parallel_execution=True` by default) and **B2** (dropping `from`/`to` edge aliases). If there are any graph actor configs in production (or in the router) that don't set `parallel_execution` explicitly, or that use legacy `from`/`to` edge keys, those would need to be updated. Everything else is clean.
hurui200320 deleted branch feature/create-executor-api 2026-06-10 10:18:35 +00:00
Sign in to join this conversation.
No reviewers
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/cleveractors-core!38
No description provided.