← Lab

ronsel: E2E Flows You Can Read, Run and Commit

6 min read ·

A year ago we open-sourced flows, a small orchestrator that ran multi-step end-to-end tests from YAML files. It did its job, and we kept using it on client projects. Along the way it grew a web UI, a notebook, AI assistance, remote agents and a second protocol or three, until the name stopped describing the thing. Today we are releasing that thing as ronsel, version 2.0, at github.com/lab34-es/ronsel and ronsel.lab34.es.

Ronsel is Galician for the wake a boat leaves on the water: the trace of where something has been. That is what the tool produces. Every run leaves a record of what was called, what came back, and whether it was what you expected.

The Problem, Still

The systems we build are chains: a user registers, a checkout completes, an IoT device receives a command. Each link is a different service with its own protocol, its own quirks, and a dependency on the link before it. Unit tests cover the links. What breaks in production is the chain.

Testing the chain usually means one of two things. Either a folder of scripts nobody but the author can read, or a document that describes the scenario and is executed by hand, by whoever is on rotation, with the results pasted into a ticket. The first is executable but opaque. The second is readable but does not run.

A Flow Is a Markdown Document

ronsel’s answer is to stop choosing. A flow is a Markdown file. You write headings, prose, warnings and notes as you would in any document, and mark the executable parts as fenced code blocks tagged step:

---
title: Fraud detection
description: Fraud must be detected when the customer is flagged
owner: ana
tags: [smoke, payments]
---

# Fraud detection

The invoice endpoint must refuse to answer for a flagged customer.

> [!WARNING]
> This flow creates a real customer in the environment it runs against.

```step
application: "accounting"
method: "getInvoice"
parameters:
  params:
    customerId: "{{ randomInt0_100 }}"
mimic:
  - application: "fraud"
    url: "/fraud-detection"
test:
  status: 404
  body:
    error:
      code: "ACCOUNTING_FRAUD_DETECTED"
```

Press Run and the steps execute in order. The request, response, assertions and timings of each one appear right below the block that produced them, the way a Python notebook shows a cell’s output under the cell. The same file is what a reviewer reads in a pull request, what QA opens to understand the scenario, and what the pipeline executes at two in the morning.

Three nouns carry the whole tool:

Everything sits in one folder, the context, which is meant to be a git repository of yours. A colleague clones it, runs npm install && npm run ronsel, fills in the env files, and has the same flows against the same applications.

What Changed Since flows

flows (2025)ronsel 2.0
Flow formatYAMLMarkdown with step blocks
InterfaceCLINotebook-style web UI + CLI
ProtocolsHTTP, MQTTHTTP, MQTT, PostgreSQL, browser via Playwright
Writing flowsby handby hand, or described in plain words to Ollama, Gemini or Anthropic
Where it runsyour machine, CIyour machine, CI, or a remote agent inside a network you cannot reach
Resultsterminal outputa test-run folder with a standalone HTML report, per run
Sharing credentialsmanualone YAML export a teammate imports in one paste

What did not change: steps are declarative, every run gets fresh random data through replacers like {{ uuid }} and {{ randomEmail }}, a memory carries values from one step to the next, and a step can mimic a dependency so a failure scenario is reproduced locally without breaking anything for real. In the example above, accounting is called for real, and what it asks fraud is answered by a fake for the duration of the step.

Assertions Beyond Equality

The test section compares status and body key by key, as deep as you write it. For anything else, a value prefixed with $expr: is a JavaScript expression over the actual value:

test:
  status: 200
  body:
    count: "$expr: value > 10"
    items: "$expr: Array.isArray(value) && value.length >= 3"
    createdAt: "$expr: new Date(value) > new Date('2026-01-01')"

Some effects never come back in the response: an HTTP call triggers a job that eventually publishes an MQTT message. Latent applications subscribe before the flow starts and let a step assert that the message arrived, with a retry window, wildcards in the topic, and a subset match on the payload.

Written With AI, Read by You

Describe a scenario in plain words and ronsel writes the flow from your own applications. The model is given the format rules, the catalogue of your applications (built from the JSDoc of each index.ts), and your prompt. Nothing more. What comes back lands in the editor as ordinary Markdown for you to read before anything runs. With a local Ollama, nothing leaves your machine. The same works for edits: select a flow, say what should change, review the diff.

We built this because the catalogue was already there for the documentation, and it turned out to be exactly what a model needs to write a correct step on the first try.

Running Where the Systems Are

Sometimes the systems under test are only reachable from a machine you cannot open a port into. A ronsel agent runs there, with a clone of the same context, and both sides connect out to an MQTT broker. Nothing listens on either side.

ronsel --remote agent-ourense --file flows/checkout.md --env uat

What travels is the commit your context is on and the env values the flow needs, encrypted to the agent’s key so the broker never sees them. What comes back is every event of the run as it happens, and the test-run folder written into your own test-runs/ as if it had run locally. The agent’s key is trusted on first sight and refused if it ever changes, the way SSH treats a host key.

Get Started

It needs Node.js 24 or newer, and nothing installed globally:

mkdir e2e && cd e2e
npx ronsel start

That furnishes the folder with example applications and flows, writes a package.json that pins the version, and opens the UI on http://localhost:3001. From then on, npm run ronsel. In a pipeline:

ronsel --file flows/checkout.md --env production
ronsel --view smoke-tests --env production

We built ronsel to solve our own testing and orchestration problems, and it has been in daily use on client projects for a year under its old name. Try it on your next scenario, and tell us what breaks. Contributions, feedback and ideas are welcome.