Feature: Autonomy Guardrails and Audit Trail
  As a plan executor
  I want autonomy guardrails to enforce step limits, tool budgets, and confirmations
  So that plan execution stays within defined safety boundaries with a full audit trail

  # ---- AutonomyGuardrails model validation ----

  Scenario: Create guardrails with all fields
    When I create autonomy guardrails with max_steps 10 and tool_budget 50.0
    Then the guardrails should have max_steps 10
    And the guardrails should have tool_budget 50.0
    And the guardrails step_count should be 0
    And the guardrails budget_spent should be 0.0

  Scenario: Create guardrails with defaults
    When I create autonomy guardrails with defaults
    Then the guardrails should have max_steps None
    And the guardrails should have tool_budget None
    And the guardrails should have empty required_confirmations

  Scenario: Guardrails reject negative max_steps
    When I try to create guardrails with max_steps 0
    Then a guardrails validation error should be raised
    And the guardrails error should mention "max_steps"

  Scenario: Guardrails reject negative tool_budget
    When I try to create guardrails with tool_budget -1.0
    Then a guardrails validation error should be raised
    And the guardrails error should mention "tool_budget"

  # ---- Step limit checks ----

  Scenario: Step limit allows within bounds
    Given guardrails with max_steps 5
    When I check step limit at step 3
    Then the step check should be allowed

  Scenario: Step limit blocks at max
    Given guardrails with max_steps 5
    When I check step limit at step 5
    Then the step check should be denied

  Scenario: Step limit blocks above max
    Given guardrails with max_steps 3
    When I check step limit at step 10
    Then the step check should be denied

  Scenario: Unlimited steps always allowed
    Given guardrails with no step limit
    When I check step limit at step 1000
    Then the step check should be allowed

  # ---- Tool budget checks ----

  Scenario: Budget allows within limit
    Given guardrails with tool_budget 100.0
    When I check tool budget for cost 50.0
    Then the budget check should be allowed

  Scenario: Budget blocks when exceeded
    Given guardrails with tool_budget 10.0 and budget_spent 8.0
    When I check tool budget for cost 5.0
    Then the budget check should be denied

  Scenario: Budget allows exact remaining
    Given guardrails with tool_budget 10.0 and budget_spent 5.0
    When I check tool budget for cost 5.0
    Then the budget check should be allowed

  Scenario: Unlimited budget always allowed
    Given guardrails with no budget limit
    When I check tool budget for cost 999.0
    Then the budget check should be allowed

  Scenario: Budget rejects negative tool cost
    Given guardrails with tool_budget 100.0
    When I try to check tool budget for cost -1.0
    Then a guardrails validation error should be raised

  # ---- Confirmation checks ----

  Scenario: Confirmation required for listed operation
    Given guardrails with required confirmations "deploy,delete"
    When I check confirmation for operation "deploy"
    Then confirmation should be required

  Scenario: Confirmation not required for unlisted operation
    Given guardrails with required confirmations "deploy,delete"
    When I check confirmation for operation "read"
    Then confirmation should not be required

  Scenario: No confirmations required when list empty
    Given guardrails with no required confirmations
    When I check confirmation for operation "deploy"
    Then confirmation should not be required

  # ---- Step counter and budget tracking ----

  Scenario: Increment step counter
    Given guardrails with max_steps 10
    When I increment the step counter 3 times
    Then the step_count should be 3

  Scenario: Record tool cost
    Given guardrails with tool_budget 100.0
    When I record a tool cost of 25.0
    And I record a tool cost of 15.0
    Then the budget_spent should be 40.0

  Scenario: Record cost rejects negative cost
    Given guardrails with tool_budget 100.0
    When I try to record a negative cost
    Then a guardrails validation error should be raised

  # ---- Audit trail ----

  Scenario: Audit trail records entries
    Given an empty audit trail
    When I add a step_allowed entry to the audit trail
    And I add a budget_blocked entry to the audit trail
    Then the audit trail should have 2 entries
    And the audit trail allowed_count should be 1
    And the audit trail denied_count should be 1

  Scenario: Audit entry has correct fields
    When I create an audit entry with event_type step_blocked and guard_name step_limit
    Then the audit entry event_type should be "step_blocked"
    And the audit entry guard_name should be "step_limit"
    And the audit entry result should be "denied"
    And the audit entry should have a timestamp

  # ---- AutonomyGuardrailService ----

  Scenario: Service configures and retrieves guardrails
    Given an autonomy guardrail service
    When I configure guardrails for plan "plan-001" with max_steps 5
    Then the service should return guardrails for plan "plan-001"
    And the service guardrails should have max_steps 5

  Scenario: Service returns None for unconfigured plan
    Given an autonomy guardrail service
    Then the service should return None for plan "nonexistent"

  Scenario: Service step limit check records audit trail
    Given an autonomy guardrail service with guardrails for plan "plan-002"
    When I check step limit via service for plan "plan-002" at step 3
    Then the audit trail for plan "plan-002" should have 1 entry

  Scenario: Service budget check records cost and audit trail
    Given an autonomy guardrail service with budget guardrails for plan "plan-003"
    When I check tool budget via service for plan "plan-003" with cost 5.0
    Then the audit trail for plan "plan-003" should have 1 entry
    And the service guardrails budget_spent should be 5.0

  Scenario: Service confirmation check records audit trail
    Given an autonomy guardrail service with confirmation guardrails for plan "plan-004"
    When I check confirmation via service for plan "plan-004" operation "deploy"
    Then the audit trail for plan "plan-004" should have 1 entry

  Scenario: Service serializes audit trail to metadata
    Given an autonomy guardrail service with guardrails for plan "plan-005"
    When I check step limit via service for plan "plan-005" at step 1
    Then the metadata for plan "plan-005" should contain the audit trail

  Scenario: Service loads guardrails from metadata
    Given an autonomy guardrail service
    When I load guardrails from metadata for plan "plan-006"
    Then the service should return guardrails for plan "plan-006"

  Scenario: Service removes plan guardrails
    Given an autonomy guardrail service with guardrails for plan "plan-007"
    When I remove plan "plan-007" from the service
    Then the service should return None for plan "plan-007"

  Scenario: Service check_step_limit returns True for unconfigured plan
    Given an autonomy guardrail service
    When I check step limit via service for plan "unknown" at step 100
    Then the step limit check should return True

  Scenario: Service check_tool_budget returns True for unconfigured plan
    Given an autonomy guardrail service
    When I check tool budget via service for plan "unknown" with cost 999.0
    Then the tool budget check should return True

  Scenario: Service check_confirmation returns False for unconfigured plan
    Given an autonomy guardrail service
    When I check confirmation via service for plan "unknown" operation "deploy"
    Then the confirmation check should return False

  Scenario: Service configure_guardrails rejects empty plan_id
    Given an autonomy guardrail service
    When I try to configure guardrails with empty plan_id
    Then a guardrails validation error should be raised

  Scenario: GuardrailEventType enumerates all event types
    Then GuardrailEventType should have at least 10 members

  Scenario: GuardrailResult enumerates allowed and denied
    Then GuardrailResult should have exactly 2 members

  # ---- Wall-clock time checks ----

  Scenario: Wall-clock allows when no limit set
    Given guardrails with no wall-clock limit
    When I check wall-clock time
    Then the wall-clock check should be allowed

  Scenario: Wall-clock allows when within limit
    Given guardrails with wall-clock limit 60.0 seconds started recently
    When I check wall-clock time
    Then the wall-clock check should be allowed

  Scenario: Wall-clock blocks when expired
    Given guardrails with wall-clock limit that has already expired
    When I check wall-clock time
    Then the wall-clock check should be denied

  Scenario: Service wall-clock check records audit trail
    Given an autonomy guardrail service with wall-clock guardrails for plan "plan-wc"
    When I check wall-clock via service for plan "plan-wc"
    Then the audit trail for plan "plan-wc" should have 1 entry

  # ---- Actor tool-call limits ----

  Scenario: Actor tool-call limit allows within bounds
    Given guardrails with actor tool-call limit 5
    When I check actor tool calls with 3 current calls
    Then the actor tool-call check should be allowed

  Scenario: Actor tool-call limit blocks at max
    Given guardrails with actor tool-call limit 5
    When I check actor tool calls with 5 current calls
    Then the actor tool-call check should be denied

  Scenario: Actor tool-call limit unlimited
    Given guardrails with no actor tool-call limit
    When I check actor tool calls with 1000 current calls
    Then the actor tool-call check should be allowed

  Scenario: Service actor tool-call check records audit trail
    Given an autonomy guardrail service with actor-limit guardrails for plan "plan-al"
    When I check actor tool calls via service for plan "plan-al" with 3 calls
    Then the audit trail for plan "plan-al" should have 1 entry

  # ---- Bounded audit trail eviction ----

  Scenario: Audit trail evicts oldest entry when full
    Given an audit trail with max_entries 3
    When I add 4 step_allowed entries to the audit trail
    Then the audit trail should have 3 entries
    And the audit trail allowed_count should be 3

  # ---- Edge cases ----

  Scenario: Budget blocks when tool_budget is zero
    Given guardrails with tool_budget 0.0
    When I check tool budget for cost 0.01
    Then the budget check should be denied

  Scenario: Budget allows zero cost when tool_budget is zero
    Given guardrails with tool_budget 0.0
    When I check tool budget for cost 0
    Then the budget check should be allowed

  Scenario: Case-insensitive confirmation matching
    Given guardrails with required confirmations "Deploy,DELETE"
    When I check confirmation for operation "deploy"
    Then confirmation should be required

  Scenario: Case-insensitive confirmation matching uppercase query
    Given guardrails with required confirmations "deploy"
    When I check confirmation for operation "DEPLOY"
    Then confirmation should be required

  Scenario: Service rejects oversized metadata entries
    Given an autonomy guardrail service
    When I try to load metadata with oversized audit trail for plan "plan-big"
    Then a guardrails validation error should be raised

  Scenario: Service rejects oversized confirmations in metadata
    Given an autonomy guardrail service
    When I try to load metadata with oversized confirmations for plan "plan-big2"
    Then a guardrails validation error should be raised

  Scenario: Metadata round-trip preserves guardrail state
    Given an autonomy guardrail service with guardrails for plan "plan-rt"
    When I check step limit via service for plan "plan-rt" at step 3
    And I serialize and restore metadata for plan "plan-rt"
    Then the restored guardrails for plan "plan-rt" should match the original

  # ---- start_time validation ----

  Scenario: start_time accepts valid ISO-8601 timestamp
    When I create guardrails with a valid start_time
    Then the guardrails start_time should be set

  Scenario: start_time rejects malformed string
    When I try to create guardrails with start_time "not-a-date"
    Then a guardrails validation error should be raised
    And the guardrails error should mention "start_time"

  Scenario: start_time accepts None
    When I create autonomy guardrails with defaults
    Then the guardrails start_time should be None

  Scenario: check_wall_clock handles malformed start_time gracefully
    Given guardrails with a malformed start_time bypassing validation
    When I check wall-clock time
    Then the wall-clock check should be denied

  # ---- Retry-per-failure checks ----

  Scenario: Retry limit allows within bounds
    Given guardrails with retry limit 3
    When I check retries per failure with 2 current retries
    Then the retry check should be allowed

  Scenario: Retry limit blocks at max
    Given guardrails with retry limit 3
    When I check retries per failure with 3 current retries
    Then the retry check should be denied

  Scenario: Retry limit blocks above max
    Given guardrails with retry limit 2
    When I check retries per failure with 5 current retries
    Then the retry check should be denied

  Scenario: Unlimited retries always allowed
    Given guardrails with no retry limit
    When I check retries per failure with 1000 current retries
    Then the retry check should be allowed

  Scenario: Service retry check records audit trail
    Given an autonomy guardrail service with retry-limit guardrails for plan "plan-rl"
    When I check retries via service for plan "plan-rl" with 1 retries
    Then the audit trail for plan "plan-rl" should have 1 entry

  Scenario: Service retry check blocks and records audit trail
    Given an autonomy guardrail service with retry-limit guardrails for plan "plan-rl2"
    When I check retries via service for plan "plan-rl2" with 5 retries
    Then the retry service check should return False
    And the audit trail for plan "plan-rl2" should have 1 entry

  Scenario: Successive retries stop after reaching max_retries_per_failure
    Given a guardrail service with max_retries_per_failure 3 for plan "retry-stop"
    When I check retries for plan "retry-stop" incrementing from 0 to 4
    Then retry checks 0, 1, 2 should be allowed
    And retry checks 3, 4 should be denied
    And the audit trail for plan "retry-stop" should have 5 entry

  # ---- PlanExecutor guardrail integration ----

  Scenario: PlanExecutor enforces step limit during stub execution
    Given a plan executor with guardrail service and step limit 2
    And a plan in execute phase with 3 decisions
    When I run execute on the plan
    Then execution should fail with a guardrail step limit error

  Scenario: PlanExecutor enforces wall-clock limit during execution
    Given a plan executor with guardrail service and expired wall-clock
    And a plan in execute phase with 1 decisions
    When I run execute on the plan
    Then execution should fail with a guardrail wall-clock error

  Scenario: PlanExecutor runs without guardrails when service is None
    Given a plan executor without guardrail service
    And a plan in execute phase with 2 decisions
    When I run execute on the plan
    Then execution should succeed

  Scenario: PlanExecutor marks start_time on first execution
    Given a plan executor with guardrail service and no wall-clock limit
    And a plan in execute phase with 1 decisions
    When I run execute on the plan
    Then the guardrails start_time should have been set

  # ---- Additional validation edge cases for coverage ----

  Scenario: Guardrails reject invalid max_tool_calls_per_invocation
    When I try to create actor limits with max_tool_calls_per_invocation 0
    Then a guardrails validation error should be raised
    And the guardrails error should mention "max_tool_calls_per_invocation"

  Scenario: Guardrails reject negative max_retries_per_failure
    When I try to create actor limits with max_retries_per_failure -1
    Then a guardrails validation error should be raised
    And the guardrails error should mention "max_retries_per_failure"

  Scenario: Guardrails reject non-positive max_wall_clock_seconds
    When I try to create guardrails with max_wall_clock_seconds 0
    Then a guardrails validation error should be raised
    And the guardrails error should mention "max_wall_clock_seconds"

  Scenario: Audit trail evicts denied entry when full
    Given an audit trail with max_entries 2
    When I add a budget_blocked entry to the audit trail
    And I add a step_allowed entry to the audit trail
    And I add a step_allowed entry to the audit trail
    Then the audit trail should have 2 entries
    And the audit trail denied_count should be 0
    And the audit trail allowed_count should be 2
