forked from cleveragents/cleveragents-core
eb46f0ff54
## 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
238 lines
11 KiB
Gherkin
238 lines
11 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
|
|
|
|
# M7 fix: <CAFS>/<CAFE> short-form and >>>>>>>> non-conflicting formats
|
|
# had zero test coverage. These scenarios exercise both new patterns.
|
|
|
|
Scenario: Parse file blocks using CAFS short delimiter format
|
|
When I parse file blocks using the CAFS short delimiter format
|
|
Then I should get 1 changeset entry with path "src/cafs_example.py"
|
|
|
|
Scenario: Parse file blocks using the non-conflicting arrow delimiter format
|
|
When I parse file blocks using the new arrow delimiter format
|
|
Then I should get 1 changeset entry with path "src/arrow_example.py"
|
|
|
|
# ---------------------------------------------------------------
|
|
# 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
|