skills/using-maestro/SKILL.md
Use when writing, debugging, or organizing Maestro mobile E2E test flows for Android/iOS apps (especially React Native). Triggers include selector failures, flaky flows, idempotency problems, screenshot output management, AI agent integration via Maestro MCP, and CI setup decisions.
npx skillsauth add toongri/oh-my-toong-playground using-maestroInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Maestro is a YAML-driven mobile E2E framework. The four non-negotiable principles:
clearState plus an isE2E argument the app respects.extendedWaitUntil for any post-launch state.~/.config/maestro/<id>/config.yaml (or the MAESTRO_USING_FLOW_DIR env var) before every operation. If the entry is missing, interview the user — never assume .maestro/. See references/flow-location-config.md.Get those four right and Maestro stays reliable. Get them wrong and the suite is brittle.
This skill is the entry point. Reach for the topic-specific reference under references/ when implementing or debugging.
flow.yaml or modifying an existing oneassertVisible / tapOn cannot find an element you can clearly see on screen.maestro/ directory layout or wiring CI artifact uploadassertScreenshot or managing screenshot outputreferences/selectors-and-determinism.md for the priority table and trade-offs.launchApp clearState: true plus an isE2E argument the app respects. This is what makes the flow idempotent across N runs.extendedWaitUntil for any state that may exceed assertVisible's default 7-second retry window — typical triggers are JS bundle load, network fetch, or animation. Within 7s, assertVisible already auto-retries; reach for extendedWaitUntil only when expected latency exceeds that.~/.config/maestro/<id>/config.yaml (or MAESTRO_USING_FLOW_DIR env var). If absent, the skill interviews the user: project root .maestro/ (committed, team-shared) vs ~/.maestro/projects/<id>/flows/ (per-user, worktree-shared) vs custom path. See references/flow-location-config.md. Inside the resolved flow_dir, organize as <flow_dir>/<feature>/ with <flow_dir>/common/ for subflows — see references/flow-organization.md.takeScreenshot outputs are transient (gitignore in internal mode, ephemeral in external mode); assertScreenshot baselines are permanent. Both modes store baselines in <flow_dir>/screenshots/. Internal mode commits them to git; external mode keeps <flow_dir>/screenshots/ outside the repo and requires its own backup discipline. See references/storage-and-screenshots.md.maestro test. See references/ai-agent-integration.md.# Install
brew install maestro
# or: curl -Ls "https://get.maestro.mobile.dev" | bash
# Resolve flow_dir for this project (interview happens on first run if absent)
flow_dir=$(bash <skill>/scripts/resolve-flow-dir.sh) || {
# Illustrative — exit 1 here is a placeholder. In practice, the calling
# agent must inspect $? and dispatch the interview when the resolver exited
# with code 2 (REGISTER_REQUIRED), then re-run the resolver.
# See references/flow-location-config.md for the interview contract.
exit 1
}
# Layout (replace `auth` with your feature name; idempotent across modes)
mkdir -p "$flow_dir/common" "$flow_dir/auth" "$flow_dir/screenshots"
# First flow (rename LoginSmoke to your flow name)
maestro test --test-output-dir=./maestro-output "$flow_dir/auth/LoginSmoke.yaml"
# Whole suite
maestro test --test-output-dir=./maestro-output "$flow_dir"
# CI (bypass interview via env var)
export MAESTRO_USING_FLOW_DIR=.maestro
maestro test --test-output-dir=./maestro-output "$MAESTRO_USING_FLOW_DIR"
When wrapping resolve-flow-dir.sh in your own automation, branch on the exit code:
| Exit code | Meaning | Caller action |
|---|---|---|
| 0 | Resolved — flow_dir on stdout | Use the value |
| 1 | Hard error (parse failure, empty config value) | Fail fast; surface stderr to the user |
| 2 | REGISTER_REQUIRED:<id>:<root>[:<MARKER>] on stderr | Dispatch the interview (see references/flow-location-config.md), write ~/.config/maestro/<id>/config.yaml, re-invoke the resolver |
Markers on exit 2: bare = first-time registration; :COLLISION = same project_id mapped to a different repo; :INVALID_SLUG = derived ID needs a manual override.
For local debugging, always pin --test-output-dir (or pass an explicit path: to takeScreenshot). Without those, output location varies — Maestro docs describe workspace-relative behavior while user reports show CWD-bound behavior in some versions. Pinning removes the ambiguity.
references/flow-location-config.md — Per-project flow location resolution. The ~/.config/maestro/<id>/config.yaml schema, the resolution algorithm, the interview script, the MAESTRO_USING_FLOW_DIR CI escape hatch.references/scenario-design.md — What to test: scenario ideation heuristics (risk matrix, single intent), ISTQB-classic edge case techniques applied to mobile, kiosk/fixed-screen scenario categories (time-based, peripheral, external trigger, soak, config change, error recovery, security, accessibility), scenario→flow mapping patterns.references/flow-organization.md — Inside the resolved flow_dir: directory structure, subflow reuse via runFlow, lifecycle and maintenance, data-driven repetition.references/selectors-and-determinism.md — Selector priority table, the five determinism guardrails, idempotency, common selector mistakes.references/storage-and-screenshots.md — takeScreenshot vs assertScreenshot policy, --test-output-dir for CI, .gitignore patterns, visual regression strategy.references/ai-agent-integration.md — Maestro MCP tools, MCP vs CLI decision matrix by stage and environment, open issues that affect kiosks and WiFi ADB.references/test-isolation-and-reset.md — Why every scenario must reset (it is the industry standard), what clearState actually clears on Android vs iOS, legitimate exceptions (kiosk, hardware bring-up, large seed data), Keychain caveat.references/pitfalls-and-cheat-sheet.md — Full pitfall table, CLI cheat sheet, flow YAML keyword cheat sheet, debug bundle layout, onboarding checklist.references/test-environment.md — Category A (software SUT — emulator default) vs Category B (hardware-integrated SUT — physical IS the SUT). Capability-driven Limits table, Maestro iOS = simulator-centric, and the decision rule.~/.maestro/tests/<timestamp>/ is the teacher.clearState as optional. Without it, the second run drifts from the first.maestro test from the repo root. PNGs scatter into the repo — pin --test-output-dir or cd "$flow_dir" first..maestro/ in scripts when the project may be registered to use an external flow_dir. Always resolve via scripts/resolve-flow-dir.sh (or the MAESTRO_USING_FLOW_DIR env var in CI).references/test-environment.md.When you observe one of these, stop adding workarounds and re-read the relevant reference. The symptom is a violation of one of the four pillars, not a flake to retry.
clearState missing, or the app ignores isE2E. See references/test-isolation-and-reset.md.extendedWaitUntil, or Metro still attached. See references/selectors-and-determinism.md.assertVisible fails" → text rendered inside <Image>/SVG, or assertion fired before JS bundle finished. Use id selector or extendedWaitUntil.testID instead.clearState to save two seconds" → flake debugging will eat thirty minutes within a week.mkdir .maestro/ and start writing" → the project may already be registered to use an external flow_dir. Resolve first; create only the directory the config points at.| Rationalization | Reality |
|---|---|
| "Just this one flow can use coordinates" | Once accepted, the next five flows copy the pattern. testID is a ten-second app change. |
| "I will add clearState after the happy path works" | A flow without clearState works once. The second run does not. You cannot learn idempotency from a single passing run. |
| "assertVisible is fine — the element is clearly there" | assertVisible only auto-retries ~7s; cold start, bundle load, and animations can easily exceed that. Use extendedWaitUntil for any post-launch assertion. |
| "I'll keep flows in a personal folder, commit later" | Uncommitted flows have no PR review, no CI execution, no reproducibility. The merge cost grows daily. |
| "MCP can author and run — why bother with CLI in CI?" | LLM calls in CI = non-deterministic budget plus rate-limit risk. Author with MCP, execute with CLI. |
| "takeScreenshot and assertScreenshot are the same — both produce PNGs" | Different lifetimes and storage policies. Mixing them either pollutes git or silently disables visual regression. |
| "The flow is flaky, let me add --retry" | Retry hides the cause. Walk the fix order in selectors-and-determinism.md first. |
| "I'll skip the resolver and just use .maestro/ like before" | The skill stores the per-project decision in ~/.config/maestro/<id>/config.yaml. Skipping the resolver creates a divergent committed copy when the project actually runs in external mode — silent breakage on CI or in another worktree. |
This skill captures generic Maestro patterns. Project-specific things — kiosk-bound coordinates, i18n keys for one app, pairing rituals, build/release commands — belong in the project's own knowledge base or .maestro/README.md.
Decision rule: if the answer changes when the app or device changes, it is project-specific. If it would apply to any Maestro user on any app, it belongs here.
tools
Use at the end of a work session to review the WHOLE session and record entities worth pinning. This is the manual, deliberate complete-sweep review — NOT an automated nudge. Triggers on "wrap up", "wrap-up", "session wrap", "end of session", "what should I pin".
documentation
Use when initializing the pins knowledge graph for the first time in a project. Guides the user through creating pins.yaml (the storage manifest). Triggers on "setup pins", "initialize pins", "create pins.yaml", "first-run pins".
testing
Use when you need to record a single pin entity to the knowledge graph. Invokes lib/pins record() to validate and write a canonical .md file. Triggers on "record pin", "pin this", "save this as a pin".
databases
Use when looking up pins by type, tags, or source. Drives lib/pins/query.ts to retrieve matching pin entries from the knowledge graph. Supersedes the legacy manual ls+frontmatter procedure.