Skip to content
Back to policies

§ DOCUMENTATION

Policy Chaining

Order policies with explicit prerequisites. Skip expensive checks when a cheaper upstream policy already decided. Escalate only when the first line of defence triggered.

§ 01

Why chain policies

Every enforcement request runs every applicable policy. Chaining lets you express dependencies without changing that default:

  • Run an expensive validator only if a cheap upstream check passed.
  • Escalate to human approval only if a PII or injection policy already triggered.
  • Guarantee ordering — e.g. always run your sanitiser before your classifier.
§ 02

Fields

FieldTypeMeaning
prerequisitesUUID[]Other policy IDs that must be evaluated first. Max 20. Default [].
evaluationOrderint 0–100000Stable tiebreaker when prerequisites do not fully order policies. Default 100.
chainTriggerenumHow the chain decides whether to evaluate this policy. See the triggers table below.
§ 03

Triggers

TriggerEvaluate this policy when…
all_passedEvery prerequisite returned no violation. Default — use when the chain is an expensive refinement of cheap upstream checks.
any_violatedAt least one prerequisite violated. Use for escalation — e.g. create an approval request only if something already triggered.
alwaysPrerequisites only influence ordering. Use when you need deterministic sequencing but the chain should always run.
§ 04

Example chain

# Policy A — cheap, fast, always runs{ "name": "pii-guard", "policyType": "pii_access", ... } # Policy B — only runs if A passed{  "name": "finance-cost-cap",  "policyType": "expression",  "ruleDefinition": { "expression": "cost > 1.0" },  "prerequisites":   ["<policy-A-id>"],  "evaluationOrder": 200,  "chainTrigger":    "all_passed"} # Policy C — escalates if either A or B violated{  "name": "security-review",  "policyType": "custom_validator",  "ruleDefinition": { "validatorId": "..." },  "prerequisites":   ["<policy-A-id>", "<policy-B-id>"],  "evaluationOrder": 300,  "chainTrigger":    "any_violated"}
§ 05

Cycle detection

Cycles are rejected at write time, inside the same transaction as the insert or update. A DFS walks every prerequisite chain before COMMIT. If a cycle is detected the request fails with HTTP 400 and a breadcrumb of the offending path:

HTTP/1.1 400 Bad RequestContent-Type: application/json {  "error": {    "code": "VALIDATION_ERROR",    "message": "Policy prerequisites would introduce a cycle",    "fields": { "prerequisites": "cycle: A → B → C → A" }  }}

Because cycles never reach the database, runtime evaluation never has to worry about them. The only failure mode at enforcement time is a missing prerequisite — e.g. the upstream policy was deleted or disabled. In that case the dependent policy is recorded as skipped (fail-closed for the chain, not for the request) and an audit event is emitted.

§ 06

Observability

  • policy.chain.skipped — emitted when a trigger condition is not met and the policy is skipped.
  • policy.chain.cycle_detected — emitted when a cycle attempt is rejected at write time.
  • Trace decisions include a skippedByChain breakdown so chains are reconstructible after the fact.
§ 07

Inspecting a chain from the CLI

Use execlave policies chain to see the full evaluation order, or execlave policies chain --id <policy-id> to list a specific policy's prerequisites and its downstream dependents. Missing prerequisites are flagged in red so drift is caught before it shows up as a silent skip at runtime.

$ execlave policies chain# ORDER  NAME               TYPE            TRIGGER       PREREQS# 10     pii-guard          pii_access      all_passed    —# 200    finance-cost-cap   expression      all_passed    1# 300    security-review    custom_validator any_violated 2