# The Approval Button Is Not the Safety System

The safest version of a powerful system is not the one that asks politely before doing something dangerous. It is the one that usually cannot do the dangerous thing at all.

That sounds like wordplay until you build one.

My personal operating system can read across several local tools, prepare work, and propose actions. Some of those actions eventually touch the outside world. A message can leave the machine. A calendar can change. At that boundary, a confirmation dialog is not a safety model. It is the final pixel on top of a safety model, or it is theater.

The useful design question is not:

> Did a human click yes?

It is:

> What exact authority came into existence, what was it bound to, how long did it live, and what made it disappear?

That shift, from approval as a Boolean to authority as a short-lived object, changed how I think about every dangerous capability in the system.

## A proposal is not an action

The system begins by preparing a proposal. A proposal is inert. It can be inspected, edited, rejected, or abandoned, but it cannot reach the outside world.

This distinction matters because generative systems are good at producing plausible candidates. Plausibility is not permission. A useful assistant should be able to fill the workbench without quietly acquiring the keys to the loading dock.

So the first boundary is structural: preparation code does not possess the execution capability. It can create an artifact and ask for review. That is all.

The human decision is stored separately from the artifact. This creates an audit trail, but the audit trail is not the important part. The important part is that the decision can be invalidated.

## Bind the decision to the thing that was reviewed

Suppose I approve a draft, then one sentence changes. Is it still approved?

In many systems, yes. The row says `approved`, so the new bytes inherit the old decision. That is convenient and wrong.

The safer pattern is content-bound approval:

1. Canonicalize the proposed action.
2. Compute a digest of that exact content.
3. Store the digest with the review decision.
4. Recompute it immediately before execution.
5. If the digests differ, the old authority does not apply.

An edit is not a small exception to the approval. It is a new proposal.

This is one of those mechanisms that feels almost disappointingly simple once it is visible. It is also the difference between “someone once approved this record” and “someone approved these exact bytes.”

## Approval and execution should not share a wire

A human approval can be durable without creating durable execution power.

After approval, the system can mint a narrow capability that says, in effect:

> This actor may perform this action, on this artifact digest, before this time, once.

The capability is not a password and not a general role. It is closer to a ticket with caveats:

- one action type;
- one content digest;
- one intended channel;
- one expiry;
- one use.

The execution path consumes it atomically. A copied capability should not become a reusable skeleton key. An expired capability should not be refreshable by wishful thinking. A capability for one payload should be useless for its edited cousin.

This creates a clean separation between identity and authority. Identity answers *who is asking?* Authority answers *what may exist right now?* A trusted process is still not a human gesture, and a human role is still not permission for every action.

## Time is part of permission

Most approval interfaces treat time as metadata: approved Tuesday, executed whenever.

For actions with real consequences, time belongs inside the authorization decision.

A send window is not a reminder to be careful. It is a temporary change in the set of actions the system can perform. Outside the window, the execution verb is absent. Inside it, the verb exists only for content that has survived the other checks.

This is a more useful way to describe safety than “the assistant is supervised.” Supervision is a social arrangement. A time-bounded capability is a technical fact.

It also makes revocation ordinary. Closing a window or letting a capability expire does not require arguing with a model. The action becomes unavailable.

## The hard case comes after success

The clean story ends with execution:

```text
proposal -> approval -> capability -> side effect -> receipt
```

Real systems add one ugly branch:

```text
proposal -> approval -> capability -> side effect -> connection lost
```

Now the local system does not know whether the outside world changed.

Retrying may be correct. Retrying may duplicate the effect. Marking the action failed may be a lie. Marking it complete may also be a lie.

This is where a safety model earns its keep. The system needs a durable execution claim, an idempotency key that survives process death, and a reconciliation path for ambiguous outcomes. If it cannot prove that retry is safe, the honest state is not `failed`. It is `unknown`.

Unknown is uncomfortable. That is precisely why software is tempted to erase it. But uncertainty in the database does not remove uncertainty from the world; it only removes the warning.

## Human-in-the-loop is not specific enough

“Human-in-the-loop” can describe almost anything: a reviewer reading a summary, a click in a modal, a policy owner setting a rule months ago, or an operator actively releasing one exact action.

Those are not equivalent.

For a dangerous capability, I want to be able to answer five questions:

1. What did the human actually see?
2. What exact decision did that gesture record?
3. What narrow capability did it create?
4. What conditions can invalidate that capability?
5. What happens if execution succeeds but acknowledgement fails?

If the answers collapse into “an authenticated user clicked confirm,” the loop is mostly decorative.

## Design the absence of power

The broader lesson is not about email or calendars. It is about negative space.

We tend to describe capable systems by listing their verbs. Read. Search. Draft. Schedule. Send. The safety architecture lives in the periods when those verbs do not exist.

- Before a proposal is persisted, there is nothing to approve.
- Before a separate review, there is no execution capability.
- After the payload changes, the old capability describes nothing useful.
- After expiry, time has removed the verb.
- After consumption, replay has removed the verb.
- During an ambiguous outcome, uncertainty has removed automatic retry.

That is the design I trust most: not a powerful agent repeatedly choosing restraint, but a system that constructs power briefly, for one purpose, and then takes it apart.

Authority should expire. Better yet, most of the time it should not exist.

Try the model yourself in [Authority Should Expire](/authority), an interactive state machine where removing one gate changes which dangerous actions can exist.
