August 11, 2026 · BariBariGood

v0.5.0: one call from clean simulator to evidence

Until now, driving a simulator through manzanas meant hand-sequencing the loop yourself: acquire a lease, boot the target, apply fixtures, install the app, launch it, dispatch a dozen actions, capture artifacts, and — on every failure path — remember to release the lease. Every agent I watched do this wrote the same boilerplate, and every one of them occasionally forgot the release and orphaned a simulator.

v0.5.0 makes the whole loop one call. A run is a declarative spec; the daemon owns the choreography:

the run lifecycle
acquire lease → boot → fixtures → install app → launch → steps →
artifact capture → release lease (applying its reset)

The YAML run-spec

You describe what to lease, what to install, and what to do — the native step DSL is just the existing action surface, one action per step, dispatched and journaled exactly like POST /v0/actions:

login-smoke.yaml
# login-smoke.yaml
name: login-smoke
target:
  labels: [ios26]
app:
  path: /Users/ci/builds/MyApp.app   # .app bundle on the daemon host
  bundle_id: com.example.myapp
steps:
  - action: tap_element
    with: {id: username}
  - action: type_into_element
    with: {id: username, text: agent}
  - action: type_into_element
    with: {id: password, text: hunter2}
  - action: tap_element
    with: {label: "Sign In"}
  - action: wait_for_element
    with: {label: "Welcome, agent!", timeout_ms: 5000}
  - name: quality gate
    action: audit

Then:

one call
$ manzanas run login-smoke.yaml -o evidence.md
run run_1a2b3c4d5e6f7a8b: passed
  journal run: lse_0123456789abcdef
  step 0 tap_element: ok
  step 1 type_into_element: ok
  step 2 type_into_element: ok
  step 3 tap_element: ok
  step 4 wait_for_element: ok
  step 5 audit: ok

-o evidence.md writes the journal's markdown export — the same document as GET /v0/journal/{run}/export.md — with every step, tree hash, screenshot, and audit finding. Paste it into a PR comment and the run argues for itself.

Steps are actions, not a new language

I deliberately did not invent a test framework. Each step is one action kind from the protocol, with the payload passed through verbatim:

step schema
steps:
  - name: optional human label
    action: tap_element        # any action kind: tap, swipe, type,
    with: {id: username}       #   tap_element, wait_for_element,
    timeout_seconds: 15        #   scroll_to_element, observe,
    continue_on_error: false   #   screenshot, audit, batch, ...

Steps stop at the first failure (later steps report skipped); continue_on_error lets cleanup steps run anyway. Whatever fails — a step, a boot, an install, the run budget expiring — the lease is always released with its reset. The evidence trail of a red run is as complete as a green one.

Three frontends, one schema

The same spec drives POST /v0/runs (the wire API, sync by default, async with polling for long runs), manzanas run spec.yaml (the CLI), and the MCP run tool — so an agent that speaks MCP gets the one-call loop for free. All three also work pointed at a broker: the run is placed on a fleet host with the same warm-first ranking as lease scheduling, then proxied to the owning daemon.

Also in v0.5.0

Broker-transparent clients. The CLI and MCP server now follow a broker lease's host_addr annotation automatically — point them at the broker and every lease-scoped call routes to the owning daemon. Fleet dashboard. The broker serves an aggregated /dash across every daemon, next to each daemon's own. Version surfacing + optional auth. Binaries report the build version stamped at link time, and --auth-token puts a shared bearer token in front of the whole API. Mock actions backend. --mock now carries a full deterministic action backend, so the entire loop — including runs — works on a Linux box with no Mac at all. That one gets its own post.

v0.5.0 is on GitHub and the Homebrew tap. Full run-spec reference in docs/runs.md.