Press Refund. The first press downloads about 10 MB of Python; after that the browser caches it.ctrlrun wheel from PyPI load into this tab. Every line in the box
was produced by ctrlrun here: the page owns the controls and nothing else. Nothing is sent
anywhere; the store is in memory and no socket is opened. The policy the refund runs under, in
cents:
Try this, in order
- €500 on
txn_1— allowed. It runs, and the receipt sayscommitted. - Payment id
txn_2, €2,000 —ApprovalRequired, with the request id a human would answer. Press Approve, change the amount to €5,000, press Refund:ApprovalMismatch. The approval was bound to the action the human saw. Put €2,000 back and it executes on that same approval. - €2,000 on
txn_2once more —ApprovalMismatch (consumed): the approval was single-use. txn_3, €20,000 —ActionDenied. No request is created; a human is not asked about a refund the policy refuses at any size.txn_4, €500, with lose the reply ticked — the remote commits, the reply is lost, the effect isAMBIGUOUS. Untick and press Refund again:AmbiguousEffect, the blind retry is refused, andremote refund callsis still 1. That is the case this library exists for: Outcomes and AMBIGUOUS.- Back to
txn_1, €500 —DuplicateEffect. One logical effect, one reservation.
The five scenarios, end to end
Press the button andctrlrun demo runs in this tab: the same five scenarios you would get
from pip install ctrlrun && ctrlrun demo, on the same wheel.
Press the button. The first run downloads about 10 MB of Python; after that the browser caches it.What the demo prints
What the demo prints
This is the output from the verification run described below, with the generated ids masked:Scenario 1 is the one to read twice:
remote refund calls: 1. The refund committed at the
fake remote, the reply was lost, the agent retried, and the retry was refused. The customer was
refunded once.What is running
The version line in the output is the released version, so it can be behind
main. Everything
else on this site describes the current release.
Verified, and how to check
Verified, and how to check
The Python this page runs was verified under Node against the same Pyodide build on
2026-09-07: Pyodide 314.0.6, Python 3.14.2, SQLite 3.39.0,
ctrlrun 0.5.0 from PyPI, the
playground’s whole sequence and all five demo scenarios. Not a copy of it — the harness reads
both programs out of try-it.js and runs those. It carried its own copy until 2026-09-06, which
is how a syntax error in the page’s copy reached this page: both harnesses were green, one
having stubbed the Python and the other having verified a program the page never ran. Two
harnesses are committed, and you can run both:verify-browser-demo.mjs
is the one that proved the demo runs. A third check needs neither Node nor the network:
tests/test_docs_travelling.py lifts both programs out of the JavaScript on every commit,
compiles the demo’s, and runs the playground’s through the six steps above against the
checkout’s own ctrlrun, asserting each outcome. A refusal that stopped happening fails CI
rather than this page.
verify-browser-wiring.mjs
is the one that proves the controls on this page get wired: this site is a single-page
application, and the first version of the script looked for its container before the page had
rendered it, so the button did nothing.If it does not run here
The page tells you what failed and gives you the command that always works:Next
- Protect your first action: the same thing, in your own process.
- Outcomes and AMBIGUOUS: what scenario 1 means.
- Get started · Why.