How it works
Saved blocks
How a passing block is saved, replayed on the next run, and patched when the app changes.
Your agent runs the plan; QaReN saves each passing block and replays it on the next run. Here is what it means.
A block that passes is saved, unless one of the conditions below applies, so the next run can replay it by the exact identities it used instead of resolving each target again.
Where blocks live
A passing block eligible under the persistence conditions below
is written to .qaren/actions/<slug>.yaml in the app directory, the one that
holds .qaren/config.yaml. The slug is the block's ID from its heading; see
Write a plan. Very long slugs are shortened with a hash
suffix.
The file is a Maestro-shaped action: each plan line as a comment, followed by the commands
with the exact testID the step used, or its label when it had no testID. The walk replays
the file by those stored identities; the rn-flow/1 dialect is the
separate compile contract for saved actions.
Replay, then walk
On the next run, a block whose plan lines, platform, app and runtime input policy are unchanged is replayed by its stored identities. For Jev requirements, including the optional interruption safety veto, see the plan readiness contract.
Start declarations retain their plan target on replay; see the authoritative saved-block contract for start waits and refusal behavior.
| Situation | Outcome |
|---|---|
| A stored identity resolves | The step replays |
| A stored identity no longer resolves, before the step changed the app | The block is walked again from that line; earlier steps stay done |
| A stored identity is ambiguous | TARGET_AMBIGUOUS, without walking again |
| A check fails after any eligible recovery | FAIL; the block is never walked again or rewritten |
When a re-walked block passes, only the commands from the re-walked line on are rewritten,
and every ✓ comment stays byte-identical. A failing block is never saved; earlier passing
blocks remain subject to the persistence conditions below.
The ledger's path is walk, replay or replay→walk@<line>, and each block reports its
source as discovered, replayed or patched. The receipt lists blocks_written.
When a block is not saved
A block that passed is left unsaved, and blocks_not_saved gives the reason without naming
any value, when:
- a step had neither a
testIDnor a label to store; - it has a phrase wait;
- a fill meets the private-input or keyboard-fallback persistence restrictions;
- a protected value from the plan or the run appears in the block, which reports
contains a protected plan-typed value; - a quoted wait or scroll target has no unique
testIDor unique visible text; - the React coverage contract withholds persistence for the block or walk.
For symbolic runtime-input slots, tier encoding, run-wide policy hashes and refreshing
policy-stale login blocks, follow the CLI guide's
runtime-input persistence contract.
An Expect input NAME on screen line is saved as its plan line and a # qaren: echo NAME
marker. Older qaren versions cannot replay such a block.
An edited or incompatible block file is treated as new and walked again. Existing saved blocks are never removed.
Inspecting blocks
qaren actions list [--json]
qaren actions show <slug>
Both read the .qaren/actions/ directory of the current directory. .yaml and .yml files
are both read; a slug with both extensions is refused. These inspection commands accept the
verified main-copy link for Git worktrees and refuse other corpus links and symlinked action
files; see the authoritative
action corpus contract.
Login recovery
For recovery triggers, ordering, default permission decline, optional app interruptions, and terminal failures, see the authoritative step recovery contract. Login recovery uses a saved sign-in block when its configured marker is visible. Follow the reusable sign-in worked example to save, configure and replay it.
Configure the login block in .qaren/config.yaml:
loginBlock: sign-in
loginMarker:
id: login-screen
loginBlock names a saved block, .qaren/actions/sign-in.yaml here, saved for this app and
platform. loginMarker has exactly one of id (a testID) or text (a label). The two keys
are set together. A missing or incompatible login block fails when login recovery is needed.
qaren reads the login block before it selects a device, so the runtime inputs it names are
required for every run. qaren pr reads it at the pull request head and refuses a symlinked
or unreadable .qaren/actions there. That block may only use inputs your plan or your
checked-out login block already names; any other name refuses PLAN_INPUT_MISSING.
Its effective sensitivity must also match for every required input, including the strict
default, so a pull request cannot downgrade an owner's secret slot.
For development error overlays, follow the
step recovery contract,
including its login replay limits.
On iOS, a change of the app's process between captures, such as a crash or restart, fails the
walk with APP_PROCESS_CHANGED.