Feature: Safety Profile Domain Model
  As a developer
  I want safety profile domain models with constraint validation
  So that plan execution safety can be configured and enforced

  # ---- Default construction ----

  Scenario: Default safety profile has correct defaults
    When I create a default safety profile
    Then the safety profile should be created
    And the safety require_sandbox should be true
    And the safety require_checkpoints should be true
    And the safety require_human_approval should be false
    And the safety allow_unsafe_tools should be false
    And the safety max_retries_per_step should be 3
    And the safety max_cost_per_plan should be none
    And the safety max_total_cost should be none
    And the safety allowed_skill_categories should be empty

  # ---- Field parsing ----

  Scenario: Safety profile accepts allowed_skill_categories
    When I create a safety profile with categories "code,test,deploy"
    Then the safety profile should be created
    And the safety allowed_skill_categories count should be 3

  Scenario: Safety profile deduplicates categories
    When I create a safety profile with categories "code,test,code"
    Then the safety profile should be created
    And the safety allowed_skill_categories count should be 2

  Scenario: Safety profile strips blank categories
    When I create a safety profile with categories "code, ,test, "
    Then the safety profile should be created
    And the safety allowed_skill_categories count should be 2

  # ---- Constraint validation ----

  Scenario: Safety profile rejects negative max_cost_per_plan
    When I try to create a safety profile with max_cost_per_plan -1.0
    Then a safety validation error should be raised

  Scenario: Safety profile rejects negative max_total_cost
    When I try to create a safety profile with max_total_cost -5.0
    Then a safety validation error should be raised

  Scenario: Safety profile rejects negative max_retries_per_step
    When I try to create a safety profile with max_retries_per_step -1
    Then a safety validation error should be raised

  Scenario: Safety profile rejects max_retries_per_step above 100
    When I try to create a safety profile with max_retries_per_step 101
    Then a safety validation error should be raised

  Scenario: Safety profile accepts zero max_cost_per_plan
    When I create a safety profile with max_cost_per_plan 0.0
    Then the safety profile should be created
    And the safety max_cost_per_plan should be 0.0

  Scenario: Safety profile accepts zero max_retries_per_step
    When I create a safety profile with max_retries_per_step 0
    Then the safety profile should be created
    And the safety max_retries_per_step should be 0

  # ---- Cross-field validation ----

  Scenario: Safety profile rejects max_cost_per_plan exceeding max_total_cost
    When I try to create a safety profile with cost 1000.0 and total 10.0
    Then a safety validation error should be raised

  Scenario: Safety profile accepts max_cost_per_plan equal to max_total_cost
    When I create a safety profile with cost 100.0 and total 100.0
    Then the safety profile should be created
    And the safety max_cost_per_plan should be 100.0
    And the safety max_total_cost should be 100.0

  # ---- Category type guard ----

  Scenario: Safety profile rejects non-string categories
    When I try to create a safety profile with non-string categories
    Then a safety validation error should be raised

  Scenario: Safety profile rejects non-list categories
    When I try to create a safety profile with non-list categories
    Then a safety validation error should be raised

  # ---- Frozen immutability ----

  Scenario: Safety profile is immutable (frozen)
    When I create a default safety profile
    And I try to assign max_retries_per_step -1 on the safety profile
    Then a safety validation error should be raised

  # ---- from_config factory ----

  Scenario: Safety profile from config loads correctly
    When I load a safety profile from config with require_sandbox false
    Then the safety profile should be created
    And the safety require_sandbox should be false

  Scenario: Safety profile from config with all fields
    When I load a safety profile from full config
    Then the safety profile should be created
    And the safety require_sandbox should be false
    And the safety require_checkpoints should be false
    And the safety require_human_approval should be true
    And the safety allow_unsafe_tools should be true
    And the safety max_cost_per_plan should be 50.0
    And the safety max_retries_per_step should be 5
    And the safety max_total_cost should be 200.0

  # ---- DEFAULT_SAFETY_PROFILE constant ----

  Scenario: DEFAULT_SAFETY_PROFILE has sensible defaults
    When I load the default safety profile constant
    Then the safety profile should be created
    And the safety require_sandbox should be true
    And the safety require_checkpoints should be true
    And the safety require_human_approval should be false
    And the safety allow_unsafe_tools should be false
    And the safety max_retries_per_step should be 3

  # ---- SafetyProfileRef ----

  Scenario: SafetyProfileRef stores name and provenance
    When I create a safety profile ref with name "strict" and provenance "plan"
    Then the safety profile ref should have name "strict"
    And the safety profile ref should have provenance "plan"

  Scenario: SafetyProfileRef rejects empty name
    When I try to create a safety profile ref with empty name
    Then a safety validation error should be raised

  # ---- resolve_safety_profile precedence ----

  Scenario: resolve_safety_profile returns plan-level when all levels set
    Given a plan safety profile with allow_unsafe_tools true
    And an action safety profile with allow_unsafe_tools false
    And a project safety profile with allow_unsafe_tools false
    And a global safety profile with allow_unsafe_tools false
    When I resolve the safety profile
    Then the resolved provenance should be "plan"
    And the resolved profile allow_unsafe_tools should be true

  Scenario: resolve_safety_profile returns action-level when no plan-level
    Given an action safety profile with allow_unsafe_tools true
    And a project safety profile with allow_unsafe_tools false
    And a global safety profile with allow_unsafe_tools false
    When I resolve the safety profile
    Then the resolved provenance should be "action"
    And the resolved profile allow_unsafe_tools should be true

  Scenario: resolve_safety_profile returns project-level when no plan or action
    Given a project safety profile with allow_unsafe_tools true
    And a global safety profile with allow_unsafe_tools false
    When I resolve the safety profile
    Then the resolved provenance should be "project"
    And the resolved profile allow_unsafe_tools should be true

  Scenario: resolve_safety_profile returns global-level when only global set
    Given a global safety profile with allow_unsafe_tools true
    When I resolve the safety profile
    Then the resolved provenance should be "global"
    And the resolved profile allow_unsafe_tools should be true

  Scenario: resolve_safety_profile returns default when all levels are None
    When I resolve the safety profile with no levels
    Then the resolved provenance should be "global"
    And the resolved profile allow_unsafe_tools should be false
    And the resolved profile require_sandbox should be true

  Scenario: resolve_safety_profile preserves all 8 fields from plan-level
    Given a full plan safety profile with all fields customized
    And a full action safety profile with defaults
    When I resolve the safety profile
    Then the resolved provenance should be "plan"
    And the resolved profile require_sandbox should be false
    And the resolved profile require_checkpoints should be false
    And the resolved profile allow_unsafe_tools should be true
    And the resolved profile require_human_approval should be true
    And the resolved profile max_cost_per_plan should be 50.0
    And the resolved profile max_retries_per_step should be 7
    And the resolved profile max_total_cost should be 200.0
    And the resolved profile allowed_skill_categories should be "code,test"

  Scenario: resolve_safety_profile preserves all 8 fields from action-level
    Given a full action safety profile with all fields customized
    When I resolve the safety profile
    Then the resolved provenance should be "action"
    And the resolved profile require_sandbox should be false
    And the resolved profile require_checkpoints should be false
    And the resolved profile allow_unsafe_tools should be true
    And the resolved profile require_human_approval should be true
    And the resolved profile max_cost_per_plan should be 50.0
    And the resolved profile max_retries_per_step should be 7
    And the resolved profile max_total_cost should be 200.0
    And the resolved profile allowed_skill_categories should be "code,test"

  # ---- model_dump round-trip ----

  Scenario: Safety profile model_dump produces valid dict
    When I create a safety profile and dump it
    Then the safety dump should have key "require_sandbox"
    Then the safety dump should have key "require_checkpoints"
    And the safety dump should have key "max_retries_per_step"
    And the safety dump should have key "allow_unsafe_tools"
    And the safety dump require_sandbox should be true

  # ---- Action with safety profile ----

  Scenario: Action accepts safety profile in from_config
    When I create an action with safety profile from config
    Then the action safety profile should not be none
    And the action safety profile require_sandbox should be false

  Scenario: Action as_cli_dict includes safety profile
    When I create an action with safety profile from config
    Then the action cli dict should contain safety_profile key

  # ---- from_yaml edge cases ----

  Scenario: Safety profile from_yaml rejects non-existent file
    When I try to load a safety profile from a non-existent yaml file
    Then a safety FileNotFoundError should be raised

  Scenario: Safety profile from_yaml rejects non-dict content
    When I try to load a safety profile from yaml with non-dict content
    Then a safety ValueError should be raised
