Overview

Verify and receipts

Judge browser work any agent, script or person did, without a model, and keep a receipt anyone can check offline.

Overview#

An agent saying “done” is a claim. MLola turns it into something a reviewer, a CI gate or an auditor can check, including for work MLola did not do.

  • mlola browser run drives the browser and judges the result.
  • mlola browser verify judges browser work that something else did: another agent, a test script or a person. It uses no model and acts on nothing.
  • A receipt comes out of both: a small JSON file with the verdict and everything needed to check it again, offline, with mlola browser receipt check.

All three use the same criteria language and the same three verdicts: verified, failed and inconclusive. Only verified is proof. For a whole change rather than one journey, see Prove a change; its browser criteria carry receipts like these.

Verify#

Terminal
# A deployed app, opened fresh in MLola's own browser (signed in, if needed)
mlola browser verify --url https://staging.example.com/orders \
  --storage-state ~/.mlola/staging-session.json \
  --expect-text "Order #1042" \
  --expect-readback "/api/orders/1042=paid" \
  --expect-no-errors

# The browser another agent is driving: attach observe-only
mlola browser verify --cdp http://127.0.0.1:9222 --page /checkout \
  --expect-text "Thank you for your order"

Criteria: --expect-url, --expect-text, --expect-title, --expect-readback <url>[=<text>] and --expect-no-errors, or --criteria criteria.json. All of them must hold.

With --url, MLola opens the page in its own browser, looks until the criteria hold (15 s by default, --watch <s>), and then judges. Because it saw everything that happened there, --expect-no-errors can be judged.

With --cdp, MLola attaches to a running browser as a witness: it opens, closes, focuses, accepts or dismisses nothing, refuses every action including navigation, and leaves every tab as it was. A single look after the work cannot see the work's history, so a criterion about errors, responses or downloads is reported as cannot be checked (inconclusive), never as “no errors”. To judge that history, start verify --cdp … --watch 120 before the agent starts.

Exit codes: 0 verified, 1 failed, 3 inconclusive. The SDK exposes verifyBrowser from @mlola/browser-runtime, and agents get the MCP tool browser_verify.

Receipts#

Terminal
mlola browser receipt check receipt.json                     # is it sound?
mlola browser receipt check receipt.json --trust ed25519:3f…  # …and signed by a key I trust?
mlola browser receipt show receipt.json --markdown
mlola browser receipt keygen                                  # sign every receipt from this machine

receipt check answers, in code: does the digest match the content; is it signed by the key it names (and one you trust, with --trust); judged again against what it recorded, do the criteria give the verdict it claims; and do the trace and artifacts match their hashes. A receipt that claims verified over a record that does not support it is contradicted, even with a valid digest and signature.

Exit codes: 0 sound and verified, 1 sound but not verified, 4 malformed, tampered, untrusted or contradicted.

What a receipt does not prove#

  • That the page was not staged: anyone who controls the app can make it show anything. Readbacks against the real backend, and a signing key only your CI holds, are what make a receipt worth trusting.
  • More than its criteria: verified means the stated criteria held, and nothing more.
  • Things it could not see, such as the history of a session MLola attached to after the work. It says so (inconclusive), and does not guess.