Connect by JBRH Open Connect

Designing undo for agent actions

There is no single undo control in Connect, and the reason is structural: agent actions do not share one reversal. Some can be withdrawn with nothing left behind, some can only be answered by a second action, and some cannot be taken back at all. Sorting an action into those three before designing the interface is what stops a button promising more than it can do.

Status
Foundation In development What this means
Audience
both, developer
In the app
#/needs-you, #/autonomy-audit
Last verified
Product version
6.3.2

Three classes, and they need different interfaces#

Reversible
The action can be withdrawn and the world is as it was. Nothing left the workspace, so nobody outside it saw anything.
Compensable
The action cannot be withdrawn, but a second action restores the intent. The customer may see both.
Irreversible
Somebody outside the workspace has already received it. The only control is the one that ran beforehand.

The classes are not about difficulty of implementation. They are about who has already seen the result, which is why the interface has to say which one it is offering. An *undo* that quietly means *compensate* teaches people to trust it in the case where it does not apply.

Reversible, in practice#

  • A held draft. It has not been handed to any provider, so there is no message in flight, no delivery attempt and nothing in a sent folder — the recipient sees nothing at all. Discarding it leaves no trace outside the workspace.
  • A priority, a star, a soft delete. A person's own triage lives on the canonical rows and can be set back; threads.priority is read by the engine when it picks work, so changing it back changes behaviour back.
  • A memory row. Any tier can be read and any row forgotten, which is the designed correction path rather than an escape hatch.
  • A file change. Connect creates and modifies files as new versions, with provenance and audit, so the previous version still exists rather than having been overwritten.

Compensable, which is most of the interesting work#

ActionThe compensationWhat is left behind
A scheduled follow-upcancel, snooze, stop_for_thread, stop_for_personNothing external, if it is caught before the drain runs
An opportunity moved to a wrong stageMove it backThe stage history, which is usually worth keeping
A support case opened in errorResolve itThe case record and its timeline entry
A call that was placedA second call, and a note on the recordThe call happened; the customer's phone rang
A lifecycle stage changemove_stage backThe journey record

The distinction from reversible is not academic: a compensating action is itself an action, it goes through the same service, it is subject to the same autonomy rule, and it appears in the audit trail beside the thing it answers. Two entries is the correct record of what happened.

Irreversible, and what to do instead#

A message the provider has acknowledged, a sentence spoken to a caller, and a first contact with a prospect are gone. So is a merge of two people, which is why merge_people stays a human decision even though duplicates is happy to propose one — separating two merged histories afterwards means guessing which follow-up and which deal belonged to which side.

For this class the only real control ran earlier, which is why Connect spends so much of its design there: autonomy modes (draft_only prepares and never asks; ask_before_send puts every outbound in front of a person), four scopes with the narrowest winning, the compliance gate before any outreach, the daily allowance that holds work rather than dropping it, and Needs You as the place held things wait.

What the taxonomy does not give you#

  • An audit trail is not an undo. Knowing what was decided, by what, under which rule, and what happened is necessary and does not restore anything.
  • Time removes options. A phone follow-up more than 24 hours late is closed as missed and never rung, because a very late call-back is worse than none; a call has a 30-minute ceiling. Neither is a reversal, both are limits that reduce the damage a stale action can do.
  • A compensation is visible. The customer sees the correction, so the wording of the second action is part of the design rather than an afterthought.
  • No general undo control exists, and describing one would overstate what is there. What exists is per-action reversal, hold-before-act, and a record of both.

Questions#

Why not build a universal undo over the audit trail?

Because the audit records decisions, not the inverse of every effect. Replaying it backwards would have to un-send a message somebody has read, un-say a sentence a caller heard, and un-merge two histories. The classes that can be reversed already are; a universal control would mostly be a promise about the ones that cannot.

Is a held draft really reversible if the customer is waiting?

The action is reversible; the delay is not. A held draft is not a slow reply, it is no reply, and the clock the customer is counting keeps running. That is a reason to work the queue promptly rather than a reason to send automatically.

Which class should a new agent capability aim for?

Compensable, if reversible is not available. The design question to ask before building is what the second action would be and whether a person could take it confidently — if there is no good answer, the capability belongs behind an approval rather than behind an undo.