Files
cleveragents-core/features/llm_actors_coverage.feature
T
aditya f0442e835d
CI / typecheck (pull_request) Successful in 50s
CI / lint (pull_request) Successful in 3m31s
CI / security (pull_request) Successful in 4m14s
CI / build (pull_request) Successful in 26s
CI / quality (pull_request) Successful in 3m45s
CI / helm (pull_request) Successful in 40s
CI / integration_tests (pull_request) Successful in 3m57s
CI / unit_tests (pull_request) Successful in 4m24s
CI / docker (pull_request) Successful in 1m29s
CI / coverage (pull_request) Successful in 10m54s
CI / benchmark-publish (pull_request) Has been skipped
CI / e2e_tests (pull_request) Successful in 15m35s
CI / status-check (pull_request) Successful in 1s
CI / benchmark-regression (pull_request) Successful in 1h0m6s
feat(acms): plan execution leverages ACMS context for LLM calls
Integrate ACMS execute-phase context assembly into LLMExecuteActor and inject assembled context into execute prompts with resilient fallback when assembly fails or returns empty output.

Wire the plan CLI executor to use an ACMS-backed execute context assembler, add Behave coverage for context injection/fallback/empty context, and extend Robot M5 verification helpers to assert execute-phase ACMS context usage.

ISSUES CLOSED: #850
2026-03-30 09:21:51 +00:00

227 lines
10 KiB
Gherkin

Feature: LLM Actors Coverage
Exercises previously uncovered code paths in the llm_actors module,
including _parse_actor_name, LLMStrategizeActor, and LLMExecuteActor.
# ---------------------------------------------------------------
# _parse_actor_name helper
# ---------------------------------------------------------------
Scenario: Parse actor name with provider/model format
When I parse actor name "openai/gpt-4"
Then the parsed provider should be "openai"
And the parsed model should be "gpt-4"
Scenario: Parse actor name with only model (no slash)
When I parse actor name "gpt-4"
Then the parsed provider should be "openai"
And the parsed model should be "gpt-4"
Scenario: Parse actor name with empty string defaults to openai/gpt-4
When I parse actor name ""
Then the parsed provider should be "openai"
And the parsed model should be "gpt-4"
Scenario: Parse actor name with anthropic provider
When I parse actor name "anthropic/claude-3"
Then the parsed provider should be "anthropic"
And the parsed model should be "claude-3"
# ---------------------------------------------------------------
# LLMStrategizeActor.__init__ validation
# ---------------------------------------------------------------
Scenario: LLMStrategizeActor rejects None provider_registry
When I create an LLMStrategizeActor with None provider_registry
Then lacov a ValidationError should be raised with message "provider_registry must not be None"
Scenario: LLMStrategizeActor rejects None lifecycle_service
When I create an LLMStrategizeActor with None lifecycle_service
Then lacov a ValidationError should be raised with message "lifecycle_service must not be None"
Scenario: LLMStrategizeActor initializes with valid dependencies
Given a mock provider registry for strategize
And a mock lifecycle service for strategize
When I create an LLMStrategizeActor with valid dependencies
Then the LLMStrategizeActor should be created successfully
# ---------------------------------------------------------------
# LLMStrategizeActor.execute
# ---------------------------------------------------------------
Scenario: LLMStrategizeActor rejects empty plan_id
Given a valid LLMStrategizeActor
When I call strategize execute with empty plan_id
Then lacov a ValidationError should be raised with message "plan_id must not be empty"
Scenario: LLMStrategizeActor executes with stream callback
Given a valid LLMStrategizeActor
And the LLM returns a numbered step list for strategize
When I call strategize execute with plan_id "PLAN123" and a stream callback
Then the strategize result should contain decisions
And the stream callback should have received "strategize_started"
And the stream callback should have received "strategize_decisions"
And the stream callback should have received "strategize_complete"
Scenario: LLMStrategizeActor executes without stream callback
Given a valid LLMStrategizeActor
And the LLM returns a numbered step list for strategize
When I call strategize execute with plan_id "PLAN456" and no stream callback
Then the strategize result should contain decisions
Scenario: LLMStrategizeActor executes with invariants
Given a valid LLMStrategizeActor
And the LLM returns a numbered step list for strategize
When I call strategize execute with plan_id "PLAN789" and invariants
Then the strategize result should contain invariant records
Scenario: LLMStrategizeActor executes with None definition_of_done
Given a valid LLMStrategizeActor
And the LLM returns a numbered step list for strategize
When I call strategize execute with plan_id "PLANX" and None definition_of_done
Then the strategize result should contain decisions
# ---------------------------------------------------------------
# LLMStrategizeActor._parse_decisions
# ---------------------------------------------------------------
Scenario: Parse decisions from numbered list
When I parse LLM decisions from numbered list "1. Create file\n2. Add tests\n3. Run CI"
Then I should get 3 parsed decisions
And parsed decision 0 should be "Create file"
Scenario: Parse decisions from bullet list
When I parse LLM decisions from bullet list "- Create file\n* Add tests\n Run CI"
Then I should get 3 parsed decisions
Scenario: Parse decisions from empty string
When I parse LLM decisions from empty string
Then I should get the default decision "Complete the plan objectives"
Scenario: Parse decisions strips blank lines
When I parse LLM decisions from text with blank lines
Then blank lines should be skipped in the result
Scenario: Parse decisions with numbered colon prefix
When I parse LLM decisions from "1: Create file\n2: Add tests"
Then I should get 2 parsed decisions
And parsed decision 0 should be "Create file"
# ---------------------------------------------------------------
# LLMExecuteActor.__init__ validation
# ---------------------------------------------------------------
Scenario: LLMExecuteActor rejects None provider_registry
When I create an LLMExecuteActor with None provider_registry
Then lacov a ValidationError should be raised with message "provider_registry must not be None"
Scenario: LLMExecuteActor rejects None lifecycle_service
When I create an LLMExecuteActor with None lifecycle_service
Then lacov a ValidationError should be raised with message "lifecycle_service must not be None"
Scenario: LLMExecuteActor initializes with valid dependencies
Given a mock provider registry for execute
And a mock lifecycle service for execute
When I create an LLMExecuteActor with valid dependencies
Then the LLMExecuteActor should be created successfully
# ---------------------------------------------------------------
# LLMExecuteActor.execute
# ---------------------------------------------------------------
Scenario: LLMExecuteActor rejects empty plan_id
Given a valid LLMExecuteActor
When I call execute actor with empty plan_id
Then lacov a ValidationError should be raised with message "plan_id must not be empty"
Scenario: LLMExecuteActor executes with stream callback
Given a valid LLMExecuteActor
And the LLM returns file blocks for execute
When I call execute actor with plan_id "EXEC01" and a stream callback
Then the execute result should contain a changeset
And the execute stream callback should have received "execute_started"
And the execute stream callback should have received "execute_step"
And the execute stream callback should have received "execute_complete"
Scenario: LLMExecuteActor executes without stream callback
Given a valid LLMExecuteActor
And the LLM returns file blocks for execute
When I call execute actor with plan_id "EXEC02" and no stream callback
Then the execute result should contain a changeset
Scenario: LLMExecuteActor executes with sandbox_root
Given a valid LLMExecuteActor
And the LLM returns file blocks for execute
When I call execute actor with plan_id "EXEC03" and a sandbox root
Then the execute result should have sandbox refs
Scenario: LLMExecuteActor executes with sandbox_root in read_only mode
Given a valid LLMExecuteActor
And the LLM returns file blocks for execute
When I call execute actor with plan_id "EXEC04" sandbox root and read_only
Then the execute result should have sandbox refs
And no files should be written to sandbox
Scenario: LLMExecuteActor executes with no file blocks from LLM
Given a valid LLMExecuteActor
And the LLM returns empty response for execute
When I call execute actor with plan_id "EXEC05" and no stream callback
Then the execute result should have zero entries
Scenario: LLMExecuteActor injects assembled execute-phase context into prompt
Given a valid LLMExecuteActor with assembled execute-phase context
And the LLM returns file blocks for execute
When I call execute actor with plan_id "EXEC06" and no stream callback
Then the execute prompt should contain "ACMS Execute-Phase Context"
And the execute prompt should contain "src/main.py"
Scenario: LLMExecuteActor falls back when context assembly fails
Given a valid LLMExecuteActor with failing context assembly
And the LLM returns file blocks for execute
When I call execute actor with plan_id "EXEC07" and no stream callback
Then the execute result should contain a changeset
And the execute prompt should not contain "ACMS Execute-Phase Context"
Scenario: LLMExecuteActor handles empty assembled context
Given a valid LLMExecuteActor with empty assembled context
And the LLM returns file blocks for execute
When I call execute actor with plan_id "EXEC08" and no stream callback
Then the execute result should contain a changeset
And the execute prompt should not contain "ACMS Execute-Phase Context"
# ---------------------------------------------------------------
# LLMExecuteActor._parse_file_blocks
# ---------------------------------------------------------------
Scenario: Parse file blocks from LLM output with FILE markers
When I parse file blocks from LLM output with two files
Then I should get 2 changeset entries
And changeset entry 0 path should be "src/main.py"
Scenario: Parse file blocks from LLM output with no files
When I parse file blocks from empty LLM output
Then I should get 0 changeset entries
# ---------------------------------------------------------------
# LLMExecuteActor._write_to_sandbox
# ---------------------------------------------------------------
Scenario: Write to sandbox creates files in the directory
Given lacov a temporary sandbox directory
When I write generated files to the sandbox
Then the sandbox should contain the generated files
Scenario: Write to sandbox handles OSError gracefully
Given lacov a temporary sandbox directory that is read-only
When I write generated files to the read-only sandbox
Then the write should not raise an exception
# ---------------------------------------------------------------
# LLM response without .content attribute
# ---------------------------------------------------------------
Scenario: Strategize actor handles LLM response without content attribute
Given a valid LLMStrategizeActor
And the LLM returns a response without content attribute
When I call strategize execute with plan_id "NOCONTENT" and no stream callback
Then the strategize result should contain decisions