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