> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Policy Engine & Decision Gate

> How NSR governs agentic action execution inside the Temporal engine — the three planes, the policy lifecycle, and the verified decision gate.

# Policy Engine & Decision Gate

How the **policy engine** (NSR) governs agentic action execution inside the **Temporal
orchestration engine**, with the **control plane** as the authoring and sync layer.

<Note>
  The decision gate is rolled out per brand in stages, so whether it is authoritative for a given
  brand depends on that brand's configuration. Do not assume a mutating action is gated — check
  the brand's rollout stage before relying on it.
</Note>

## The three planes

The system separates *who decides policy*, *who reasons about it*, and *who executes* into three
planes with clean contracts between them.

```
┌──────────────────────────────────────────────────────────────────────────┐
│  CONTROL PLANE — author, configure, sync                                 │
│    response-one-prod (Next.js)          engine-control-plane (Rust)      │
│    • policy-engine, prompt-to-rule      • AutomationConfig +             │
│    • sync-org-rules                       policy_controls.action_rules   │
│    • bootstrap-guardrails               • GuardrailPolicy                │
└───────────┬──────────────────────────────────────────┬───────────────────┘
            │ author NL → rules; sync org KB           │ AutomationConfig
            ▼                                          ▼ (per brand/org)
┌───────────────────────────┐          ┌───────────────────────────────────┐
│  POLICY ENGINE — decide   │  verdict │  ORCHESTRATION — execute          │
│  stateset-nsr (Rust)      │◀────────▶│  next-temporal-rs                 │
│  • Neuro-symbolic         │ /v1/     │  • RAV2 workflow                  │
│    reasoning              │ decisions│  • 50+ connectors                 │
│  • Per-org rule KB        │          │  • nsr_reason + nsr_decide        │
│  • Proof chains           │          │  • Determinism via patched() gates│
└───────────────────────────┘          └───────────────────────────────────┘
```

## Policy lifecycle

```
   AUTHOR              SYNC                 ENFORCE              LEARN
   ──────              ────                 ───────              ─────
 NL policy / UI  ─▶ push org rules   ─▶  workflow calls    ─▶  outcome facts
 prompt-to-rule     sync-org-rules       nsr_decide per        record_decision_
 GuardrailPolicy    bootstrap-           mutating action       outcome → NSR
 action_rules       guardrails           → /v1/decisions       forward-chain

```

1. **Author** — a human writes policy in natural language; `prompt-to-rule` compiles it into NSR
   rules. Structured limits — max auto-refund, blocked actions — live in
   `AutomationConfig.policy_controls` and the unified `GuardrailPolicy`.
2. **Sync** — `sync-org-rules` and `bootstrap-guardrails` push the org's rules into NSR's
   per-org knowledge base. **The engine never holds the rule store**; it references the org KB
   that NSR hydrates server-side.
3. **Enforce** — at runtime the workflow asks NSR to authorize each mutating action.
4. **Learn** — `record_decision_outcome` feeds executed and reverted outcomes
   back into NSR's forward chain so rules sharpen over time.

## Two NSR touchpoints

NSR is consulted **twice**, for different jobs. Keeping them separate is the core design
decision.

| Touchpoint        | Endpoint           | Role                                                      | Authority                       |
| ----------------- | ------------------ | --------------------------------------------------------- | ------------------------------- |
| **Reasoning**     | `/api/v1/nsr/chat` | Intent, guidelines, suggested actions, grounding          | **Advisory** — enrichment       |
| **Decision gate** | `/v1/decisions`    | Per-action `approved`/`denied`/`refused` plus cited proof | **Authoritative** where enabled |

The reasoning call shapes *what the agent says and proposes*. The decision gate governs
*whether a mutating action is allowed to run*.

<Warning>
  A confidence number from the reasoning surface is **not an authorization**. That is the gate's
  job — and the gate can **refuse**, escalating to a human, rather than guess.
</Warning>

## Where it sits in the workflow

```
 load_config → get_ticket → skip_check → escalation_check → gather_context
      ▼
 nsr_reasoning  ── advisory: intent, guidelines, grounding ──┐
      ▼                                                       ▼ feeds axes
 function_call_loop → time_sensitive_check → classify
      ▼                          evaluate_action_policies (heuristic)
 nsr_decision_gate  ── authoritative per mutating action ────┤
      ▼                                                       ▼ compare
   verdict → gate_outcome ──▶ review_gate → freshness_check
      └──────────▶ commit (execute connector | review_required | escalate)
```

`evaluate_action_policies` is a pure function applying the structured `action_rules` — blocked,
require\_review, and `max_auto_amount`. The decision gate runs alongside it; where enabled, its verdict is the source of truth.

## The verified decision gate

Read-only actions skip the gate entirely. For each **mutating** proposed action:

```
              ProposedAction (mutating)
                       ▼
          build_decision_input()   pure: args → facts, action → query
                       ▼
          activity nsr_decide → POST /v1/decisions
             (Temporal activity: retried, result cached in history)
                       ▼
     ┌─────────────────┴──────────────────┐
     │ transport error after retries      │ verdict returned
     ▼                                    ▼
  fail_closed()                    approved | denied | refused
  → Refused,                       + ProofChain + confidence
    requires_human_review                  │
                       └──────────┬────────┘
                                  ▼
                        gate_outcome(verdict)   pure, exhaustive match
              ┌───────────────────┼───────────────────┐
              ▼                   ▼                   ▼
      Approved AND            Denied              Refused
      proof == grounded          │              (or fail_closed)
              ▼                   ▼                   ▼
      GateOutcome::Execute   Block{reason}      Escalate{reason}
              ▼                   ▼                   ▼
      run connector          drop action,       durable human-review
                             tell LLM           signal wait
                             "blocked by <rule>"
```

### Two safety properties

These are baked into `gate_outcome` and unit-tested:

* **Grounded-only approvals** — an `Approved` verdict only executes when its proof status is
  `grounded`. A confident-but-ungrounded approval **escalates instead**.
* **Fail-closed** — `Refused` and the transport-failure fallback always escalate. An
  unreachable policy engine never yields execution.

<Note>
  Fail-closed means an NSR outage degrades into human review, not into unauthorized automation.
  Capacity-plan the review queue accordingly.
</Note>

## Why host the gate in Temporal

Putting the gate in a durable workflow rather than a synchronous API guard buys three
properties:

```
  WORKFLOW CODE (deterministic, replayable)   ACTIVITY (non-deterministic I/O)
  ─────────────────────────────────────────   ────────────────────────────────
  • ctx.patched(RAV2_NSR_DECISION_GATE)  ──▶  • nsr_decide → POST /v1/decisions
  • build_decision_input()  (pure)            • retried per RetryPolicy
  • gate_outcome()          (pure)            • result recorded in event history
  • match → execute | block | escalate   ◀──  • replay re-uses the SAME verdict
```

1. **Deterministic, auditable replay** — the verdict and proof chain that authorized an action
   live permanently in the workflow's event history. Replays reuse it, so **the audit trail is
   the execution itself** rather than a separate log that can drift.
2. **Durable human review** — `Refused` is a first-class *state*, not an error. The workflow
   parks on a signal and waits hours or days for a human verdict. A synchronous guard cannot do
   this.
3. **Safe rollout** — new gate logic is `patched()`-gated, so in-flight workflows replay
   deterministically on the old path while new executions take the new one.

## Key types

| Concern                                                                                                    | Location                                  |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| Verified-decision types — `VerifiedDecisionInput`/`Result`, `DecisionVerdict`, `ProofChain`, `GateOutcome` | `engine-types/src/verified_decision.rs`   |
| Rule-first decision pipeline and `GuardrailPolicy`                                                         | `engine-types/src/decision.rs`            |
| `nsr_decide` / `nsr_reason` activities                                                                     | `engine-activities/src/nsr.rs`            |
| Pure gate matcher and input builder                                                                        | `engine-workflows/src/nsr_helpers.rs`     |
| Mutating-action classifier                                                                                 | `engine-workflows/src/tool_inspection.rs` |
| Heuristic policy eval and wiring                                                                           | `engine-workflows/src/lib.rs`             |

## Related

* [Verified Decisions API](/stateset-nsr-decisions) — the `/v1/decisions` contract
* [ResponseAutomationV2](/next-temporal/workflow)
* [Control plane](/next-temporal/control-plane)
