|
1 | 1 | # Enforce dry-run before live execution |
2 | 2 | # |
3 | | -# If the workflow declaration requires dry_run_first, |
4 | | -# verify that the plan includes a dry-run step before |
5 | | -# any mutating tool call. |
| 3 | +# If the workflow declaration requires dry_run_first, verify that each |
| 4 | +# mutating tool invocation is preceded by a dry-run invocation of the |
| 5 | +# same tool. |
| 6 | +# |
| 7 | +# Tools that do not support dry-run (read-only validators, notification |
| 8 | +# tools, record-keeping tools) are exempt. This list mirrors the |
| 9 | +# `safety.supports_dry_run == false` flag in the corresponding tool |
| 10 | +# contracts under schemas/. |
| 11 | +# |
| 12 | +# Production adaptation: in a real deployment, this list should be |
| 13 | +# generated from tool contract metadata rather than hardcoded. |
6 | 14 |
|
7 | 15 | package itsm.guardrails.enforce_dry_run |
8 | 16 |
|
9 | 17 | import rego.v1 |
10 | 18 |
|
| 19 | +# Tools exempt from dry-run enforcement (supports_dry_run: false in contracts) |
| 20 | +_tools_without_dry_run := { |
| 21 | + "validate_service_health", |
| 22 | + "notify_stakeholders", |
| 23 | + "open_change_record", |
| 24 | + "restart_service_rollback", |
| 25 | +} |
| 26 | + |
11 | 27 | violation contains msg if { |
12 | 28 | input.workflow.guardrails.must_use_dry_run_first == true |
13 | 29 | some i, tool in input.plan.tool_calls |
14 | 30 | tool.dry_run != true |
| 31 | + not tool.tool_name in _tools_without_dry_run |
15 | 32 | not _preceded_by_dry_run(input.plan.tool_calls, i, tool.tool_name) |
16 | 33 | msg := sprintf("tool '%s' at step %d has no preceding dry-run invocation", [tool.tool_name, i]) |
17 | 34 | } |
|
0 commit comments