Async & Away: UX for Unwatched Runs

6 min read

H8
Playbook · Agent UX & Human Interaction

Nobody watches a forty-minute agent — design for the return, not the wait.

Every streaming token, live thought trace and progress bar you built assumes a user staring at the screen, and past about ninety seconds that user is gone. The expensive problem is not the wait; it is re-entry — the two minutes they spend reconstructing what happened while they were away, which they will pay on every single run. This playbook is about the away case: what to notify, what to put in the artifact instead of your UI, how to make return cost seconds, and why an approval gate with nobody behind it is a deadlock rather than a safety control.

STEP 1

The moment the run outlives attention, the container changes.

A chat transcript is the right shape for a conversation because both parties are present and the last message is the state. Neither holds once the agent runs alone. The user returns to forty messages they did not see arrive, and the state they need — what changed, what is blocked, what needs them — is scattered across those forty rather than sitting at the bottom. The interface stops being a conversation and becomes an inbox over jobs: a list of runs, each with a status, an owner of the next move, and a deliverable.

  • Give every run a stable identity, a URL, and a status the user can read in one glance: running / needs you / done / failed. "Needs you" is the only one that should ever be able to reach out.
  • Design the list before the detail view. Users with async agents live in the list; the detail view is where they go once a run has earned attention.
  • Streaming is not wasted, but it is now a debugging affordance for the minority of watched runs — not the primary surface. Budget your design effort accordingly.

Tell whether you have this problem with one number: the fraction of runs where the user's last interaction precedes the agent's last action. If it is above about a third, you are shipping an async product through a synchronous interface, and every complaint you get about "I couldn't tell what it did" is that mismatch.

STEP 2

Notifications are a budget, and it is spent permanently.

Attention is the one resource in this design that does not replenish. A user who gets pinged for something they did not need to see does not discount the next notification — they mute the channel, and your escalation path is gone at the moment you actually need it. Treat the notification allowance as fixed and small, and spend it only on state the user must act on.

  • Notify on decisions and terminal states. Blocked on approval, hit an irreversible boundary, failed, finished. These are the four; everything else is progress, and progress is a thing you pull, not push.
  • Never notify on percentage. "Step 7 of 12" answers a question nobody away from their desk is asking, and it costs the same as a real alert.
  • Coalesce by run, not by event. A fan-out of twelve sub-agents that each want to say something is one notification about a run, not twelve. Batch on a window; escalate only if the run stays blocked past it.
  • Make the notification actionable in place. If the only action is approve/deny, put approve/deny in the notification. A ping that can only be resolved by opening a laptop is a ping that waits until evening.
STEP 3

The artifact is the status surface.

Users do not come back to your run view to find out what happened; they come back to the thing the agent was making. If the deliverable is a pull request, a document, a ticket, a dashboard, then that object's native surface is where status belongs — because that is where the user already is, and because it survives your product. Push the state into the artifact and the artifact becomes self-describing to people who never touched your UI at all.

  • Write progress into the deliverable as it accrues: a draft PR that updates its own description, a document with a live "status and open questions" block at the top.
  • Make the artifact carry provenance — which run produced it, at which version, and a link back. A reviewer who finds it cold needs to know an agent wrote it before, not after, they trust it.
  • Keep the artifact valid at every checkpoint, not only at the end. An async run that is interrupted should leave something reviewable rather than a half-written file, which is the UX face of durable state.
STEP 4

Re-entry is a diff, not a replay.

The single highest-leverage screen in an async agent product is the one that answers "what changed since I last looked, and what needs me?" — in that order, in about ten seconds. A transcript cannot do this: it is ordered by time, undifferentiated by importance, and it makes the user do the summarising. Build the diff explicitly.

  • Anchor on last-seen. Track the user's read position per run and render against it. "Since you were here" beats "so far" every time, and it is why the same run can show two different summaries to two different reviewers.
  • Order by decision, then by change, then by narrative. Open questions and pending approvals first, then what was created or modified, then the reasoning — which is the folding rule from progressive disclosure applied over elapsed time instead of over a single step.
  • Preserve the deep link. Every summarised line should expand to the exact step, tool call and observation that produced it. Summaries the user cannot verify get trusted for a week and then abandoned.
  • Say what it did not do. Skipped steps, deferred questions and silently-failed tools are precisely what a returning user cannot infer from the artifact, and the omission is where the trust damage happens.

Write the re-entry summary from the agent's own structured run state — decisions, artifacts, blockers — rather than by summarising the transcript with a model. A summary generated from the log inherits the log's hallucinations and quietly drops the step that failed; a summary rendered from state cannot omit a blocker because the blocker is a field.

STEP 5

An approval gate with nobody behind it is a deadlock.

Human-in-the-loop controls are designed on the assumption of a human in the loop. Move the run to async and the same gate becomes a stall: the agent stops at minute three, the user sees it at 9pm, and a task that should have taken an hour takes a day — after which teams do the predictable thing and remove the gate entirely. The fix is to move the human decision earlier, not to delete it.

  • Pre-authorise at dispatch. Show the plan and its consequential steps before the user walks away, and collect the approvals then. One decision up front beats four interruptions later, and it is the same consequence-tiering as approval UX, just front-loaded.
  • Give every block a deadline and a default. A gate must declare what happens when nobody answers within the window — and for anything irreversible that default is stop and hold, never proceed. A gate with no timeout is a run that leaks forever.
  • Let the agent bank the reversible work. Blocked on one approval is not blocked on everything: continue the independent branches, leave the gated one pending, and present both on return.
  • Widen autonomy with evidence, not with impatience. The right response to gate fatigue is a track record that promotes a specific action to unattended, per progressive autonomy — not a blanket "don't ask me again."
STEP 6

When not to go async.

Async is a commitment to bounded blast radius, because the correcting human is not there. If the agent's actions are irreversible, if you cannot cleanly stop a run mid-flight, or if the task genuinely requires judgement at each step, the honest product is a synchronous one that finishes in three minutes — not an async one that discovers at minute thirty that it needed a decision at minute four. Ship async only when a run left entirely alone can fail safely and leave a reviewable trail; the prerequisites are a working kill switch and a bounded set of unattended actions, and if you do not have both, the queue you are building is a backlog of surprises.