Skip to main content

Flow Anatomy

A flow is a saved document. Knowing what is inside it makes the editor predictable, explains why some things are reusable and others are not, and tells you what actually moves when you export a flow to another machine.

Purpose

This page describes the parts of a flow and how they relate. Read it before your first non-trivial flow, and again when you start splitting work into Sub Flows.

The parts

Everything on that diagram is stored in the flow. A flow you export carries all of it.

Nodes

A node is one typed step. Every node has:

FieldMeaning
idStable identity — never changes, survives rename
typeWhat kind of node it is (action.click, detect.image, …)
nameThe label you see on the canvas — free text, rename freely
paramsThe node's settings, specific to its type
retryPolicyOptional per-node retry, or none
outputMappingsOptional writes from the node's output into variables
uiCanvas position, colour, collapsed state

The important consequence: id is identity, name is a label. Renaming a node never breaks a connection, and two nodes may share a name.

Nodes do not own their routing. A node reports which port it left through; the flow's edges decide where that leads. This is why you can rewire a flow without touching any node's settings.

Ports

A port is a named exit on a node. Most node types have a fixed set:

  • success, failure, timeout, cancelled, fatal for most nodes
  • true / false for a simple If
  • matched / notMatched for Memory Match
  • solved / unsolved / failure for Captcha

Three node types compute their ports from their own settings instead:

NodePorts depend on
If in Multiple Branches modeone case1…caseN per branch, plus default
If in Text-match modematch / noMatch
Find Image in Branch-by-template modeone port per selected template, plus notFound
Branch (Random Branch)branch1…branchN from the branch count

When you lower a branch count, the trailing ports disappear and the editor prunes the orphaned edges for you.

Required versus optional ports

Some ports must be wired for the flow to validate; others may be left open on purpose.

  • Required — leaving it unwired is a validation error. success on most nodes.
  • Optional (lenient) — leaving it unwired is a legitimate "give up here" choice. The run ends gracefully if it is taken.

Branch is the strict case: every branch port must be connected, because the runtime may select any of them and an unwired branch would randomly dead-end the run.

Edges

An edge connects one node's port to another node's input. Edges carry:

FieldMeaning
fromNodeId + fromPortWhere it leaves
toNodeIdWhere it goes
priorityOrdering when several edges share a port
conditionOptional guard evaluated during resolution

Multiple edges may leave the same port. They are tried in priority order; see Edge resolution.

Regions

A region is a named rectangle stored in percentages of the screen, not pixels.

That percentage storage is the reason a flow built on one resolution keeps working on another. A region that covers the top-right quarter of a 1280×720 screen covers the top-right quarter of a 1920×1080 screen too.

Regions have an id (identity) and a name (label). Nodes bind to the id, so renaming a region never breaks the nodes using it.

Regions can be saved to the Region Library and reused across flows.

Assets — template images

An asset is a captured template image the Find Image node matches against. Each one records:

  • the image file and its checksum,
  • its pixel dimensions,
  • the resolution it was captured from,
  • a quality assessment — entropy, edge density, dominant-colour ratio, and whether it is recommended.

The quality assessment is why the app can warn you that a template is too plain or too cluttered to match reliably.

Template quality is a real setting, not a nicety

A template that is mostly one flat colour has low entropy and will match many places on screen. A template with lots of background will stop matching the moment anything behind it changes. Capture a tight crop of the distinctive part.

Groups

A group is a named frame drawn around part of the canvas, optionally with a note, optionally collapsed.

Groups are pure authoring furniture. They have no ports, no settings, and never execute. Membership is geometric — a group contains whatever cards its frame encloses — so moving a node into a group is just a drag, and no node stores a parent id that could go stale.

A group with no members and only a note is your comment box. There is no separate comment concept to learn.

Settings

Flow settings are grouped into four blocks.

Target

Which emulator this flow expects: preferred instance name, ADB serial, expected resolution and orientation.

Defaults

The fallback values nodes use when they do not specify their own:

SettingRangeWhat it covers
timeoutMs100–120000Default node timeout
pollIntervalMs50–5000Default detection poll interval
waitAfterActionMs0–60000Default settle time after an action
imageThreshold0.5–0.99Default match confidence

Runtime

The guardrails and execution policy:

SettingRangeEffect
maxNodeExecutions1–100000Hard cap on total nodes executed
maxRuntimeDurationMs1000–86400000Hard cap on wall-clock run time
maxConsecutiveFailures1–1000Hard cap on failures in a row
captureFailureScreenshotsbooleanSave a screenshot when a node fails
inputModeadb / nativeHow input is delivered
frameSyncobjectFrame freshness rules (below)

Features

Per-flow feature gates: ocrEnabled, nativeInputEnabled, multiScaleImageDetectionEnabled.

OCR is gated per flow

Read Text will not run in a flow whose ocrEnabled feature gate is off. If a Read Text node fails immediately with no output, check this setting before debugging the region.

Frame synchronisation

Detection has to read a current picture of the screen, not a stale one. The frameSync settings control that:

SettingRangeMeaning
requireFreshFrameAfterInputbooleanAfter sending input, wait for a genuinely new frame
maxFrameAgeMs16–5000How old a frame may be and still count as usable
freshFrameTimeoutMs100–30000How long to wait for that fresh frame
visualStabilityFrames0–10How many consecutive stable frames to require

A frame is usable when now - frame.capturedAt <= maxFrameAgeMs.

This is what stops the classic automation bug where you tap a button and immediately "see" the pre-tap screen.

What identity means for reuse

Because several things are identified by stable ids rather than names, the following are all safe:

  • Renaming a node, a region, or a template.
  • Moving nodes around the canvas.
  • Reordering branches (ports are positional but edges follow).

And the following are not:

  • Deleting a template that a branch-by-template Find Image routes on — its port disappears with it.
  • Lowering a branch count below a wired branch — the trailing edge is pruned.

Best practices

  • Name nodes for intent, numbered in reading order. 1. Find a gem on screen, 2. Swipe it to the centre. The reference automation in this documentation set does exactly this, and it makes a 13-node flow readable at a glance.
  • Use groups to mark phases, then collapse the ones you are not working on.
  • Put reusable rectangles in the Region Library rather than redrawing them.
  • Set flow defaults once instead of setting the same timeout on twenty nodes.
  • Keep one flow to one job. If you cannot describe a flow in one sentence, it wants splitting into Sub Flows.

Performance notes

  • Region-scoped detection is materially faster than whole-screen detection, because the matcher works on a crop. Setting a search region is the single cheapest speed-up available.
  • Template count multiplies matching cost. A multi-template Find Image with 16 templates does roughly 16× the matching work of one template against the same frame.
  • Flow defaults do not cost anything at run time — they are resolved at compile time.

Memory notes

  • Template assets are loaded and cached for matching. A flow with many large templates has a larger working set than one with a few tight crops.
  • Groups, regions and node ui data are authoring-only. They are stored in the flow but never loaded into the runtime's hot path.

Common mistakes

MistakeConsequenceFix
Storing coordinates as pixels in your headFlow breaks on a different resolutionUse regions — they are percentage-based
Capturing a template with backgroundStops matching when the background changesTight crop of the distinctive part
Relying on node names for wiringNo effect — names are labelsWire ports; ids are identity
Leaving ocrEnabled offRead Text does nothingEnable it in flow settings
One giant flowUnreadable, untestableSplit into Sub Flows

Troubleshooting

My flow validates but a branch is greyed out or missing. The port set is computed from settings for If (multi-branch / text-match), Find Image (branch-by-template) and Branch. Check the branch count or template selection.

A template stopped matching after I changed emulator resolution. Template matching is pixel-based. Either re-capture the template, or enable multi-scale detection in the flow's feature gates.

Read Text returns nothing at all. Check features.ocrEnabled in flow settings first.

FAQ

Does renaming a flow break Sub Flow calls to it? No. Calls bind to the flow id; the name is a display cache.

Are groups exported with the flow? Yes. They are part of the flow document.

Can two nodes have the same name? Yes. Names are labels, not identity.

Where do regions live — the flow or the library? Both are possible. A rectangle drawn inline belongs to the flow; saving it to the Region Library makes it reusable across flows.