Add max_child_depth field to DecompositionConfig (default: 5, matching
plan.max-child-depth config) with validation in __post_init__. Update
_build_hierarchy() in DecompositionService to stop recursion and emit
a warning when the depth limit is reached.
BDD tests verify: max_child_depth guards trigger before max_depth
when more restrictive, warnings are logged, default config value is
correct, and invalid values are rejected.
ISSUES CLOSED: #10269
Wire subplan_service from the DI container into _get_plan_executor() so
PlanExecutor can spawn child plans during the Execute phase. When
SubplanService is available but SubplanExecutionService is not explicitly
injected, _execute_subplans() now lazily creates a SubplanExecutionService
on-the-fly using the parent plan's subplan_config and the new
_execute_child_plan callback.
- _get_plan_executor(): retrieve subplan_service from container, pass to
PlanExecutor constructor
- _execute_subplans(): lazily build SubplanExecutionService when only
SubplanService is wired (Forgejo #10268)
- _execute_child_plan(): new PlanExecutor method that runs a child plan's
strategize + execute phases, registered as executor_fn callback
ISSUES CLOSED: #10268
## Summary
This PR fixes issue #10878 where architecture reviews were truncated because the regex pattern for parsing file output would stop at the first ``` encountered in the Markdown report.
## Changes
- Change file delimiters from ``` to >>>>>>>/<<<<<<< to avoid Markdown conflicts
- Add tier hydration before strategize phase in plan_executor.py
- Increase max_tokens to 16384 in llm_actors.py for longer outputs
- Increase context_max_tokens_hot from 16000 to 32000 in settings.py
- Fix get_hot_view → get_hot_fragments in strategy_actor.py and plan_executor.py
- Add opencode to skip directories in context_tier_hydrator.py
- Change sandbox output location to plan-output/ directory in plan.py
- Add get_context_summary stub method to acms_service.py
## Testing
Run architecture review action and verify the output report is complete with all sections.
Reviewed-on: cleveragents/cleveragents-core#10938
Implements the full tool-calling path for `agents actor run --skill` so that LLM
actors can actually invoke tools through the ToolCallingRuntime loop when a skill
is attached.
Core changes:
- reactive/tool_caller.py (new): ToolCallingLLMCaller implements the LLMCaller protocol;
binds tool schemas via bind_tools(), threads SystemMessage+HumanMessage on first call
and AIMessage+ToolMessages on subsequent calls, extracts tool calls from LangChain
responses following the LangChainSessionCaller pattern.
- reactive/tool_caller.py: bidirectional tool name encoding via uppercase sentinels
(_encode_tool_name / _decode_tool_name) to make CleverAgents namespaced tool names
("builtin/file-read", "server:local/tool") compatible with Anthropic's tool name
pattern ^[a-zA-Z0-9_-]{1,128}$. Uses _C_ for ":" and _S_ for "/" — uppercase
sentinels are safe because valid CleverAgents tool names forbid uppercase letters.
Encoding applied in _resolve_llm() before bind_tools(); decoding applied in invoke()
when extracting tool calls from the LLM response.
- reactive/tool_agent.py (new): ToolCallingAgent builds a per-run local ToolRegistry from
resolved skill tool entries by looking up names in the shared builtin_registry; drives
ToolCallingRuntime.run_tool_loop(); exposes last_result for tool_calls surfacing.
- reactive/application.py: (ST-1) _make_agent_instance() now always merges skill tools
instead of silently dropping them when actor has no base tools list; routes
tools+llm→ToolCallingAgent, tools+non-llm→SimpleToolAgent, no-tools+llm→SimpleLLMAgent;
(ST-4) _builtin_registry created at startup with register_file_tools/git/subplan;
(ST-6) _tally_tool_calls() + last_run_tool_calls property.
- reactive/graph_executor.py: (ST-5) ToolCallingAgent added to isinstance check in
_invoke_agent() so context dict is forwarded for Jinja2 rendering.
- cli/commands/actor_run.py: prints "Tool Calls: {n}" when > 0.
Test fixes:
- features/steps/actor_cli_run_steps.py: _make_app() sets last_run_tool_calls=0 to avoid
MagicMock>int TypeError in Python 3.13.
- features/steps/actor_run_signature_resolve_steps.py: same fix.
- robot/helper_actor_run_signature.py: same fix.
- features/reactive_application_coverage_boost.feature: updated scenario to verify new
correct behavior (LLM+skills → ToolCallingAgent, not silently kept as SimpleLLMAgent).
BDD coverage: 34 scenarios in features/actor_run_tool_calling.feature covering
tool call success, multi-turn loop, no-skill regression, silent-drop fix, LLMCaller
internals, _build_tool_registry edge cases, last_run_tool_calls tallying,
tool name encoding/decoding, and LLM response decoding.
ISSUES CLOSED: #11211
Fixes a critical data integrity bug where validation_name and resource_id
arguments were being silently swapped based on a fragile heuristic when
resource_id contained '/'. This caused silent data corruption without any
error being raised.
The 3-line conditional swap block has been removed from
ValidationAttachmentRepository.attach(), ensuring arguments flow directly
from caller to the persistence layer in the correct order.
Fixed _resolve_hot_max_tokens() to read hot_max_tokens from the correct
sub-key in context_policy_json. The value is stored under
context_policy_json["acms_config"]["hot_max_tokens"] by
'agents project context set --hot-max-tokens', not at the top level.
The previous read (config_dict.get("hot_max_tokens")) always returned
None, causing the assembler to silently use the global 16K default even
when a project-level override was configured.
Also adds two Behave regression scenarios with @tdd_issue @tdd_issue_11035
tags that exercise the real DB query code path via a mocked
NamespacedProjectModel row, verifying:
1. hot_max_tokens=32000 in acms_config is applied to CoreContextBudget
and ContextRequest (override path).
2. Missing hot_max_tokens falls back to the constructor-injected global
default of 4096 (fallback path).
Module-level import json added to steps file; redundant inline MagicMock
import removed.
ISSUES CLOSED: #11035
ISSUES CLOSED: #11215
Two code paths in the reactive actor run pipeline silently discarded the
options: block from v3 actor YAML, preventing custom OpenAI-compatible
backends (llama.cpp, Ollama, etc.) from being used.
Review fixes applied:
- Fix 1: Relabeled issue #11223 from Type/Task to Type/Bug; added
@tdd_issue/@tdd_issue_11223 tags to all 5 Behave scenarios.
- Fix 2: openai_api_key in options now routes through the registry's
__api_key_sentinel mechanism so user-provided keys correctly override
environment defaults. (stream_router.py)
- Fix 3: type: graph actors now propagate actor-level options to
individual node configs via setdefault. (config_parser.py)
- Fix 4: Options keys are validated against an explicit allowlist;
reserved keys (provider_type, model_id) are excluded; unrecognized
keys log a WARNING instead of being silently forwarded. (stream_router.py)
- Fix 5: Updated _build_from_v3 docstring to list options as a
propagated field. (config_parser.py)
- Fix 6: Removed inconsistent and options_raw emptiness guard; empty
options dicts are now preserved consistently. (config_parser.py)
- Fix 7: Reserved keys provider_type and model_id are excluded from
the options merge loop to prevent TypeError. (stream_router.py)
- Fix 8: Added Behave scenario verifying top-level temperature takes
precedence over options duplicate. (consolidated_routing.feature + steps)
- Fix 9: Strengthened "no extra kwargs" assertion to assert kwargs == {}
directly instead of using an allow-list filter. (stream_router steps)
- Fix 10: Strengthened options assertion to exact dict equality.
(actor_v3_schema_extended_steps.py)
- N1: Comment style aligned to M5: prefix convention.
- N2: Type annotations changed from Any to Context (behave.runner).
- N3: Added Behave scenario for empty options: {} dict behavior.
Tests: 5 new Behave scenarios (3 in actor_v3_schema.feature, 2 in
consolidated_routing.feature) with @tdd_issue/@tdd_issue_11223 tags.
ISSUES CLOSED: #11223
Fix formatting issues detected by CI lint check:
- Simplify multi-line decorator arguments to single line
- Simplify multi-line assertion error messages to single line
This resolves the format --check failure blocking CI.
The Behave scenario at line 671 of consolidated_domain_models.feature
asserts 'the effective set should have {count:d} invariants' but no
step handler existed, causing UndefinedStepError and CI failure.
Adds the missing step: @then('the effective set should have {count:d} invariants')
to step_invariant_models_steps.py, mirroring the existing invariant-set count pattern.
The PR #11143 adds action_invariants as a 4th parameter to
merge_invariants() and InvariantSet.merge(), but two call sites
were not updated:
- benchmarks/invariant_merge_bench.py: 5 calls with 3 positional args
- robot/helper_m3_e2e_verification.py: 2 calls using keyword args
All call sites now pass action_invariants=[] for backward-compatible
empty-action behavior.
Fixes list_invariants(effective=True) to forward action_name to
get_effective_invariants when scope is ACTION, ensuring action-scoped
invariants are included in effective invariant lists.
Also applies ruff formatting to the return statement in get_effective_invariants.
Addresses reviewer observation about list_invariants gap.
Refs: #9126
The 4-tier invariant precedence chain (plan > action > project > global) was
broken at the domain layer — merge_invariants() and InvariantSet.merge() only
accepted 3 parameters (plan, project, global), silently dropping all action-
scoped invariants. Added action_invariants as a fourth parameter with proper
backward compatibility (default to empty list). Updated module docstrings,
InvariantScope docstring, and InvariantService.get_effective_invariants() to
reflect the correct precedence chain. Added comprehensive BDD test scenarios
covering four-tier merge precedence, action-before-project ordering, and effective
invariant computation with all four scopes.
ISSUES CLOSED: #9126
Add TokenAuthMiddleware to emit AUTH_SUCCESS/AUTH_FAILURE with spec-aligned audit details and wire it through the DI container using server.token resolution.
Add Behave and Robot coverage for auth event emission and end-to-end audit persistence, and update audit subscriber producer notes and changelog.
ISSUES CLOSED: #714
Added _resolve_effective_budget() method to
ACMSExecutePhaseContextAssembler that reads each linked project's
settings.hot_max_tokens and uses the maximum override value as the
pipeline budget instead of the hardcoded global default.
Updated assemble() to use the resolved effective budget for both
CoreContextBudget and ContextRequest.
Added Behave regression scenario with @tdd_issue @tdd_issue_11035 tags
verifying the pipeline receives the project-level budget.
Removed test artifact ANALYSIS.md left over from scenario 20.
Fixes: #11035Fixes: #11215
The plan tree command reported zero decision nodes after strategize because
PlanExecutor.run_strategize() never persisted strategy decisions as domain
Decision objects. Added _persist_strategy_decisions() to the PlanExecutor,
wired decision_service from the DI container in _get_plan_executor(), and
ensure each strategy decision is recorded with correct DecisionType mapping.
ISSUES CLOSED: #10813
Fixed MCPToolAdapter.infer_resource_slots() to handle null values
in JSON Schema by using instead of
which returned None when the key exists with a null value.
The @tdd_expected_fail tag was removed from TDD issue #10470 since the fix is now applied.
Rename ActorSelectionOverlay._render() 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.
ISSUES CLOSED: #11039
Replace string-based startswith() path traversal guards with robust
Path.relative_to() across three files to prevent prefix-collision
bypass attacks. The old checks using str(target).startswith(str(root))
could be evaded by paths like /tmp/abc123-escape when root is /tmp/abc123.
Files patched:
- src/cleveragents/skills/builtins/file_ops.py (validate_sandbox_path)
- src/cleveragents/resource/handlers/_base.py (_safe_resolve)
- src/cleveragents/application/services/llm_actors.py (_write_to_sandbox)
ISSUES CLOSED: #7478
- Resolve CHANGELOG.md conflict to include both entries, referencing issue #8164 instead of PR #11161
- Resolve CONTRIBUTORS.md conflict, fix leading space in new entry
- Remove duplicate create_sequence_node function (dead code)
Merges master into feat/structural-output-validation branch
Two defects in ActorConfiguration._extract_v3_actor() prevented YAML files
using the spec-canonical nested actors map format from being registered:
Defect A: The method looked for the 'type' field inside the 'config:' block
(config_block.get('type')) instead of at the actor-entry level
(first_entry.get('type')). Because the spec places 'type' at
actors.<name>.type — a sibling of 'config:', not a child of it — the v3
detection branch never fired for nested-actors-map YAMLs, causing
_extract_v3_actor() to return (None, None, None, False) immediately.
Defect B: Even if the type was found, the method only consulted separate
config.provider and config.model keys; it never parsed the combined
config.actor: 'provider/model' shorthand. Without the combined-field
fallback, provider and model remained None for all spec-canonical YAMLs
that use the shorthand, causing from_blob() to raise
'BadParameter: provider is required'.
Both defects had to be fixed together because Defect A blocked the code
from ever reaching the path where Defect B would otherwise have been
encountered.
The combined-format parser (config.actor split on first '/') is inserted
after the explicit config.provider / config.model lookups so that explicit
separate fields always take precedence over the shorthand when both are
present.
Parsing logic extracted into _parse_combined_actor_field() helper to
eliminate DRY violation. Both provider and model halves validated
consistently (empty provider half was previously silently inferred).
Adds Behave scenarios covering: nested actors map + combined config.actor
(success), explicit config.provider precedence over config.actor,
explicit config.model precedence over config.actor, genuinely missing
provider/model raising validation error, and malformed combined values
(empty model half, empty provider half, missing delimiter). Adds a Robot
integration test for the combined-actor YAML path with provider/model
assertions via show. Restores accidentally deleted TOOL and GRAPH Robot
integration tests. Mirrors provider/model extraction in
step_run_actor_update for assertion step compatibility.
ISSUES CLOSED: #11189
The Behevare-parallel runner in this project does not support inline
parameter substitution for '{param}' or '<param>' markers within
Scenario Outline Examples. All 4 original Scenario Outlines were failing
because parameters were not being substituted, causing UndefinedStep and
ValueError failures.
Fix: Convert all Scenario Outline scenarios to regular Scenarios with
explicit literal step definitions. Each unique Gherkin line gets its own
@Given/@When/@Then step definition matching the exact string.
Also fix pre-existing bugs identified in PR review #8719:
- Fix undefined step by quoting {{seq}} in feature (line 46)
- Fix ctx.decision_result → ctx.validation_result context variable (line 184)
- Fix ctx.struct_result → ctx.validation_result context variable (line 210)
- Replace # type: ignore[arg-type] with proper Callable[[Any], dict] type
- Add missing literal step definitions for structured_output tests
ISSUES CLOSED: #11161
Replace exact character matching with structural component checking
for output validation. Implements three validators covering plan tree
output, decision CLI dicts, and structured session snapshots.
- validate_plan_tree: validates node dicts for required keys (decision_id, type, sequence, question, children), ULID format, correct types, and sibling ordering
- validate_decision_dict: validates decision CLI output against Decision.as_cli_dict() schema with field presence, type, ULID, confidence range [0..1], bool fields
- validate_structured_output: validates StructuredOutput envelope for command, session_id (ULID), status membership, exit_code, elements integrity
- validate_structured_component_output: unified dispatcher by target_type
BDD tests in features/structural_validation.feature.
ISSUES CLOSED: #11147
Chose Option B (fallback): remove orphaned BDD scenarios for two retired agents.
Commit 3c8cf601 deleted .opencode/agents/agent-evolution-pool-supervisor.md
and .opencode/agents/implementation-pool-supervisor.md as part of a large-scale
restructuring of agent definition files. However, their corresponding Behave feature
files and step definitions were not cleaned up, causing nox -s unit_tests to fail
with AssertionError on master.
Removed files:
- features/agent_evolution_pool_supervisor_metadata.feature (7 scenarios testing
agent-evolution-pool-supervisor.md existence and content)
- features/steps/agent_evolution_pool_supervisor_metadata_steps.py (382 lines of
step definitions for the above feature)
- features/pr_compliance_pool_supervisor.feature (10 scenarios testing
implementation-pool-supervisor.md existence and content)
- features/steps/pr_compliance_pool_supervisor_steps.py (219 lines of step
definitions for the above feature)
Both agent definition files were permanently retired by the restructuring commit
and are not expected to return. Keeping orphaned tests that assert on deleted files
is not valid; removal is the correct resolution per the project's handling of
obsolete tests policy.
Quality gates verified:
- nox -s unit_tests: 694 features passed, 0 failed (was failing on master)
- nox -s lint: all checks passed
- nox -s typecheck: 0 errors, 3 warnings (pre-existing import warnings)
- nox -s coverage_report: 96.5% (meets 96.5% threshold)
- nox -s integration_tests: 1998 tests passed, 0 failed
ISSUES CLOSED: #11208
Squash of two overlapping fixes that both addressed ASV benchmark-regression
CI failures due to missing S3 baseline data:> 1. Make the job skip when no S3 baseline exists (e0239ef)
> 2. Add baseline check logic with .benchmark-baseline file (2d628bd)> Combined approach detects S3 availability, creates baseline marker file,
and gracefully skips regression when no baselines are available rather than
failing. Also updates ASV config and noxfile to support this resilience
ISSUES CLOSED: #10378
The exc_info=True parameter was accidentally removed from the
event_handler_failed logging block during a previous formatting cleanup.
This restores it to include full exception tracebacks in warning logs,
which is required by existing integration tests.
Refs: #10378
Add _closed flag and close() method to ReactiveEventBus to complete the
RxPY Subject, preventing subscriber resource leaks. close() is idempotent
and raises RuntimeError when called on a closed bus.
Also adds:
- emit() guard against post-close calls (RuntimeError)
- __enter__/__exit__ context manager protocol for automatic cleanup
- BDD scenarios in event_bus.feature and TDD tests in
tdd_reactive_event_bus_close.feature
ISSUES CLOSED: #10378
Implements concrete StdioMCPTransport class that:
- Spawns MCP server as subprocess and communicates via JSON-RPC 2.0 over stdio
- Performs MCP handshake (initialize + notifications/initialized)
- Supports tools/list and tools/call methods
- Uses RLock for thread-safe concurrent access
- Auto-selected when transport='stdio' in MCPServerConfig
Adds BDD tests for stdio transport covering:
- Connection lifecycle and error handling
- Tool discovery and invocation
- MCPToolAdapter integration
ISSUES CLOSED: #4918
Executes the systematic ACP -> A2A module rename per ADR-047.
- Renamed all legacy ACP references to A2A throughout cleveragents.a2a module
- Standardized all 22 exported symbols to A2A naming conventions
- Added BDD test suite validating export completeness, zero ACP remnants, and documentation accuracy
Closes#10583
Parent Epic: #8569 (A2A Standard Adoption)