53455275ba
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 18s
CI / lint (pull_request) Successful in 23s
CI / security (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 35s
CI / integration_tests (pull_request) Successful in 3m46s
CI / unit_tests (pull_request) Successful in 11m44s
CI / docker (pull_request) Successful in 38s
CI / benchmark-regression (pull_request) Successful in 27m39s
CI / coverage (pull_request) Successful in 55m41s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 16s
CI / lint (push) Successful in 19s
CI / typecheck (push) Successful in 32s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 41s
CI / integration_tests (push) Successful in 2m55s
CI / unit_tests (push) Successful in 11m43s
CI / docker (push) Successful in 40s
CI / benchmark-publish (push) Successful in 16m11s
CI / coverage (push) Successful in 44m49s
Add runtime autonomy constraints (max steps, tool budget, required confirmations) and a structured audit trail for plan execution. New domain models: - AutonomyGuardrails: enforces step limits, tool budgets, and confirmation gates with validators and check methods - GuardrailAuditEntry: records each enforcement event with timestamp, event type, guard name, result, reason, and context - GuardrailAuditTrail: ordered collection of audit entries persisted to plan metadata New service: - AutonomyGuardrailService: high-level service for configuring guardrails per plan, checking constraints, recording audit entries, and serializing/restoring state via plan metadata Tests: - Behave: 69 scenarios covering model validation, step/budget/ confirmation checks, audit trail recording, and service operations - Robot: 8 test cases for autonomy guardrail CLI flag smoke testing - ASV: 6 benchmark suites measuring enforcement overhead Documentation: - Updated docs/reference/automation_profiles.md with guardrail fields, enforcement behavior, audit trail schema, and event type reference ISSUES CLOSED: #204
402 lines
17 KiB
Gherkin
402 lines
17 KiB
Gherkin
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
|