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: <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
