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
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
227 lines
10 KiB
Gherkin
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
|