Research / Effects, recovery and replay

P14 · full · draft

Deferred Irreversibility: A Session Commit Protocol That Enumerates What Cannot Be Undone

partial full Effects, recovery and replay

State of the work. Commit protocol, deferral queue, abort and crash recovery are shipped on the Python tier with tests and a small measurement; witness retention is design-only and no external evaluation exists.

This is a draft, not peer reviewed. Repository references link to revl at commit 45bec431f.

Pitch

Instead of asking a human to approve every agent action, type each action by its declared effect, run the reversible ones freely, queue the deferrable ones, and show one prompt that lists the irreversible set the protocol enumerates, bound to a hash so that what was approved is what fires.

Abstract

Agent frameworks guard tool use with per-action approval or with apply-then-undo, and neither tells the user what can no longer be taken back. We describe the session commit protocol of the revl language. Every boundary crossing is classified by a declared extern effect: witnessed (revertible with a checked inverse), deferred (an irreversible tail postponed to the session commit point), or immediate (fires at the call, may carry a best-effort compensation). The class is derived from the checked effect type; the design gives neither the runtime nor the harness a path to move a call between classes (a design statement, checked only against the session-commit design document and the gate guide). Deferred calls are lowered to exactly one form, an enqueue of a WAL-logged named-call descriptor, so the queue is the set of irreversible crossings the commit will cause. Commit is a two-step verb: enumerate a manifest, then confirm with its hash; a stale hash is refused. Abort drops the queue at zero cost and replays witnessed inverses. The crash windows covered by the tests (approval to flush, the approved-but-unflushed window, abort) have specified recovery verdicts. Tiers without a session owner refuse deferred calls at emit time rather than degrade silently. In a harness-boundary experiment reported in the project roadmap archive, 75.0 percent of 24 boundary calls (one harness's tool set) were auto-approved with proof and prompts per session fell from 4.7 to 1.9 over six scripted sessions plus one live session. We state what is not yet shown: the experiment is small, the retention policy for witnesses is a design, and only one of six runtimes owns a session.

Problem and claim

Per-action approval does not scale and trains users to click through. Apply-then-undo hides the fact that some effects (a sent message, an emptied directory) have no undo, and when the undo horizon ends the class of an action silently changes. The usual database answer, two-phase commit with savepoints, assumes the participants can hold a prepared state; an outbound email cannot.

The claim: if the language makes the reversibility class part of the checked effect type, then (1) most agent actions need no prompt, (2) the remaining irreversible set can, by design, be enumerated from the compiler's own audit surface, and (3) one prompt over that set, bound by hash to what will fire, replaces per-action consent. The exhaustiveness argument is a design argument, not a mechanized proof: every crossing is meant to pass through a classified extern, a deferred call has exactly one lowering, and immediate crossings are logged at fire (docs/design/245-session-commit.md:515-545).

Contributions

  1. A three-way action classification derived as a type judgment from the declared extern effect, with the deferred modifier as a declared property in the extern slot (docs/design/245-session-commit.md:259-300; src/revl/parser.py:1647-1654; docs/harness-gate-guide.md:107-135).
  2. A deferral queue that is session-owned, FIFO, WAL-logged at enqueue and flushed once at commit, with write-ahead honesty (intent logged at enqueue, outcome logged after fire) so a crash between fire and outcome leaves one emission unknown rather than falsely done (docs/design/245-session-commit.md:369-420).
  3. A two-step commit with a manifest hash: enumerate, then confirm; a post-enumeration enqueue or swap changes the hash and the confirm is refused (docs/design/245-session-commit.md:515-570; backends/python/runtime.py:5540-5546; tests/test_session_commit.py:251).
  4. An abort path that is exact for classes a and b by design (nothing fired, nothing to offset) and a recovery specification for crash windows, including the approved-but-undischarged window where commit-approved dominates (docs/design/245-session-commit.md:571-595; tests/test_session_commit.py:338,381).
  5. A tier gate: the five tiers without a session owner refuse any call to a deferred extern at emit time, keyed on call sites, because both degradations (fire early, enqueue to nobody) are wrong (src/revl/session_commit.py:1-30,92-110; tests/test_session_commit_tier_gate.py).
  6. Prompts-per-session as a measured quantity: {commit, perCall, residue} counters in the manifest (docs/design/245-session-commit.md:596-614; tests/test_session_commit.py:154-196).
  7. Designed, not shipped: a witness retention policy where dropping a witness is a visible class change with a WAL entry and an undo-horizon marker (docs/design/273-witness-retention.md). This is future work unless built.

Evidence on main

The tree is revl main at commit 45bec431f. The session-commit design document's header still says "design proposed" (docs/design/245-session-commit.md:3), while code and tests show the slices landed; this draft follows the code and the roadmap archive's COMPLETE entry (docs/roadmap-archive/12-post-v2-0-horizon-strategy-review.md:251).

Claimpath:line or testStatusNote
Three classes as a type judgmentdocs/design/245-session-commit.md:259-300; docs/harness-gate-guide.md:107-135shippedGate guide: class is "the worst class over every crossing its checked reach includes"
deferred modifier parses on externssrc/revl/parser.py:1647-1654shipped
Session owner holds queue, escrow, live-frame registry; verdict is session-scopedbackends/python/runtime.py:5157-5175shipped (py only)Docstring: "the gate target ... never the runtime's per-call current frame"
Enqueue lowering with WAL entrybackends/python/runtime.py:4224 (enqueue_deferred); docs/design/245-session-commit.md:369-420shipped (py)Every marked position enqueues, issue #1457 (docs/design/245-session-commit.md:395-420); tests/test_deferred_every_position_1457.py exists
Commit flushes FIFO, witnessed persists, immediates already outtests/test_session_commit.py:154shippedNeeds the pinned cordis fork; skipped otherwise
Abort reverts witnessed and drops deferredtests/test_session_commit.py:197shippedsame caveat
Hash-bound confirm refuses a stale manifesttests/test_session_commit.py:251; backends/python/runtime.py:5540-5546shipped
WAL entry order and crash windowstests/test_session_commit.py:283,338,381shipped
revl_commit, revl_commit_confirm, revl_abort on MCPdocs/guide-ai-agents.md:330-350 (tool table row)shippedGated verb commit per docs/design/245-session-commit.md:79-110
Tier gate on five ownerless tierssrc/revl/session_commit.py:92-110; tests/test_session_commit_tier_gate.py:1-30; backends/java/emit.py:9831shippedDeclared-but-uncalled still emits
Prompts-per-session counterstests/test_session_commit.py:154-196 (prompts == {"commit": 1, "perCall": 0, "residue": 0})shipped
Measurement of classes and prompts on a real harnessdocs/roadmap-archive/12-post-v2-0-horizon-strategy-review.md:274partialQuoted summary of an experiment on a downstream engine; the raw data is not in this tree
Witness retention and undo-horizon markerdocs/design/273-witness-retention.md:1-12; docs/roadmap-archive/12-post-v2-0-horizon-strategy-review.md:498 ("Spec only, implementation follows")design-onlyNo witness-budget, droppable or witness-dropped in src or backends
Recover re-fires owed deferred emissions through the runtime flushtests/test_recover_real_world_1477.py:616shipped, policy-gatedA re-fire happens only when the policy file says "recovery may re-issue owed emissions", after a reissue-fence entry; the default stays owed. This contradicts the design document's open question 2 ("never auto-fires", docs/design/245-session-commit.md:691) as written, which needs rewording
E-Stop interaction: halt strands rather than unwindsdocs/design/teardown-contract.md:113-150shippedA third verdict, relevant to the commit story

Numbers that exist

Paper outline

  1. Introduction: approval fatigue and the undo horizon; the one-prompt claim.
  2. Effects and classes: the extern effect declarations, class as a type judgment, the worst-over-reach rule.
  3. The protocol: session, queue, escrow, two-step commit, manifest hash, abort.
  4. Durability: WAL entries, write-ahead honesty, crash windows and verdicts.
  5. Exhaustiveness argument and the tier gate.
  6. Implementation: the py session owner, MCP verbs, operator gating.
  7. Evaluation: prompts per session, class distribution, adversarial exhaustiveness, user study.
  8. Related work: 2PC, outbox, sagas, agent assistants, capability prompts.
  9. Limits and future work: retention and undo horizon, multi-tier owners.

Threats to validity and limits

Fact-check

Claims in this draft were checked against revl main at commit 45bec431f: 30 checked, 6 corrected.

Cite this draft

@misc{revlp14,
  author       = {Thomas Moussajee},
  title        = {Deferred Irreversibility: A Session Commit Protocol That Enumerates What Cannot Be Undone},
  year         = {2026},
  howpublished = {\url{https://inso1337.github.io/revl/research/P14.html}},
  note         = {Research draft, not peer reviewed. Written against revl commit 45bec431f. Licensed CC BY 4.0.}
}

Text licensed CC BY 4.0, author Thomas Moussajee.

← All research drafts