Skip to content
Menu

How it works

Check a plan

Run qaren check against the working tree, read the receipt and find the run's evidence.

qaren check walks a plan on a borrowed device against the app's working tree. Run it from the app's directory, next to .qaren/config.yaml:

sh
qaren check --plan-file plan.md --json
qaren check --plan-file plan.md --device <simulator-UUID> --json
qaren check --platform android --plan-file plan.md --device emulator-5554 --json

The configuration needs at least appId and devClientScheme. Optional deadlines in that file set the preparation budgets check and pr store on the run record.

What a run does

text
plan preflight -> device selection -> lease + durable run record
               -> app preparation -> screen proof -> walk -> teardown

The plan is parsed first, so an unparseable plan or a missing Jev key refuses before any device is touched. The run then leases the device, prepares the app and its own Metro, walks the plan and cleans up owned resources. By default, the simulator or emulator stays running and the installed app stays. Failed iOS checks can hold resources for inspection; see Leases, cancellation and cleanup. See CLI-owned iOS build routes for iOS startup ordering.

Choosing the device

iOS

check borrows the only booted simulator, or the booted simulator named by --device <simulator-UUID>. With no simulator booted, or more than one booted and no --device, it refuses DEVICE_UNAVAILABLE.

For explicit boot, restart or exit shutdown, follow the iOS simulator lifecycle contract.

Android

--platform android borrows one booted emulator through the adb server that is already running. qaren never starts or stops the adb server; with none answering, or with an adb under ANDROID_HOME whose version differs from the server's, it refuses DEVICE_UNAVAILABLE.

Automatic selection counts only emulators that have finished booting. Exactly one must qualify; with several, pass --device emulator-NNNN. Physical devices are not supported.

The build runs Gradle :app:assembleDebug for the emulator's ABI, after expo prebuild --platform android --clean when android/ is absent. An absent android/ must be git-ignored; otherwise the build fails BUILD_FAILED before generating it.

Flags

FlagMeaning
--plan-file <plan.md>The plan to walk. Required.
--platform ios|androidDefaults to ios.
--device <id>A simulator UUID on iOS, an emulator serial on Android.
--config <path>Another configuration file. Defaults to .qaren/config.yaml.
--fresh-installSee the fresh-install contract.
--hold, --no-holdCheck-only inspection controls; see the iOS admission and cleanup contract.
--boot-device, --restart-device, --shutdown-device-on-exitSee the iOS simulator lifecycle contract.
--jsonAccepted for scripts; the receipt is always JSON on stdout.

The receipt

Every run prints one qaren/1 JSON receipt on stdout and a one-line summary on stderr. The fields you read most:

FieldMeaning
resultpass, fail, failed or refused
run_idThe run's ID; artifact paths are in the receipt
failure.codeThe typed failure or refusal code
failure.detailWhat happened
next_actionWhat to do next
failure.screenLast-observed controls for a target-class failure; see the screen evidence contract for states, primary selection, bounds, privacy and age
failure.suggestionSuggested plan edit; see the screen evidence contract for its relationship to next_action
failure.repeatEarlier matching failure; matching and notice rules follow the screen evidence contract
artifacts.reportThe path of report.md
ledgerThe verdict, the walk path, step and Jev counts, recoveries
ledger.blocksCounts of replayed, patched and discovered blocks reached by the run
blocks_writtenBlocks saved under .qaren/actions/
blocks_not_savedBlocks that passed but were not saved, each with a value-free reason
recipeWhether the plan was saved, flagged or cleared under .qaren/plans/; see saved plans
worktree_driftApp files whose git status changed during the walk; never changes the verdict

See Troubleshooting for the common codes.

The local report and PR comment show how many blocks were replayed; see the saved-block contract.

For interpreting app-derived failure evidence, follow the app-derived failure text contract.

Exit codes

ExitMeaning
0pass
1fail: the app or the plan failed, or the run failed
2Usage error: help on stderr, nothing on stdout
3unknown
4refused: a typed refusal, such as a busy device or a missing Jev key

The run directory

The run storage contract determines the directory. The receipt names the artifacts that exist.

PathContents
run.jsonThe durable run record
ledger.jsonEvery walked step with its outcome, timing and evidence
report.mdThe human-readable report; app text follows the failure text contract
logs/events.jsonlThe local event stream that qaren watch reads; see the run identity and privacy contract
logs/core.logThe screen child's private log
logs/core-diagnostics.jsonlPrivate media-withheld diagnostics, target-miss diagnostics, runtime-input diagnostics and admission recovery timings, when emitted

The tested candidate

check records the commit, the lockfile hash and a fingerprint of the working tree before the walk and checks them again after it. A change in between fails the run with CANDIDATE_DRIFTED, which can never be a pass. See the candidate provenance contract for excluded paths and untracked Watchman cookies.

Leases, cancellation and cleanup

One run holds a device at a time within the configured lock namespace, which owns root selection, the machine-wide default and its opt-out, switching and recovery requirements. A second run on the same device in that namespace refuses DEVICE_BUSY; a lease whose holder has died is reclaimed by running that run's own cleanup.

A signal, or the calling process going away, ends the run as RUN_CANCELLED after normal teardown. When teardown cannot prove a resource is gone, the lease stays and the receipt says so: run qaren cleanup <run-id>. Never delete lock files to get around it.

Failed iOS checks can keep the reached state for inspection. See the authoritative iOS admission and cleanup contract for eligibility, --hold / --no-hold, environment overrides, receipt fields and releasing a hold.

Time budgets

QAREN_WALK_SECONDS bounds the whole walk and QAREN_STEP_SECONDS bounds one step. Both take positive whole seconds and default to 1200.

Edit this page on GitHub