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" @tdd_issue @tdd_issue_11254 Scenario: Parse actor name with namespace/name format When I parse actor name "local/my-strategist" Then the parsed provider should be "openai" And the parsed model should be "local/my-strategist" @tdd_issue @tdd_issue_11254 Scenario: Parse actor name with org namespace/name format When I parse actor name "cleverthis/my-executor" Then the parsed provider should be "openai" And the parsed model should be "cleverthis/my-executor" @tdd_issue @tdd_issue_11254 Scenario: Parse actor name with unknown-provider-like segment When I parse actor name "unknown/gpt-4" Then the parsed provider should be "openai" And the parsed model should be "unknown/gpt-4" # --------------------------------------------------------------- # 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: / 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