Abstract
Every modern software application depends on linked user journeys: different people and systems acting across devices, services, and time to achieve a shared result.
Consider a rideshare app
- A rider requests a trip from their phone.
- A dispatch service matches an available driver.
- The driver accepts the request and navigates to the pickup point.
- When the driver arrives and the dispatch service confirms the rider’s presence, the trip begins.
In this system, each participant acts independently, on different devices and timelines, yet their actions depend on one another. But today’s testing tools can’t represent that complexity. They assume a single actor, a single timeline, and a shared execution context. There’s no built-in support for concurrent actors, no way to express dependencies across workflows, and no faithful model of distributed timing.
Run Rules, QA Wolf’s test orchestration system, solves that problem. It lets end-to-end tests mirror how real systems behave: distributed, asynchronous, and multi-actor. Each participant’s behavior is an independent workflow that runs in isolation, coordinated declaratively through a dependency model rather than a control script. Run Rules compiles these declarations into a directed acyclic graph (DAG) that represents causal and concurrent relationships among workflows. It evaluates data dependencies in real time, launches workflows as soon as their inputs resolve, and preserves causal order without shared state or fragile sequencing.
This post explains how Run Rules makes linked user journeys testable at scale and what it took to build a coordination model that faithfully reflects real-world behavior.
Definitions
- Workflow
A sequence of steps representing one actor’s behavior in the system (e.g., “rider requests trip,” “dispatch matches driver,” “driver accepts trip”).
- Actor
A participant in a workflow, such as a rider, driver, or dispatch service (API).
- Directed acyclic graph (DAG)
The structure Run Rules uses to model workflow dependencies. Each node is a workflow; each edge defines a declared relationship (e.g., “
driver_acceptdepends ondispatch_match”).- Coordination
The synchronization of multiple actors that may act sequentially or concurrently. Run Rules supports two primary models:
- Turn-based coordination: One workflow’s outputs unlock another’s (e.g.,
rider_request→dispatch_match→driver_accept). - Real-time coordination: Concurrent workflows synchronize on shared conditions (e.g.,
driver_arrivalandrider_presencebefore ride start).
- Turn-based coordination: One workflow’s outputs unlock another’s (e.g.,
- Run Rules
QA Wolf’s declarative coordination system. Run Rules defines when workflows become eligible to run and what data they consume or produce, then uses those rules to work out the execution order as the run goes.
- Container
A sandboxed runtime provisioned for a single workflow. Containers isolate environments completely (no shared memory, cookies, or session state), the way real-world actors are isolated from one another.
Modeling Linked User Journeys
Many modern software features involve interactions between multiple participants or systems. A rideshare trip is one example:
Step 1 of 8The rider requests a trip from their phone.
Each participant operates in its own environment and interacts with the system at different times. Their actions are related through shared data (trip requests, driver assignments, arrival updates), not through a single session or runtime.
The overall trip emerges from these exchanges: one actor’s output becomes another’s input, and timing determines how the system responds. Linked user journeys like this involve both sequence and overlap. Some actions depend on earlier results, while others occur in parallel.
To represent them accurately, a model must describe the relationships between participants: the flow of data, the timing of actions, and the conditions that connect one step to another.
Procedural Coordination and Its Technical Limitations
Traditional testing frameworks coordinate procedurally, running a fixed sequence of actions: run A, then run B, then run C. That approach works for single-user tests but doesn’t reflect how modern systems behave when several actors operate independently on different devices or timelines.
Frameworks like Playwright and Cypress lack any concept of coordination, so QA engineers have to collapse multi-actor flows into a single control thread to test them. Even when they run through external test runners, there’s no programmatic coordination layer, which forces test authors to pass state imperatively through scripted steps rather than through declared dependencies between workflows. The design (or, really, lack thereof) makes it impossible to represent actions that happen in parallel or depend on data produced elsewhere.
As a result, procedural test code that attempts to validate linked user journeys inevitably creates one or more of the following technical problems:
1. Concurrent execution can’t be represented.
Procedural frameworks control every workflow through one execution thread, so each actor’s actions must run in order. Actors that should operate at the same time, like a rider and a driver, end up running one after another. This single-threaded structure can’t model independently scheduled actors or expose issues that depend on true parallel behavior, such as synchronization errors and timing drift between devices.
2. Tests use static waits, which don’t accurately reflect real system timing.
Procedural tests often rely on fixed sleeps or polling loops to simulate the pauses between actions. These waiting strategies ignore variable network latency, backend processing time, and user response delays, making timing-sensitive issues hard to detect or reproduce.
3. Shared state makes results unreliable.
A single test runner reuses the same memory, session, or device state to act as multiple users. That overlap allows one actor’s data to leak into another’s environment, producing nondeterministic results and making test failures difficult to isolate.
4. Workflow relationships are buried in code.
Rules about which workflow depends on which output are embedded inside procedural logic. When the product changes, QA engineers must trace those dependencies manually through the test code, increasing maintenance time and the risk of error.
5. Failures trigger unnecessary full-suite re-runs.
When one workflow fails, procedural suites must restart the entire sequence from the beginning. This re-runs workflows that passed, rebuilds state that was still valid, and wastes runtime resources.
6. Causal relationships can’t be observed.
Procedural tests log events in order but don’t record the data dependencies between them. When something breaks, QA engineers can see what happened but not why. They can’t see, for example, which workflow was waiting on which input, or what condition allowed the next step to run. Without that causal context, debugging distributed workflows becomes slow and error-prone.
Coordinating Workflows Declaratively
In distributed systems, coordination naturally follows the data: processes run when the information they depend on becomes available. Run Rules brings that same approach to testing. Instead of scripting when each workflow should run, QA engineers describe what data each workflow produces and what data each one needs. With that information, Run Rules works out the execution order and starts each workflow the moment its inputs arrive.
This model rests on the ephemeral suite-scoped variable space. Together, that space and the Run Rules govern how workflows discover dependencies, become eligible to run, and synchronize, whether turn by turn or in real time.
Ephemeral suite-scoped variables: the foundation of Run Rules
Run Rules coordinates workflows through a shared, ephemeral suite-scoped variable space, which is created when a run begins and discarded when it ends. Workflows read and write values in this space with two custom methods injected into the runtime environment:
setOutput()is used by producer workflows to create a key-value pair in the suite scope.getWorkflowInput()is used by consumers to read from key-value pairs in the suite scope.
This way, workflows never share memory, sessions, or runtime state.
This model eliminates global fixtures, cross-workflow setup code, and implicit state. Each run receives its own isolated namespace, preventing data from leaking across runs or retry attempts. Because the variable space lives outside any individual test runner, workflows can execute in parallel across machines, devices, or environments while still coordinating through declared data.
Values published with setOutput() appear in the variable space immediately. These values become synchronization points for other workflows, replacing brittle timing logic and hard-coded orchestration. A workflow runs when the data it needs is available, not when another workflow finishes.
This minimal abstraction—publish a value, consume a value—is the foundation of all coordination in Run Rules.
Run Rules: analysis at setup and eligibility at runtime
Run Rules operates at two levels: provisioning-time dependency analysis and runtime eligibility enforcement.
Provisioning-time dependency analysis
Before a run begins, Run Rules analyzes every workflow and extracts its calls to getWorkflowInput(). From these calls, Run Rules builds each workflow’s concrete dependency set, or its needs. It then assembles those dependencies into a directed graph and validates the graph, catching cycles with no seed values, missing producers, and other invalid structures before any workflow executes.
Runtime eligibility enforcement
At runtime, Run Rules enforces dependencies dynamically as workflows execute. Each run begins with an empty suite-scoped variable space. Workflows start immediately in their isolated environments and run normally until they reach a declared dependency via getWorkflowInput(). If the required variable already exists, execution continues; if not, the workflow pauses at that point. There’s no polling loop or custom waiting logic. Run Rules resumes the workflow automatically when the variable becomes available.
Producers run before consumers only when a test author declares a dependency between them. Run Rules does not infer ordering from data alone; it enforces the relationships expressed through getWorkflowInput(). Once a workflow declares that it needs a value from another workflow, Run Rules holds that workflow at that point until the value is published.
If a workflow fails, only that workflow is retried. Outputs from successful workflows persist across attempts, allowing retries to reuse upstream values without re-running the entire suite. Each retry sees the same inputs as the original attempt, preserving the intended causal sequence.
Run Rules therefore provides deterministic, data-driven coordination. Workflows advance exactly when their declared dependencies allow, never earlier or out of order, and never through orchestration code embedded in test logic.
Coordination patterns supported by Run Rules
Run Rules supports both turn-based and real-time coordination.
Turn-based coordination
A downstream workflow depends on values produced by an upstream workflow. Run Rules enforces this ordering automatically: the downstream workflow cannot begin until the upstream workflow’s outputs exist. This models sequential behavior, such as “the rider requests a trip before the driver sees it,” without explicit waits or control flow.
Real-time coordination
Independent workflows can run concurrently but synchronize on specific conditions. A workflow becomes eligible the moment its required variables appear, even while other workflows are still executing. The result is true parallelism, where actors proceed independently but meet on shared conditions such as presence, proximity, or matched IDs.
Turn-based and real-time coordination need no separate logic: both emerge from the same eligibility rules applied to the ephemeral variable space.
Observability through declarative coordination
Because dependencies are explicit and workflows run in isolation, Run Rules can reconstruct the causal execution sequence. Each workflow records its own logs, traces, and artifacts. Run Rules correlates these per-workflow timelines with the variable-space events to produce a suite-level causal trace: which workflow waited on which inputs, when each dependency appeared, and how failures propagated.
This makes multi-workflow behavior observable, debuggable, and explainable, all of which procedural orchestration tends to make harder.
Why it matters
Declarative coordination shifts responsibility from test authors to Run Rules:
- True parallel execution reveals real concurrency issues: Workflows operate independently and start as soon as their inputs exist, with no sequential bottlenecks and no shared timeline.
- Isolation ensures deterministic behavior at any scale: Each workflow runs in its own container with no shared session or state. The same suite produces the same result regardless of the level of parallelism.
- Targeted retries preserve causal integrity and save time: When a workflow fails, Run Rules retries only the node that failed, reusing upstream outputs, so there are no full-suite re-runs and no corrupted state.
- Declarative relationships make coordination transparent: Dependencies are visible in the generated graph, not hidden in orchestrated test code. Adding or modifying a workflow requires updating its declarations, not rewriting control flow.
- Causal traces expose how and why failures occurred: Engineers can inspect the full chain of events, down to which workflow produced which value, when dependencies were resolved, and what triggered each start.
Run Rules models coordination the way it works in real systems, where independent actors respond to changing data and timing emerges from propagation and constraints. This extends end-to-end testing beyond linear paths into distributed, data-driven scenarios that match the systems teams build and depend on today.
The Impact of Coordination-Aware Testing
Run Rules expands what automated testing can verify. By coordinating isolated workflows across users, devices, and systems under test, it changes how QA engineers test linked user journeys: from procedural sequences to explicit, reusable components built into the system and supported by the testing framework. They can now represent real behavior—where one actor begins a flow, another joins later, and both operate under different timing and platform conditions—without flattening those interactions into a single script.
Because coordination is declared rather than scripted, suites become adaptive and resilient. When a workflow fails, Run Rules retries only the affected node, reusing valid upstream outputs. When dependencies are unmet, it holds related workflows instead of re-running the entire suite. That makes coverage faster to iterate on and failures easier to isolate.
The same declarative structure makes deep observability possible. Every workflow maintains its own timeline, while Run Rules reconstructs a causal trace showing how dependencies are resolved over time, across roles, and across platforms. Engineers can trace cause and effect directly, so they see why something failed and in what context, not just what failed.
Patent Pending
Patent applications have been filed covering aspects of the technology described herein.
Ownership: QA Wolf, Inc.
Inventors:
- Brendan Downing
- Eric Dobbertin
- Eric Eidelberg
- Erkan Erol
- Jaden Lemmon
- Jon Perl
- Michael Price
- Nishant Shukla