Cloop already exposes reversibility signals in its trust surfaces, but too much of that reversibility is still advisory text rather than an executable control.
The backend already supports undo in two important places:
- single-loop event undo in
src/cloop/loops/events.py::undo_last_event, exposed over HTTP atPOST /loops/{loop_id}/undoand via MCPloop.undo - planning checkpoint rollback metadata and rollback execution helpers in
src/cloop/loops/_planning_workflows/execution_rollback.py, surfaced through planning execution metadata insrc/cloop/loops/planning_workflows.py
The frontend already renders receipt cards with trust.rollbackLabel, but that label is display-only. A user can be told something is reversible without being given an actual undo action. That weakens trust exactly where trust is supposed to be strongest: after consequential work lands.
Whenever Cloop already has real backend reversal support, the user gets a first-class executable undo or rollback action from the same receipt and history surfaces that describe the landed result.
Reversible outcomes should behave consistently across planning, review, enrichment, working-set, and command-palette flows:
- reversible work gets an executable
undo_action - rerunnable work gets an explicit
rerun_action - irreversible work is labeled clearly as irreversible
- stale or no-longer-safe undo paths are disabled with explicit reasons
- successful undo creates its own landed receipt and resume target
- Reverse an unintended action without leaving the current workflow context.
- Know whether an executed result is actually reversible or only described as reversible.
- Undo from the receipt card, recent history, operator workspace, or command palette.
- Understand where they will land after undo completes.
- Providing global time travel or multi-step arbitrary history replay.
- Pretending every mutation is undoable.
- Hiding partial rollback risk behind a one-click UI.
- Replacing confirmation and trust metadata for consequential reversals.
- Trust at the point of action.
- Action over narration.
- One obvious next move.
- Workflow handoffs beat tab jumping.
- Human authority, AI acceleration.
The executable undo model is a first-class shared workflow contract.
- loop undo is freshness-safe and exact-handle only:
POST /loops/{loop_id}/undo- body requires
expected_event_id - stale handles are rejected explicitly instead of undoing "whatever is latest"
- loop undo responses return enough data to land a fresh receipt:
- restored loop payload
undone_event_idundo_event_idundone_event_type
- loop mutation responses expose reversible-event metadata so the frontend can attach real undo handles:
latest_reversible_event_idlatest_reversible_event_type
- planning rollback is a public transport contract:
POST /loops/planning/sessions/{session_id}/rollback- body requires
run_id - only the latest active run can be rolled back
- fully rolled-back runs stay in history but are marked inactive for continuity and analytics
- planning execution payloads carry a shared executable
undo_actioncontract plus advisoryrollback_cuesacross HTTP, CLI, MCP, and web, so clients stop re-deriving rollback handles from raw cue counts or prose - working-set undo is a public exact-handle contract:
POST /loops/working-sets/undo- body requires
expected_event_id - stale handles are rejected explicitly instead of undoing a newer working-set change
- reversible working-set responses expose
latest_reversible_event_idandlatest_reversible_event_type
frontend/src/contracts-ui.tsincludes a first-classundoaction type on shared operator action cardsRecentShellActionOutcomestores both trust copy and a structured executableundoActionfrontend/src/executable-undo.tscentralizes:- loop-event undo handles
- planning-run rollback handles
- HTTP execution helpers
- stale-action failure handling
- post-undo receipt shaping
Executable undo appears anywhere the backend already exposes a safe inverse contract:
- planning execution receipts and operator handoff cards
- enrichment apply receipts
- working-set continuity receipts for create/update/delete/focus/pin/stage/defer/reorder/remove/bulk-add flows
- recent shell-action continuity entries
- operator "since last" outcome cards
- command-palette quick undo commands
Successful undo or rollback creates a new landed receipt with a clear resume target. Stale or drifted handles are disabled with explicit reasons instead of silently failing.
Cloop should distinguish three cases clearly.
Use when the backend already supports a precise reverse operation for the landed result.
Examples:
- loop update, status, or close mutations handled through
undo_last_event - planning execution results with explicit
rollback_actions
Use when rollback exists but may be partial, best-effort, or stale-sensitive.
Examples:
- planning execution rollback that may fail for some actions if downstream state has drifted
- multi-resource outcomes where some actions are reversible and others are not
The UI should still expose executable rollback, but with stronger trust language and confirmation.
Use when no safe backend reversal exists yet.
In this case:
- show trust metadata clearly
- do not render a fake undo button
- keep copy explicit about why reversal is unavailable
Undo must never be inferred from label text alone.
An executable undo action must be backed by structured backend-aware data, not by guessing from rollbackLabel. The system should treat "rollback supported" as different from "safe to execute this exact undo now."
That means the UI needs a real undo handle or validated target, not only a string.
Executable undo should appear in the same follow-through surfaces that already show receipt outcomes:
- receipt cards rendered from
OperatorActionCard - recent-action history entries built from
RecentShellActionEntry - operator workspace "Since last visit" items when a landed receipt is shown there
- review workspace post-action follow-through
- working-set follow-through receipts
- command-palette recent actions and quick undo commands
The interaction model should stay consistent across surfaces: the same outcome should not be undoable in one place, rerunnable in another, and text-only somewhere else.
| Outcome | Tier / handle | Transports | Receipt / recent / palette | Stale behavior |
|---|---|---|---|---|
| Relationship confirm / dismiss | Reversible · relationship_decision |
HTTP, web, CLI, MCP | Receipt cards, recent history, and command palette all reuse the same saved review follow-through handle. | Exact pair-state validation disables undo with the backend-provided stale reason if the relationship pair moved. |
| Enrichment apply | Reversible · loop_event |
HTTP, web, CLI, MCP | Receipt cards, recent history, and command palette all reuse the shared loop-event undo handle emitted by the applied outcome. | Loop-event freshness validation disables undo with the specific drift or not-found reason when newer loop work landed. |
| Enrichment reject | Irreversible · — | HTTP, web, CLI, MCP | Receipt cards, recent history, and command palette show the generic irreversible rollback label only; no executable undo action is emitted. | Not applicable because no safe inverse exists. |
| Clarification answer-only | Reversible with stale guard · clarification_answer |
HTTP, web, CLI, MCP | Direct browser submissions, durable receipts, recent history, and command palette all reuse the same clarification-answer undo handle. | Undo is rejected with the specific stale-state reason when a later pending suggestion references the same question text. |
| Clarification answer + rerun | Irreversible · — | HTTP, web, CLI, MCP | Review follow-through renders receipt metadata and recent history, but no receipt, recent-history, or command-palette undo control is exposed. | Not applicable because rerun refinement remains intentionally irreversible. |
| Working-set mutation | Reversible · working_set_event |
HTTP, web, CLI, MCP | Receipt cards, recent history, and command palette all reuse the exact working-set event handle. | Exact event freshness validation disables undo with the backend-provided drift reason if later working-set changes landed. |
| Planning checkpoint | Reversible / best-effort rollback · planning_run |
HTTP, web, CLI, MCP | Receipt cards, recent history, and command palette all reuse the backend-authored rollback handle and confirmation copy. | Only the latest active run can roll back; stale or superseded runs disable rollback with the backend-provided reason. |
Planning execution already exposes the richest rollback metadata. The UI should:
- render a first-class rollback action when
undoableis true androllback_actionsexist - explain when rollback is partial or best-effort
- return the user to a sensible post-rollback surface, typically the planning session or a restored downstream object context
Review and enrichment flows often land as loop updates, closes, or status transitions. When the landed result maps to a reversible loop event, the receipt should expose executable undo through the same loop-event undo path used elsewhere.
If a review action is not actually reversible, the receipt should remain explicit about that.
Working-set mutations use the same executable undo model as loop and planning receipts.
Working-set undo coverage includes:
- create, update, and delete of named working sets
- active working-set / focus-mode context changes
- single-item pin, stage, defer, remove, and reorder mutations
- bulk loop-add expansion into the active working set
These receipts carry exact working-set event handles through the shared undo contract, so recent history, operator outcome cards, and command-palette quick undo all reuse the same safe backend path.
The command palette should reuse the same executable undo handles already attached to receipt outcomes. It should not invent a separate undo path. Keyboard-first users should be able to undo recent reversible actions without leaving the palette flow.
- User executes a reversible mutation.
- The resulting receipt card includes an explicit Undo or Rollback action.
- User invokes undo from the receipt.
- Backend executes the matching reversal path.
- UI renders a new landed receipt describing what was restored and where to continue.
- User opens recent actions or "Since last visit."
- A prior landed receipt still has a valid executable undo handle.
- User triggers undo from that history entry.
- If the handle is still valid, undo runs and emits a fresh receipt.
- If the handle is stale or unsafe, the action is disabled and the reason is shown.
- User applies a review decision or enrichment suggestion.
- The review workspace emits a receipt card describing the landed change.
- If that change maps to a reversible backend event, the receipt includes Undo.
- Undo restores the prior loop state and keeps the user in a sensible review or loop context.
- User opens the command palette.
- Recent reversible outcomes are offered as quick undo commands.
- User executes undo directly from the keyboard.
- The palette closes into the post-undo resume target or refreshes the recent list, depending on context.
- Irreversible action: show a clear irreversible label and no executable undo control.
- Stale undo handle: disable the action and explain that newer changes or drift prevent safe rollback.
- Partial rollback: if only part of a planning rollback succeeds, show the partial result explicitly with failure detail and next-step guidance.
- Concurrent changes after the receipt: the undo path must validate freshness so an old receipt cannot silently undo a newer change.
- Claimed loops: if loop undo requires claim-aware execution, the transport contract must expose the needed claim context before web undo is treated as complete.
- Repeated clicks or retries: undo actions should be idempotent or clearly reject duplicates without creating ambiguous state.
- Deleted target after execution: show why undo is no longer available rather than failing silently.
frontend/src/contracts-ui.tsshould gain a first-class undo action contract instead of overloadingeventorrollbackLabel.- Shared operator action cards should be able to carry both trust copy and executable undo actions without introducing a separate receipt-only renderer.
RecentShellActionOutcomeshould carry structured executable undo metadata, not justrollbackLabel.frontend/src/action-receipts.tsshould be able to attach undo actions to receipt cards using the same shared action-card model as open, pin, stage, and defer actions.TrustSurfaceMetadata.rollbackLabelshould remain trust copy, not the executable contract.- Planning rollback needs a public transport-safe execution path. Internal helpers in
execution_rollback.pyare not sufficient by themselves for frontend-triggered rollback. - Loop-event undo should validate that the intended reversible event is still the correct one to undo. If the existing
POST /loops/{loop_id}/undocontract is too coarse for safe receipt-driven undo, it should be extended with freshness validation such as an expected event identifier or equivalent server-side guard. - Working-set undo should follow the same freshness-safe rule, using durable working-set event handles instead of advisory rollback copy.
- Undo responses should return enough structured data to create a new receipt and handoff result so continuity remains coherent after rollback.
ShellLocationContractshould remain the canonical navigation target for post-undo landing states.
- Lock the backend-safe undo contract
- decide what structured undo handle the frontend may safely store and replay
- distinguish direct loop-event undo from planning rollback actions
- Add shared frontend action support
- extend the shared card and receipt model to represent executable undo cleanly
- keep advisory rollback text separate from executable actions
- Ship the highest-value reversible flows first
- planning rollback and loop-event undo are the first mandatory surfaces
- review, enrichment, working-set, and command-palette follow-through then reuse the same shared model
- Define degraded behavior
- stale handles, partial rollback, drift, and irreversible actions should all render explicit reasons instead of generic failure messages
- Any receipt for a backend-reversible outcome can render an executable undo or rollback action through the shared action-card model.
- Reversible and irreversible outcomes remain visually consistent, but only truly reversible outcomes render executable controls.
- Undo is available from receipt cards, recent history, and command-palette recents using the same underlying handle.
- Review, enrichment, planning, and working-set follow-through all use the same reversibility model instead of bespoke per-surface affordances.
- Stale, partial, or no-longer-safe undo paths are explained explicitly.
- Successful undo emits a new landed receipt with a clear resume target.
trust-surfaces.mdai-action-cards.mdworkflow-handoffs.mdreview-redesign.mdworking-sets.mdcommand-palette.mdstate-navigation.mdoutcome-continuity.md
Tier: Reversible with stale-state guard
- Backend:
undo_clarification_answersinenrichment_review.py - Restores clarification rows to unanswered state
- Reopens suggestions that were superseded by those answers
- Stale-state guard: if any pending suggestion now references the same question text, undo is rejected — the answer is irreversible because undoing would invalidate the new suggestion's context
- HTTP:
POST /loops/{loop_id}/clarifications/undo - Web: direct loop suggestion surface answer-only receipts reuse the same HTTP contract
- MCP:
clarification.undo - CLI:
cloop clarification undo --loop-id ID --clarification-id CID ...
Tier: Irreversible
- The cascading 8-step rollback (new suggestions, loop field mutations, enrichment state, events, embeddings, webhooks) is too fragile to expose as a single atomic undo
- Review-session follow-through carries
undo_action=Nonewith explicitrollback_label: "Undo is not available for this enrichment outcome."
- Should planning rollback be presented as "Undo" when it is complete and as "Rollback" when it is best-effort, or should one label be used consistently?
- For multi-resource outcomes, should the user get one top-level rollback action or an intermediate confirmation view listing the exact rollback operations that will run?