AI interface design / Design study
State boundaries for an AI-assisted interface
A design study of untrusted proposals, stale responses, explicit action consent, and recoverable execution.
An assistant can propose a next step without permission to execute it. Preserve that distinction across streaming, edits, retries, and navigation. The model proposes information; the application owns validation, authorization, and execution.
Technical design study: conceptual guidance with illustrative examples and proposed checks, not a description of deployed client architecture, measured production outcomes, or tests already run.
01 / Proposals
Validate shape before interpreting intent
Consider an assistant that suggests updating a task’s due date. Its response may contain a task ID, a proposed date, and an explanation. Parse that response against an explicit contract: required fields, allowed action types, valid dates, and bounded text. Then apply domain checks against current application data. A syntactically valid task ID does not establish that the task exists or that the current user may edit it.
Keep streamed text in a provisional display state. A partial object should never become an executable command, and a schema-valid response is still untrusted input. Render explanation text through the application’s safe text path. The counterexample is passing a generated action name directly to a dispatcher: an unexpected field or persuasive explanation can cross the boundary from suggestion into authority without a deliberate application decision.
02 / Request lineage
A response belongs to a specific context
Associate every request with a request ID, the selected record, and the context revision used to produce it. If the user changes the task or edits its due date while a response is arriving, the old proposal loses commit authority. Cancellation saves resources where supported; an identity and revision check protects state even if a callback still arrives. Regenerating a response should replace the current proposal generation explicitly.
Preserve useful distinctions in the view model: generating, invalid response, valid proposal, awaiting confirmation, executing, succeeded, and uncertain outcome. A generation error is different from an action failure. The user should be able to retry a read without accidentally repeating a write. Store confirmed action outcomes separately from replaceable conversational output so a late generation cannot overwrite evidence of an operation already accepted.
03 / Execution authority
Consent attaches to a concrete operation
Separate reading a task from changing it. Before a write, show the exact task, proposed date, and relevant current value. Confirmation applies to that reviewed payload and revision, not to any later model output. If the payload changes, request confirmation again. The server must independently authorize the operation and reject stale revisions where concurrent edits would otherwise be overwritten. The model’s confidence cannot substitute for either check.
Use an application operation ID for an accepted write and retain it across retries of that same operation. The server needs a deduplication contract; merely placing an ID in a browser request does not guarantee idempotency. After a timeout, report an uncertain outcome and reconcile by operation ID or authoritative state before sending another write. A changed payload is a new operation. This makes retry behavior follow user intent instead of network timing.
System flow
From generated suggestion to confirmed change
- 01
Capture context
Record the task, context revision, and request identity before generation begins.
- 02
Validate the proposal
Parse the completed response and check allowed values against current domain state.
- 03
Review the action
Present the exact change and capture consent for that payload and revision.
- 04
Execute and reconcile
The server authorizes and deduplicates execution; the interface records a confirmed or uncertain outcome.
Decision record
Design decisions and tradeoffs
↔ Scroll horizontally to compare approaches and tradeoffs.
| Decision | Approach | Tradeoff |
|---|---|---|
| Untrusted structured output | Validate both response shape and domain meaning before enabling action. | Some plausible suggestions are rejected, but unsupported actions cannot enter the execution path. |
| Context-bound proposals | Require matching request identity and context revision. | A useful older answer may need regeneration after an edit; it cannot silently act on changed data. |
| Specific write consent | Bind confirmation to the reviewed operation payload. | Writes require an explicit review step, while read-only assistance can remain lightweight. |
| Stable operation identity | Reuse an operation ID only when retrying the same accepted payload. | The server must retain deduplication outcomes and expose a reconciliation path for ambiguous delivery. |
Failure cases & validation
Failure scenarios to validate
Proposed validation scenarios and expected behavior. These are design checks, not reported production test results.
A completed response omits the date or names an unsupported action.
Validation reports the invalid proposal and no execution control is enabled.
A response for task A arrives after navigation to task B.
Its request identity prevents it from replacing B’s proposal or triggering an action.
The proposed date changes after the user confirms.
Previous consent does not authorize the changed payload; review is required again.
Another editor changes the task before execution.
Server revision checks reject the stale write and the interface presents current state for review.
The server accepts the write but the response times out.
The interface reconciles the existing operation before retrying and never treats uncertainty as a confirmed failure.
Reflection
Engineering lessons
- 01
Treat generated content as a proposal that crosses explicit validation and authorization boundaries.
- 02
Make request lineage and consent part of state, so context changes revoke stale authority.
- 03
Design the uncertain outcome before adding retries; idempotency requires a server-side contract.
Evidence & further reading