Research / Effects, recovery and replay

P22 · full · draft

Speculation by Default: A Verified Proposal, a Graded Dossier and a Single Commit Between Running Composition and Disk

partial full Effects, recovery and replay

State of the work. The proposal and commit loop, undo as a gated change, the gauntlet dossier and a repair loop are shipped with tests; the edit verb existing clients call first still commits by default, two dossier slots are unwired, and no outcome evaluation exists yet.

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

Pitch

An agent's edit is verified on a per-caller proposal and, on the speculative path, only one explicit commit, re-verified against the live composition, can change what runs or what is on disk, and undo is a recompile through the same gate.

Abstract

LLM coding agents usually edit by mutating: the tool applies the patch, then a test run or a human finds out whether it was acceptable, and "undo" is a second mutation that nobody checks. We describe the edit loop of revl's agent session, built around three states: the running composition with the exact source it was compiled from (shared by every caller), a per-caller proposal (a speculative working copy), and disk (an export, written only by one verb). A change is applied to the proposal and run through the same admission, lease and quarantine gates a live swap would face, optionally followed by a gauntlet that grades the candidate in an isolated scratch session and returns a dossier separating what was proved, what was tested with counts and what is merely claimed. On the speculative path nothing swaps or writes until an explicit commit, which re-verifies against the composition as it is now and is refused if another caller committed in between, rather than silently undoing that commit. Undo recompiles the target generation's sources through the compile and admission gate, so a generation a stricter checker now rejects cannot be restored by backdoor. A repair loop composes these pieces (fault, causal trace, bisect, candidate, gauntlet, policy, widening check, swap) and is exercised by an unattended end-to-end test. We report what is shipped: 12 speculation tests, a stale-proposal refusal test and an undo-refuses-rejected-target test. We also report the gaps the design doc itself names: revl_edit still commits by default, the quarantine and lease gates do not cover undo, two dossier slots are hard-coded pending, and the repair loop does not yet use the canary.

Problem and claim

Agent tools put "try this" and "do this" in the same call. The design doc states it directly: the loop an agent runs still mutates on the first step (docs/design/1696-speculation.md:5-11). A rejected-after-the-fact edit has already been seen by every other client, and the only durable form of the work is a file the agent writes outside the system. Undo, where it exists, is often another unchecked mutation (citation to be checked, product docs).

The claim is about the structure of the loop, not about model quality: (a) a speculative change never swaps and never writes; (b) a commit is the only state change another caller can observe; (c) a commit verifies again, and refuses if the running generation moved; (d) export writes exactly the held source (docs/design/1696-speculation.md:64-75). Together with undo being a gated recompile (docs/generation-history.md:46-65), this leaves one admission gate that every swap path reaches, the compile and admission gate, instead of one verification design per verb. Quarantine and lease are not on every path (see below).

The claim has a precise boundary, and the paper should state it. "No mutation path bypasses verification" holds for the admission gate: a swap, an edit commit, an undo and a repair all reach it. It does not hold for every gate: docs/quarantine-tier.md:112-115 says undo and rollback reach the swap with neither the quarantine gate nor the lease check. And the default of the most-used verb is not speculative yet (see Evidence).

Contributions

  1. A three-state model (running composition with held source; per-caller proposal keyed by operator identity; disk as export) with stale-proposal refusal in place of silent overwrite (src/revl/mcp/proposal.py:1-18, src/revl/mcp/proposal.py:71-79; docs/design/1696-speculation.md:13-38).
  2. A speculative-by-default change verb (revl_change) whose proposal runs the full verification and then stops, with commit and discard as the only exits (src/revl/mcp/server.py:1744; docs/design/1696-speculation.md:40-62).
  3. Undo as a gated change: the target generation's sources are recompiled through the same gate as a swap, a rejected target is a refusal result with the composition untouched, and every successful mutating response carries its undo call, or undo: null with a reason (docs/generation-history.md:46-90; src/revl/mcp/session.py:2937-3010; tests/test_undo.py:230).
  4. A graded candidate dossier, revl_gauntlet, run in a throwaway scratch session, with an explicit epistemic split into proved, tested (with counts) and claimed (docs/gauntlet.md:17-35, docs/gauntlet.md:115-125; tests/test_gauntlet.py:191).
  5. A quarantine tier whose verdicts (passed, trapped, timeout, rejected, deferred, unavailable) come from running the candidate under a wasm component runtime, with an admission hook that applies only where policy requires it (docs/quarantine-tier.md:74-116).
  6. A repair loop that composes the above with a closed-by-default self-repair policy and a human-ack interrupt on widened reach, with an unattended exit test (docs/repair-loop.md:19-110, docs/repair-loop.md:179-188; tests/test_repair_loop.py:234).

Evidence on main

Claimpath:line or testStatusNote
Speculative change neither swaps nor writes; builds up; one commit lands ittests/test_mcp_speculation_1696.py:96, tests/test_mcp_speculation_1696.py:113shipped12 tests in the file
A refused proposal is not heldtests/test_mcp_speculation_1696.py:126shipped
Second caller sees only committed changestests/test_mcp_speculation_1696.py:148shipped"second caller" = another operator identity on one server
Stale proposal refused instead of undoing a commitsrc/revl/mcp/proposal.py:71-79; tests/test_mcp_speculation_1696.py:163shipped
Export writes exactly the held source, never a proposaltests/test_mcp_speculation_1696.py:180, tests/test_mcp_speculation_1696.py:191, tests/test_mcp_speculation_1696.py:197shipped
Speculative fill does not boot a drafttests/test_mcp_speculation_1696.py:215shipped
Undo of a committed change and export reports no inversetests/test_mcp_speculation_1696.py:231, tests/test_mcp_speculation_1696.py:240shipped
revl_edit defaults to commitsrc/revl/mcp/server.py:2136, src/revl/mcp/server.py:2156; docs/design/1696-speculation.md:50-54open gapthe doc says flipping it is slice 2, after callers move
Proposal survives reconnect; fork from a proposaldocs/design/1696-speculation.md:87-88designedslice 3
Undo re-admits through the compile and admission gate; rejected target refused, composition untouchedsrc/revl/mcp/session.py:2937-3010; tests/test_undo.py:230shipped
Every mutating response carries its undodocs/generation-history.md:67-90; tests/test_mcp_undo_1703.py (15 tests)shippedverbs with no inverse answer undo: null with a reason
Undo and rollback skip the quarantine and lease gatesdocs/quarantine-tier.md:112-115shipped as stated, contradicts the thesisreaches Session.swap under the operator's undo authority
Gauntlet runs in an isolated scratch session; live composition untoucheddocs/gauntlet.md:115-125; tests/test_gauntlet.py:191shipped9 tests in the file
Dossier splits proved / tested / claimeddocs/gauntlet.md:17-35shipped
Fault sweep and inverse round-trip in the dossierdocs/gauntlet.md:165-180; src/revl/mcp/gauntlet.py:252partialboth slots hard-coded pending; the features exist separately, only the dossier wiring is missing
Quarantine verdicts from a real wasmtime rundocs/quarantine-tier.md:74-88; tests/test_quarantine.py:149, tests/test_quarantine.py:203shippedneeds wasm-tools and wasmtime; tests skip without them; 20 tests
Quarantine gate is opt-indocs/quarantine-tier.md:116-118shippedinert with no requiring policy
Repair loop end to end, unattendedtests/test_repair_loop.py:234; docs/repair-loop.md:179-188shipped16 tests in the file; swap leg needs cordis-py
Repair loop ends in "swap or canary"src/revl/mcp/repair.py:325-370partialonly SwapRemediation is wired; the canary hook reports not-wired; the canary verb exists separately (tests/test_canary.py, 15 tests)
Plan/apply with drift refusal and derived rollbackdocs/apply.md; tests/test_apply.py (13 tests)shippedthe plan is checked against itself and reality; an independent oracle is future work (docs/apply.md:127-130)

Numbers that exist

Paper outline

  1. Introduction: "try" and "do" are the same call; the claim and its boundary.
  2. The three states, the proposal, the commit; guarantees (a) to (d).
  3. Verification stages: admission, lease, quarantine, gauntlet; what each can and cannot show.
  4. Undo as a gated change; the undo contract on every mutating response; where undo is deliberately weaker.
  5. The graded dossier: proved, tested, claimed; isolation of the scratch session.
  6. Composition: the repair loop and its closed-by-default policy.
  7. Evaluation: verify-before-commit against apply-then-test on seeded defects, a multi-caller overwrite count, repair-loop outcomes.
  8. Limits and open gaps (edit default, undo exceptions, pending slots).
  9. Related work and conclusion.

Threats to validity and limits

Fact-check

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

Cite this draft

@misc{revlp22,
  author       = {Thomas Moussajee},
  title        = {Speculation by Default: A Verified Proposal, a Graded Dossier and a Single Commit Between Running Composition and Disk},
  year         = {2026},
  howpublished = {\url{https://inso1337.github.io/revl/research/P22.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