Skip to main content
Status: Developer preview.
Every gated action in the fabric is decided by one policy engine (internally, evaluateActionPolicy), evaluated before the action is allowed to execute. Given the same inputs, it returns the same decision every time — that decision, not a prompt and not a model’s own judgment, is what gates execution.

What a policy pack is

A policy pack is your tenant’s versioned configuration for how actions get judged: which categories require review, at what monetary thresholds, which tools are off the table entirely, and who’s allowed to approve what. Praxa ships a default pack, so a new tenant is governed from its first call; you layer your own rules on top through your own policy configuration — never by asking the model nicely. Packs are versioned. A change to your rules creates a new version; it never silently rewrites the record of runs that already executed under an older one (more on this under Versioning and receipts, below).

Where it runs

The engine is evaluated at a small, fixed set of chokepoints — right before a tool call is allowed to commit, and right before a durable run’s next step is allowed to commit — instead of each feature deciding for itself. That consolidation is in progress. Eleven separate fail-closed approval domains — purchases, browser actions, connector writes, social posts, credential changes, and more — were each built at a different time for a different feature inside Praxa’s own product, and each today enforces its own version of this discipline. The fabric’s engine is what unifies them behind one auditable decision instead of eleven: each domain becomes an adapter onto the shared engine, required to prove an identical-or-stricter call than it made before — not just “the new engine also blocks things.” See Deployment and Isolation for the full trust story.

The four outcomes

Every evaluation resolves to exactly one risk tier: peer_review and human_approval decisions also carry a TTL and the approver requirements for that specific action — how long the window stays open and who’s allowed to close it. See Expiry and TTL.

Trigger inputs, in plain language

The engine weighs seven inputs together into one decision:
  • Tool capability. Every tool already declares, as part of its own definition, whether it’s read-only, destructive, and/or requires a human in the loop. This is the starting signal — read-only lookups lean toward auto; anything destructive or flagged human-in-the-loop never lands there.
  • Effect classification. What kind of consequence the action has — sending something externally, moving money, deleting data, widening what the agent can do later — independent of which specific tool is involved.
  • Monetary threshold. Whether a dollar amount in the action crosses a line your policy pack sets. A 12callanda12 call and a 12,000 one aren’t the same decision even through the same tool.
  • Sensitive category. Whether the action falls into a category that’s always reviewed regardless of amount — payments, deletions, publishing, credential handling, outbound customer communication. The dunning email below is one of these.
  • Anomaly vs. history. Whether the action looks like the normal pattern for this tenant, or a departure from it — a new recipient, an unusual hour, a volume spike.
  • Tenant policy pack. Your own configured rules, layered on the platform default.
  • Standing grants. A person’s own prior, narrowly scoped “always allow this” decision — bound to a specific host, recipient, or merchant, and carrying its own expiry. A grant only ever narrows what still needs review; it never overrides a deny.
A caller can also tighten — never loosen — behavior per request. The policy field on POST /v1/execute is a bounded view over the same engine: a risk ceiling, budget caps (see budgets-and-limits.md), a model-lane pin, a tool allowlist, and an approval mode. It constrains what one run is allowed to do; it can’t make the tenant pack more permissive.

Fail-closed defaults

An action the engine doesn’t recognize, or can’t classify with confidence, never defaults to auto — unknown means reviewed, not unrestricted. And if the engine itself can’t be evaluated — an error, a timeout — the action is refused rather than waved through. A policy gate that fails open on its own error is not a policy gate. This is the same invariant approvals.md describes for the approval payload itself: what’s presented for review is exactly what’s checked at resume time, with no path around the check.

Versioning and receipts

The policy pack version active at the moment a run is admitted is frozen for that run’s entire lifetime — it can’t change mid-run, the same way a run’s budget and step ceilings can’t (see budgets-and-limits.md). That version travels with the run and is recorded on its receipt. That’s what makes the fabric’s audit trail meaningful months later: you’re not asking whether something would be approved under today’s rules — you’re checking a run against the exact rules that were live when it ran. A replayable audit, not a best-effort reconstruction.

Worked example: the dunning email, classified

Back to Meridian Fabrication (full scenario in approvals.md). The send_email call the agent proposes reads, against the seven inputs above:
  • Tool capability: not destructive — nothing is deleted — but flagged human-in-the-loop.
  • Effect classification: external communication, leaving the tenant’s boundary to a real customer.
  • Sensitive category: outbound customer correspondence tied to a receivable.
  • Monetary threshold: $4,280.00 named in the body — below most tenants’ peer_review line, but not nothing.
That combination resolves to human_approval: one authorized approver’s decision is enough, but the email does not go out unreviewed. A larger invoice, or a first-time recipient the tenant’s history doesn’t recognize, could push the same tool call to peer_review instead under a stricter pack. Same tool, different decision — the decision belongs to the engine, evaluated fresh against this specific action, not fixed to send_email as a property of the tool itself.

See also