The problem: an approval can repeat a decision already made
A user selects three documents and asks an agent to prepare a private summary. If the application then asks permission to open each selected document and generate each intermediate note, it may be repeating the same decision. If it never asks again, it may miss a consequential change, such as sending the summary to someone else.
The design problem is to identify when a new authorization decision is needed. I propose reusing a narrowly scoped grant for actions already covered by the task, and requesting a decision when that scope changes. This is a suggested interaction model, not a claim of tested usability improvements.
Use consent fatigue as a warning, not an assumed result
The European Commission's April 2023 cookie discussion paper describes complex banners and repeated choices as a source of cookie fatigue. It argues that this can undermine meaningful expression of privacy preferences. That is relevant design context, but it is not a study of AI agent approval interfaces. [1] (opens in a new tab)
My hypothesis is that fewer redundant requests could leave users more attention for decisions that change authority. The constraint is equally important: reducing prompts must not silently widen the agent's permissions. A lower prompt count alone would not establish a better interface.
Bind each decision to a defined scope
A reusable decision should identify the principal, task, resources, operation, destination, and expiry. Reuse is appropriate only while those constraints match the proposed action. An existing task instruction may already provide the necessary authorization if its scope is clear and the user has authority to grant it.
- Reading the three selected documents for the requested summary can remain within the original grant.
- Reading a different folder changes the resource scope.
- Sending the summary to an external address changes the operation and destination.
- Using the files for a separate task changes the purpose and task scope.
- A revoked or expired grant cannot be treated as an active decision.
The W3C Permissions Working Draft distinguishes granted, denied, and prompt states, and describes permission lifetimes and revocation. That is a useful precedent for separating a stored decision from a request for a new one. It describes browser features, not the agent design proposed here. [2] (opens in a new tab)
Route the action before presenting a prompt
An approval interface should not offer to override a restriction that the user lacks authority to change. The application should resolve policy and grant validity first. I would use four outcomes:
| Outcome | Condition | Interface behavior |
|---|---|---|
| Execute | The action fits a valid grant and needs no fresh confirmation. | Continue; make the action visible in the activity record. |
| Ask | Policy permits the action, but an eligible user must supply a new or renewed decision. | Explain the exact requested change and wait. |
| Block | An applicable rule prohibits the action, or the grant was revoked. | Stop the action and explain the relevant constraint. |
| Hold | Policy, identity, or grant state cannot be established. | Do not execute; resolve the missing information. |
In this example, allow means the application has established that the user is eligible to authorize the proposed action. Trusted services must produce the inputs. Retrieved text and model-generated arguments cannot declare a policy allowed or a grant valid.
def next_step(policy, grant, scope_matches, fresh_confirmation=False):
if policy == "deny" or grant == "revoked":
return "block"
if policy != "allow":
return "hold"
if grant not in {"valid", "missing", "expired"}:
return "hold"
if grant == "valid" and scope_matches and not fresh_confirmation:
return "execute"
return "ask"The function only selects a route. It does not authenticate a user, resolve a resource, verify a signature, or enforce a tool call. Fresh confirmation is a known requirement for a particular action, not a flag the agent may invent whenever it is uncertain.
Check the routing logic with fixed cases
The local check below exercises nine synthetic cases. The cases cover reuse, scope changes, missing and expired grants, revocation, explicit denial, unknown policy, invalid grants, and a required fresh confirmation.
cases = [
(("allow", "valid", True, False), "execute"),
(("allow", "valid", False, False), "ask"),
(("allow", "missing", False, False), "ask"),
(("allow", "expired", True, False), "ask"),
(("allow", "revoked", True, False), "block"),
(("deny", "valid", True, False), "block"),
(("unknown", "valid", True, False), "hold"),
(("allow", "invalid", True, False), "hold"),
(("allow", "valid", True, True), "ask"),
]
for inputs, expected in cases:
assert next_step(*inputs) == expected, (inputs, expected)
print(f"{len(cases)} routing cases passed")9 routing cases passedThis result verifies the stated branches for those inputs. It does not verify a real permission store, action executor, language model, or user interface. In particular, it provides no evidence that users understand the proposed prompts.
Ask for a concrete decision
When an otherwise permitted change needs new authorization, describe it in user terms. Show the actual recipient and a preview of the material involved. Avoid a generic request such as allowing the agent to continue.
Send the forecast summary to Alex?
Recipient: alex@example.com
Material: the summary draft shown in this preview
Purpose: share the forecast summary outside the private workspace
Permission: this send only
[Send this summary] [Keep as draft] [Cancel]This is illustrative interface copy. The application must resolve the recipient and bind the approval to the previewed content and exact action. If the recipient or material changes after approval, the old decision does not authorize the changed send.
A closed prompt, timeout, or missing response is not approval. A declined action should not trigger an immediate sequence of differently worded requests. Offer the available narrower outcome, such as keeping the draft.
Keep review and withdrawal accessible
- Show active grants with their resource scope, destinations, and expiry.
- Expose a clear way to end a grant and stop pending uses of it.
- Record which grant authorized each action, without copying sensitive payloads into the activity view.
- Distinguish stopping future actions from reversing an action already completed.
Recheck authorization immediately before execution. A correct approval screen does not prevent a queued action from using a grant that the user has since revoked. The display and the executor must agree on the same action identity.
Trade-offs and unanswered questions
- Broad remembered preferences reduce interruptions but can hide scope changes. Prefer task-bounded decisions over an unrestricted remember-this-choice option.
- A precise prompt can become too long to scan. Put the action, material, recipient, and duration first; keep technical detail available separately.
- Frequent scope changes may require frequent decisions. A workflow may need a smaller task or a different execution path.
- Policy ambiguity is not a reason to ask the user to approve blindly. Resolve what the action would do before offering a choice.
- The attention benefit remains a hypothesis. The verified example concerns routing correctness only.
The proposed behavior is straightforward: reuse authority that is already valid, request a specific new decision when needed, and preserve the stop condition when authority is absent or unclear.
