Recipe: First approval flow end-to-end
escalate_on_deny does not raise an approval requestThe deny fires. A rule tagged escalate_on_deny: true denies exactly as
written -- the matched rule's own policy_id, effect: "deny",
reason_code: "RULE_MATCH". You are protected.
The escalation does not. The tag is accepted by the policy schema and
carried into the policy bundle, and no enforcer acts on it: no approval request
is raised, no approver is notified, and decision.requires_approval stays
false. Code that branches on decision.requires_approval in order to act on
this tag therefore never runs. (A different mechanism, LLM function policies'
require_approval, does set that field -- escalate_on_deny is not wired to
it.) Tracking: #2391.
To request approval today, call it explicitly.
client.request_approval(decision) posts a real approval request. Nothing
about the tag calls it for you. See Approval callback.
Per #2363 the
approver-facing /approvals pages are gated in production, so requests are
resolved through the API.
This applies to the published Python SDK (controlzero 1.13.14) and the
published Node SDK (@controlzero/sdk 1.13.6) alike.
Time: ~10 minutes Prereqs: Teams tier; one teammate with approver permissions; Python SDK 1.6.0+ Status: BETA
The approval request path works on every deployment, including the hosted (SaaS) plan. The feature is in BETA and is off by default -- this walkthrough turns the per-scope toggle on in Step 2.
Read this before you start: the approver-facing pages are not reachable yet. The approvals inbox and request detail routes redirect to the dashboard in every shipped deployment, and the notification deep link points at that same path, so the approve-from-the-dashboard step below cannot be completed today. The request runs to its deadline and the SDK raises HITLTimeoutError with a synthesized deny, so the original deny stands. Follow the walkthrough to wire and verify the request path; the resolve step lands with the inbox. See Set up approvals for the full toggle and cascade reference.
This walkthrough sets up a deny on Bash:sudo *, triggers it from an agent, requests approval from your code, approves it from the dashboard, and verifies the audit lineage.
Step 1. Mark the rule you intend to review
Edit your project policy in the dashboard or as YAML:
version: '1'
rules:
- id: require-sudo-approval
deny: 'Bash:sudo *'
escalate_on_deny: true
reason: 'sudo requires admin approval'
- allow: 'Bash:*'
Save the policy.