# How to Configure User Gates and Approval Workflows in LoopX

> Learn to configure user gates and approval workflows in LoopX. Block agent execution until decisions are made via CLI or dashboard for seamless human oversight.

- Repository: [huangruiteng/loopx](https://github.com/huangruiteng/loopx)
- Tags: how-to-guide
- Published: 2026-08-07

---

**LoopX treats every human decision point as a durable `user_gate` todo that blocks agent execution until an explicit decision outcome—approve, reject, or defer—is recorded via the CLI or dashboard.**

Configuring user gates and approval workflows in LoopX centers on creating specialized todos that integrate with the control plane's quota kernel. When the system encounters a `user_gate` task class, as defined in [`docs/project-agent-todo-contract.md`](https://github.com/huangruiteng/loopx/blob/main/docs/project-agent-todo-contract.md), it projects an interaction contract that pauses the agent's lane until an operator resolves the gate. This pattern ensures critical oversight points receive explicit human approval while maintaining workflow durability through the state management system.

## What Is a User Gate?

A **user gate** is a specialized todo with `task_class="user_gate"` that represents a concrete decision requiring human judgment. According to the project-agent-todo-contract specification, these gates are durable state objects that the control plane projects into an agent's lane, effectively halting progress until resolved.

The system supports three scoping modes:

- **Agent-scoped**: Uses `--blocks-agent $AGENT_ID` to restrict resolution to a specific agent
- **Global**: Uses `--global-gate=true` for approvals affecting the entire goal
- **Unscoped**: Multi-agent gates that any eligible agent may resolve

When open, the quota kernel treats these gates as hard blocks (`interaction_blocking_user_gate`), surfacing them via interaction contracts while optionally applying notification cooldowns to prevent operator spam.

## Creating User Gates via the LoopX CLI

You configure approval workflows by creating gates using the `loopx todo add` command. The CLI automatically emits a `surface_user_gate` packet that the quota kernel interprets to pause lane execution.

### Agent-Scoped Gates

Create a blocking gate tied to a specific agent when you need granular approval control:

```bash
loopx todo add \
  --goal-id $GOAL_ID \
  --role user \
  --task-class user_gate \
  --blocks-agent ci-runner-01 \
  --text "Approve deployment of artifact v2.1 to staging?"

```

The `--blocks-agent` parameter ensures only `ci-runner-01` execution is paused while other agents may continue processing independent work.

### Global Approval Gates

For decisions affecting the entire goal—such as production releases or merge approvals—omit the agent scoping and use the global flag:

```bash
loopx todo add \
  --goal-id $GOAL_ID \
  --role user \
  --task-class user_gate \
  --global-gate=true \
  --text "Approve production release of version 1.2.3?"

```

Global gates, as documented in [`docs/state-interaction-model.md`](https://github.com/huangruiteng/loopx/blob/main/docs/state-interaction-model.md), block all progress on the goal until the decision outcome is recorded.

## How Gates Are Surfaced to Operators

When the quota kernel encounters a `user_gate`, it generates an **interaction contract** with `kind=surface_user_gate` that projects the gate into the presentation layer (`apps/presentation/dashboard`). The contract includes the mode, decision scope, and response plan.

The system emits a gate packet structure resembling:

```json
{
  "interaction_contract": {
    "kind": "surface_user_gate",
    "mode": "user_gate",
    "response_plan": {
      "kind": "surface_user_gate",
      "decision_outcome": null
    }
  },
  "notification_cooldown": {
    "notification_suppressed": false,
    "deadline": "2026-09-01T12:00:00Z"
  },
  "scoped_user_gate_fallback": null
}

```

**Notification Cooldown**: To prevent spamming operators while a gate remains open, the system creates a `user_gate_notification_cooldown_v0` packet, as specified in [`docs/status-data-contract.md`](https://github.com/huangruiteng/loopx/blob/main/docs/status-data-contract.md), suppressing duplicate notices until the deadline expires.

**Scoped Fallbacks**: When a gate blocks the main lane but independent work can proceed safely, the control plane may project a `scoped_user_gate_fallback`, allowing side-track execution while maintaining the gate block on the critical path.

## Resolving Gates with Decision Outcomes

Operators resolve gates using the `loopx todo complete` command, writing a durable `decision_outcome` back into the todo. This triggers the quota kernel to consume the gate and resume execution.

### Recording an Approval

```bash
loopx todo complete \
  --todo-id $TODO_ID \
  --decision-outcome approve \
  --comment "Reviewed and approved by product owner"

```

Upon resolution, the control plane:

1. **Consumes** the gate, removing it from the agent's lane
2. **Clears** the `notification_cooldown` state
3. **Advances** the workflow to the next lane, or projects any pending `scoped_user_gate_fallback` work

### Deferring Decisions

When immediate approval isn't possible, defer the gate to establish a cooling-off period:

```bash
loopx todo complete \
  --todo-id $TODO_ID \
  --decision-outcome defer \
  --comment "Pending security review, will revisit tomorrow"

```

Setting `decision_outcome=defer` creates a new `user_gate_notification_cooldown` packet, respecting the chosen cadence before resurfacing the gate.

## Common Approval Workflow Patterns

### Global Release Gates

Use global gates for high-stakes decisions requiring centralized oversight. According to [`docs/product/core-control-plane/state-definitions.md`](https://github.com/huangruiteng/loopx/blob/main/docs/product/core-control-plane/state-definitions.md), these gates ensure no agent proceeds past critical checkpoints without explicit sign-off:

```bash
loopx todo add \
  --goal-id $GOAL_ID \
  --task-class user_gate \
  --global-gate=true \
  --text "Approve merge of feature branch to main?"

```

### Scoped Fallback with Independent Monitoring

When the main lane requires approval but monitoring tasks can run safely, implement the scoped fallback pattern. The runtime logic in [`loopx/control_plane/todos/user_gate.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/todos/user_gate.py) supports projecting parallel work via `apply_scoped_user_gate_fallback_projection` while keeping the critical path blocked.

### Multi-Agent Resolution

For gates resolvable by any member of a team, omit `--blocks-agent` but avoid ambiguity by specifying clear decision scopes. The [`repair-patterns.md`](https://github.com/huangruiteng/loopx/blob/main/repair-patterns.md) reference warns against "unscoped_multi_agent_user_gate" configurations that lack explicit resolution criteria.

### Exact Decision Scope Overrides

In complex workflows, enforce precise control using `exact_user_gate_decision_scope_override`, documented in [`docs/reference/protocols/decision-scope-v0.md`](https://github.com/huangruiteng/loopx/blob/main/docs/reference/protocols/decision-scope-v0.md), ensuring only intended agents or specific global contexts can resolve the gate.

## Summary

- **User gates** are durable todos with `task_class="user_gate"` that create hard blocks in the agent execution lane pending human decision
- **Create gates** using `loopx todo add` with `--blocks-agent` for scoped approvals or `--global-gate` for goal-wide decisions
- **Surface mechanism** relies on interaction contracts (`kind=surface_user_gate`) projected by the quota kernel to the dashboard, with optional `user_gate_notification_cooldown_v0` to manage notification frequency
- **Resolve gates** via `loopx todo complete --decision-outcome`, supporting outcomes of `approve`, `reject`, or `defer`, which triggers consumption of the gate and workflow resumption
- **Advanced patterns** include scoped fallbacks for parallel execution and exact decision scope overrides for security-sensitive approvals

## Frequently Asked Questions

### What is the difference between a scoped gate and a global gate in LoopX?

A **scoped gate** uses `--blocks-agent` to pause execution for a specific agent while allowing other agents to continue, whereas a **global gate** (`--global-gate=true`) blocks all progress on the entire goal until resolved. Scoped gates support parallelism through `scoped_user_gate_fallback` projections, while global gates enforce synchronized checkpoints across the workflow.

### How do I defer a user gate decision instead of approving it immediately?

Use `loopx todo complete --todo-id $TODO_ID --decision-outcome defer` to record a deferral. This creates a new `user_gate_notification_cooldown` packet that suppresses gate notifications until the specified deadline, allowing operators to revisit the decision after gathering additional information or completing external reviews.

### What happens to agent execution when a user gate is open?

When a gate is open, the quota kernel treats it as an `interaction_blocking_user_gate`, pausing the affected agent's lane projection. The agent cannot advance past the gate until the operator records a decision outcome. If configured, `scoped_user_gate_fallback` may allow independent side-track work to proceed while maintaining the block on the critical path.

### Where is the user gate runtime logic implemented in the LoopX source code?

The core projection, fallback handling, and cooldown management logic resides in [`loopx/control_plane/todos/user_gate.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/todos/user_gate.py), while the data contracts governing todo structure are defined in [`docs/project-agent-todo-contract.md`](https://github.com/huangruiteng/loopx/blob/main/docs/project-agent-todo-contract.md) and interaction semantics are specified in [`docs/state-interaction-model.md`](https://github.com/huangruiteng/loopx/blob/main/docs/state-interaction-model.md).