# Irreversibility Gate

> Pattern 05 · Approving actions · part of Agent Consent Patterns.
> Human page: https://agentconsent.dev/patterns/irreversibility-gate/

Weight the confirmation to the consequence. A reversible action should cost a click; an irreversible one should cost a deliberate, legible gesture, and nothing in between should feel the same.

## Problem

Most products confirm everything the same way: one modal, one "Are you sure?", one OK button. When every action carries identical friction, the friction stops meaning anything. Users learn the rhythm (modal, click, done) and apply it just as fast to "delete 1,240 files forever" as to "archive one draft." The uniform gate trained them to click through the exact moment it was supposed to stop them. Agents make this worse: they propose actions in batches, at machine speed, and a user rubber-stamping a queue has no built-in pause where the irreversible one stands out.

## Solution

Make friction a function of consequence. Grade actions by how hard they are to undo, and let that grade drive both how the gate looks and what it asks of the user:

- **Reversible**, confirm in a single click. The action can be walked back, so the gate mostly exists to inform.
- **Undoable**, confirm in a click, but advertise the recovery path (an undo window, a trash that can be restored) so the user knows the safety net is there.
- **Irreversible**. The confirm turns destructive, the specific consequences are enumerated, and the action stays locked behind a deliberate gesture: typing the confirm phrase.

> **Live demo** — an interactive Irreversibility Gate demo runs on the page: https://agentconsent.dev/patterns/irreversibility-gate/

The demo is the `IrreversibilityGate` component from `@agentconsent/react`. Change the `severity` prop to watch the gate re-weight itself. The type-to-confirm field appears only at the irreversible tier, where the cost of a misfire justifies it.

## Anatomy

> **Anatomy** — a labeled breakdown of the Irreversibility Gate component's parts is shown on the interactive page: https://agentconsent.dev/patterns/irreversibility-gate/

## When to use it

- **Genuinely irreversible actions**, permanent deletion, money that leaves, messages that reach real people, anything with no undo on the other side.
- **A queue of mixed-stakes actions**, where one destructive item hides among routine ones and needs to visibly break the user's approval rhythm.
- **When the agent inferred the target.** If the agent chose *what* to delete or *whom* to pay, the enumerated consequences are the user's chance to catch a wrong inference before it lands.

## When not to use it

- **Reversible, low-stakes actions.** Gating an archive or a draft-save with type-to-confirm is friction theatre; it teaches users that the heavy gate is noise and blunts it for the actions that need it.
- **High-frequency actions in a hot loop.** If the user takes the same irreversible action dozens of times a session, the answer is a standing authority boundary or a spend limit, not a phrase typed forty times.
- **As a substitute for reversibility.** The best gate is not needing one, prefer an undo window over a scary confirm wherever the system can offer it.

## Real-world examples

- **GitHub's repository deletion.** The canonical type-to-confirm: deleting a repo requires typing its full name into a field that enumerates exactly what will be lost. Archiving, by contrast, is a single click, friction graded to consequence, across the same settings page.
- **AWS typed confirmations.** Deleting an S3 bucket requires typing the bucket name; deleting a CloudFormation stack or disabling termination protection each add their own deliberate gesture. The heavy gates appear only where recovery genuinely doesn't exist.
- **Coding agents on destructive commands.** Claude Code keeps stopping for approval on state-destroying shell commands (a force-push, a recursive delete) even when the user has auto-accepted routine file edits. The approval rhythm breaks exactly where the consequence tier changes, which is this pattern's whole argument.

*Annotated screenshots of these flows are being collected. Products are credited, annotations follow the site-wide callout conventions, and any screenshot is removed on request. See [About](/about/).*

## Accessibility

- **Inline mode** renders a `role="group"` labeled by the action title, so screen readers announce the stakes on entry ("Permanently delete 1,240 files, group") before the user reaches the controls.
- **Modal mode** (`asModal`) is a Radix `AlertDialog`: focus is trapped, `role="alertdialog"` is named by the title, and initial focus lands on **cancel**. The least destructive action is always the resting default.
- **Escape cancels**, routed through `onCancel` like the button. Dismissing is an explicit "no", never a way to leave the action pending.
- **Type-to-confirm over hold-to-confirm.** A press-and-hold button is invisible to screen readers, unusable with a switch or one-handed, and arbitrary about duration. Typing a known phrase is a standard text input: labeled, focusable, and unambiguous about what completes it.
- The confirm button exposes its locked state with a real `disabled` attribute plus `aria-disabled`, so assistive tech announces that it cannot yet be activated. The type-to-confirm field is `htmlFor`-associated with its label.
- Severity is never carried by color alone: it changes the confirm label, the icon, and whether a phrase is required, not just the palette.

## Anti-patterns

- **Uniform friction.** One "Are you sure?" for everything is the pattern's reason to exist. If archiving and permanent deletion feel identical, neither gate is doing its job.
- **Friction with no information.** A heavy confirm that still won't say *what* it will do ("This action cannot be undone. Continue?") makes the user commit blind. Enumerate the specifics, or the ceremony is empty.
- **Hold-to-confirm as the only path.** Trendy, and an accessibility dead end. If you must offer it, offer type-to-confirm alongside, and prefer typing as the default.
- **Default-focused confirm.** Enter-to-confirm on the destructive action turns the gate into a speed bump the user clears by reflex. The cheapest gesture must be the reversible one.
- **Consequence inflation.** Wrapping reversible actions in irreversible-tier language ("This will permanently…") when it won't. Crying wolf here is how users learn to ignore the real warnings.

## Code

```tsx
import { IrreversibilityGate } from "@agentconsent/react";
import "@agentconsent/react/theme.css";

<IrreversibilityGate.Root
  severity="irreversible"        // "reversible" | "undoable" | "irreversible"
  confirmPhrase="DELETE"          // required-type gate, irreversible tier only
  onConfirm={() => execute(action)}
  onCancel={() => dismiss(action)}
>
  <IrreversibilityGate.Header>
    <IrreversibilityGate.Icon>⚠</IrreversibilityGate.Icon>
    <IrreversibilityGate.Title>
      Permanently delete 1,240 files
    </IrreversibilityGate.Title>
  </IrreversibilityGate.Header>

  <IrreversibilityGate.Description>
    The agent wants to empty the export directory for good.
  </IrreversibilityGate.Description>

  <IrreversibilityGate.Consequences>
    <IrreversibilityGate.Consequence>
      Permanently removes 1,240 files (4.2 GB).
    </IrreversibilityGate.Consequence>
    <IrreversibilityGate.Consequence>
      Cannot be undone, no version history or backup exists.
    </IrreversibilityGate.Consequence>
  </IrreversibilityGate.Consequences>

  <IrreversibilityGate.ConfirmField />

  <IrreversibilityGate.Actions>
    <IrreversibilityGate.Cancel>Keep files</IrreversibilityGate.Cancel>
    <IrreversibilityGate.Confirm>Delete forever</IrreversibilityGate.Confirm>
  </IrreversibilityGate.Actions>
</IrreversibilityGate.Root>
```

`ConfirmField` renders nothing unless the gate actually requires a phrase
(irreversible severity + a `confirmPhrase`), so you can leave it in the tree
across all severities. Pass `asModal` with `open`/`onOpenChange` to render as a
focus-trapped alert dialog. All parts are unstyled primitives with `data-acp`
attributes; skip `theme.css` and style them yourself, or override the `--acp-*`
tokens to retheme.
