Skip to content

Add Maestro E2E suite for Android Chrome (physical device)#657

Open
theDavidCoen wants to merge 7 commits into
arkade-os:masterfrom
theDavidCoen:add-maestro-e2e-upstream
Open

Add Maestro E2E suite for Android Chrome (physical device)#657
theDavidCoen wants to merge 7 commits into
arkade-os:masterfrom
theDavidCoen:add-maestro-e2e-upstream

Conversation

@theDavidCoen

@theDavidCoen theDavidCoen commented Jun 10, 2026

Copy link
Copy Markdown

Summary

Adds a Maestro-based E2E test suite under maestro/ for running Arkade Wallet on a physical Android device via Google Chrome.

This complements existing Playwright tests (src/test/e2e/) with real mobile Chrome interaction on hardware — useful for PWA layout, touch targets, Chrome permissions, and device-specific viewport behavior.

Includes:

  • Complete Tests (maestro/complete-tests/) — flows 01–15 covering onboarding, smoke, unlock, settings, and wallet reset
  • Reference flow (maestro/flows/01_startup_restore.yaml) — baseline recording pattern
  • Runner scriptsrun.sh, studio.sh, run-scenario.sh (smoke, onboarding, prerelease, etc.)
  • Tap recording pipelinerecord-taps.sh + parse-taps.py
  • Device adaptationadapt-device.py + device-profiles.yaml to remap coordinates across resolutions/navigation modes
  • Documentationmaestro/README.md with setup, reference device specs, and troubleshooting

Design choices:

  • Coordinate-based taps (integer %) recorded from real device input — no evalScript
  • appId: com.android.chrome + -p android — taps on phone, not desktop Chromium
  • openLink subflow opens WALLET_URL before each run
  • Local config.yaml gitignored; config.yaml.example committed

Reference device (coordinates calibrated for):

  • Xiaomi 13T Pro, 1220×2712, Android 16, 3-button navigation bar enabled

Test plan

  • cp maestro/config.yaml.example maestro/config.yaml and set WALLET_URL
  • Connect Android device with USB debugging (adb devices)
  • ./maestro/scripts/run-scenario.sh onboarding — flows 01 + 02 pass
  • ./maestro/scripts/run-scenario.sh smoke — flows 03–12 pass (wallet on home, unlocked)
  • ./maestro/studio.sh — Studio targets physical device, not desktop Chromium
  • (Optional) ./maestro/scripts/adapt-device.sh --detect on another device
  • (Optional) ./maestro/scripts/adapt-device.sh --to-profile <profile> --dry-run

Notes for reviewers

  • Requires Maestro CLI and adb — not wired into CI yet
  • PyYAML needed only for adapt-device.py (pip install pyyaml)
  • Flow 15 resets wallet state; settings and prerelease scenarios are destructive
  • Test 13 uses a fixed unlock password defined in the flow file

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Full Android end-to-end test suite (15 flows + smoke suites) for onboarding, restore, send/receive, settings, and reset scenarios.
    • Device tap recording/playback, device-profile adaptation, and per-run reporting with Markdown + JUnit and screenshots.
    • CLI entrypoints to run, studio, and run scenario workflows.
  • Documentation

    • Expanded README and reporting guides with setup, secret injection, and recording/playback workflows.
  • Chores

    • Added test fixtures, timing/wait helpers, and automation utilities.

theDavidCoen and others added 2 commits June 10, 2026 11:17
Coordinate-based flows for onboarding, smoke, and settings scenarios; config via gitignored config.yaml from config.yaml.example. Recorded on Xiaomi 13T Pro (1220×2712, Android 16, 3-button nav).

Co-authored-by: Cursor <cursoragent@cursor.com>
…iles.

Viewport and chrome-zone mapping from device-profiles.yaml; detect and init profiles via adb. Document workflow in maestro/README.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR establishes a comprehensive Maestro Android E2E test infrastructure for Arkade Wallet. It introduces local configuration, tap recording utilities, automated report generation, device profile adaptation, foundational synchronization primitives, reusable interaction subflows, and a complete catalog of 15 E2E test flows with documented secret management and scenario orchestration.

Changes

Maestro Android E2E Suite

Layer / File(s) Summary
Documentation and configuration baseline
maestro/.gitignore, maestro/README.md, maestro/REPORTING.md, maestro/complete-tests/README.md, maestro/config.yaml.example
README documents configuration workflow, secret injection approach, and manual tap recording; REPORTING.md explains pass/fail output; .gitignore ignores local config, secrets, debug/reports directories, and recordings; config.yaml.example provides template with env variables and flow discovery settings.
Tap recording and report generation tools
maestro/record-taps.sh, maestro/scripts/parse-taps.py, maestro/scripts/build-report.py, maestro/scripts/open-report.sh
record-taps.sh captures touch events via adb getevent; parse-taps.py converts raw log to Maestro tap coordinates and suggested wait steps; build-report.py generates timestamped Markdown reports from JUnit/debug artifacts with metadata, failed/passed test tables, and screenshot links; open-report.sh launches reports in available desktop viewers.
Configuration and device resolution helpers
maestro/scripts/maestro-config.sh, maestro/scripts/maestro-device.sh
maestro-config.sh reads/exports WALLET_URL/NSEC/PASSWORD from config.yaml with validation guards; maestro-device.sh resolves Android device via ADB_DEVICE env or first connected adb device.
Device profile detection and coordinate mapping
maestro/device-profiles.yaml, maestro/scripts/adapt-device.py, maestro/scripts/adapt-device.sh
device-profiles.yaml defines screen dimensions, navigation type, chrome/viewport zones for xiaomi-13t-pro and example gesture profiles; adapt-device.py detects connected device characteristics via wm size/navigation_mode and remaps percentage coordinates in YAML flow files between profiles; adapt-device.sh is Bash wrapper.
Scenario execution and secret sync orchestration
maestro/run.sh, maestro/scripts/run-scenario.sh, maestro/studio.sh, maestro/scripts/sync-secret-subflows.py
run.sh is main CLI entrypoint: loads config, validates WALLET_URL, syncs secrets, resolves device, and execs maestro test with env args. run-scenario.sh orchestrates specific scenarios (smoke/onboarding/reset/unlock/settings/prerelease/single), generates timestamped debug/JUnit/report outputs, runs maestro test, and invokes build-report.py. studio.sh launches Maestro studio with device and wallet URL. sync-secret-subflows.py reads WALLET_NSEC/PASSWORD from config.yaml, validates NSEC, and generates gitignored input_wallet*.yaml subflows for secret injection.
Base animation wait subflows
maestro/complete-tests/subflows/wait_1s.yaml, wait_2s.yaml, wait_3s.yaml, wait_4s.yaml, wait_5s.yaml
Foundational synchronization subflows that wait 1–5 seconds for on-screen animations to complete via waitForAnimationToEnd, used throughout flows for UI stabilization.
Wallet URL entry and home-screen bootstrap
maestro/complete-tests/subflows/open_wallet_url.yaml, maestro/complete-tests/subflows/ensure_on_wallet_home.yaml
open_wallet_url.yaml opens WALLET_URL in Chrome and waits 3 seconds. ensure_on_wallet_home.yaml orchestrates wallet entry: opens URL, scrolls to top, dismisses "Continue anyway", unlocks wallet (with password injection), handles "Welcome to Arkade" creation flow, conditionally resets home when Receive not visible, and stabilizes with waits.
Navigation reset and assertion subflows
maestro/complete-tests/subflows/reset_to_wallet_home.yaml, maestro/complete-tests/subflows/assert_*.yaml, maestro/complete-tests/subflows/*_home.yaml, maestro/complete-tests/subflows/scroll_*.yaml
reset_to_wallet_home.yaml conditionally navigates backward across DFX/copy/swaps/settings/activity states to reset to wallet home. Assert subflows validate states (on_home, receive_screen, send_form, quick_actions). Scroll subflows handle settings list scrolling and home-to-top positioning.
Atomic tap, keyboard, and scroll recordings
maestro/complete-tests/subflows/tap_*.yaml, maestro/complete-tests/subflows/keyboard_*.yaml, maestro/complete-tests/subflows/scroll_settings_*.yaml
Recorded interaction subflows: tap_header_* (activity/back/settings), tap_home_* (receive/send/swap/scan), tap_receive_* (QR/copy/back/add-amount), tap_send_* (recipient/amount/paste/scan/back), tap_settings_* (back/menu rows by y%/subs), tap_welcome_create_wallet. Keyboard subflows: backspace_x4, enter_sats, tap_100/500/1000. Scroll subflows: settings_down_1/2/3 with recorded swipe gestures.
Composite multi-step interaction subflows
maestro/complete-tests/subflows/receive_add_amount_keyboard.yaml, maestro/complete-tests/subflows/receive_copy_share_sheet.yaml, maestro/complete-tests/subflows/exercise_send_form.yaml, maestro/complete-tests/subflows/wait_receive_ready.yaml, maestro/complete-tests/subflows/wait_wallet_ready_after_create.yaml, maestro/complete-tests/subflows/open_receive_from_home.yaml, maestro/complete-tests/subflows/open_send_from_home.yaml, maestro/complete-tests/subflows/open_settings_from_home.yaml
High-level subflows combining taps/waits/permission handling: receive_add_amount_keyboard chains taps + waits for amount entry UI. receive_copy_share_sheet orchestrates copy/sheet interactions with dismissal. exercise_send_form dismisses clipboard permission and taps recipient/amount/send coordinates. wait_receive_ready and wait_wallet_ready_after_create use extendedWaitUntil or conditional taps for readiness. open_*_from_home wires preconditions and navigation for receive/send/settings entry.
Complete E2E test flows and suites
maestro/complete-tests/flows/01_fresh_wallet.yaml through 15_settings_deep_reset_wallet.yaml, maestro/complete-tests/flows/_run_all_smoke.yaml, maestro/complete-tests/flows/_run_reset_then_create.yaml
15 complete flows: 01 fresh-wallet onboarding, 02 startup-restore with NSEC injection, 03–14 home/receive/send/unlock/settings/activity smoke tests, 15 deep settings reset. Flows orchestrate recorded subflows with tapped coordinates and waits. Smoke suite chains flows with home resets between. Reset-restore suite chains reset (flow 15) + wait 4s + restore (flow 02).
Secret injection and example templates
maestro/complete-tests/subflows/_input_wallet_nsec.yaml.example, maestro/scripts/sync-secret-subflows.py
_input_wallet_nsec.yaml.example templates the generated YAML structure with placeholder NSEC. sync-secret-subflows.py reads WALLET_NSEC/PASSWORD from config.yaml, validates NSEC is not empty/placeholder, and generates gitignored _input_wallet_nsec.yaml and _input_wallet_password.yaml containing double-quoted secret values for injection into flows.

🎯 4 (Complex) | ⏱️ ~60 minutes

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@arkanaai arkanaai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Code Review — #657

Scope: Purely additive — 88 new files under maestro/, zero modifications to existing code. No CI/workflow changes. No protocol-critical code (no VTXO, signing, forfeit, round, or exit path changes). Low integration risk.

Shell scripts and Python code are well-written overall — set -euo pipefail everywhere, proper variable quoting, no shell=True in subprocess calls, yaml.safe_load used correctly. Good work there.

Requesting changes for two issues:


🔴 CRITICAL — Hardcoded nsec private key committed to source

maestro/complete-tests/flows/02_startup_restore.yaml and maestro/flows/01_startup_restore.yaml:

- inputText: "nsec197q5zzwnj5569azegmgwjnkzn5za3y0nm4y3khkyf2u84wgud28q562vpm"

This is a bech32-encoded Nostr private key in plain text, committed to a public repo. Even if it's a throwaway test key with no funds today, this is a hard no:

  • Anyone who forks the repo gets the key
  • Automated secret scanners will flag it
  • If someone accidentally loads this key into a funded wallet, funds are exposed

Fix: Move to config.yaml (already gitignored) as WALLET_NSEC env var. Reference as ${WALLET_NSEC} in the flow. Document the placeholder in config.yaml.example. The infrastructure for this already exists — WALLET_URL and WALLET_PASSWORD are already handled this way.


🟡 MEDIUM — Hardcoded password 123qwerty. in complete-tests

maestro/complete-tests/flows/13_unlock_wallet.yaml (3 occurrences):

- inputText: "123qwerty."

Inconsistent with the existing pattern: maestro/subflows/ correctly uses ${WALLET_PASSWORD} from config. The complete-tests flows should do the same.

Fix: Replace all "123qwerty." literals with ${WALLET_PASSWORD}.


ℹ️ Minor notes (non-blocking)

  1. maestro/scripts/build-report.pyET.parse(path) uses stdlib ElementTree which is safe against XXE in modern CPython, but defusedxml would be more defensive if the project ever consumes XML from untrusted sources. Low priority.

  2. maestro/scripts/adapt-device.py--write overwrites flow YAML files in place. The explicit flag requirement is good design, but consider adding a confirmation prompt or always creating .bak files by default (currently opt-in via --backup).

  3. 88 files is a large PR. Consider whether future changes to this suite could be split (e.g., scripts + infra in one PR, flows in another) to make review more tractable.


Verdict: Fix the hardcoded nsec (critical) and the hardcoded password (medium), then this is good to merge. The test infrastructure itself is solid — well-documented, clean shell scripting, sensible gitignore, and a thoughtful device-adaptation pipeline.

🤖 Reviewed by Arkana

Use WALLET_NSEC and WALLET_PASSWORD env vars in flows; validate in run-scenario for restore/unlock scenarios. Remove hardcoded secrets from YAML.

Co-authored-by: Cursor <cursoragent@cursor.com>

@arkanaai arkanaai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Follow-up Review — 5ff4c067

Previous findings addressed ✅

🔴 Hardcoded nsec — Fixed. Both maestro/flows/01_startup_restore.yaml and maestro/complete-tests/flows/02_startup_restore.yaml now use ${WALLET_NSEC}.

🟡 Hardcoded password — Fixed. All three "123qwerty." literals in maestro/complete-tests/flows/13_unlock_wallet.yaml replaced with ${WALLET_PASSWORD}.

Implementation review of new code:

The refactoring in maestro/scripts/maestro-config.sh is clean — read_config_env, export_maestro_env, maestro_env_args, and per-scenario validation in run-scenario.sh (require_wallet_nsec for restore scenarios, require_wallet_password for unlock scenarios). Good separation.

One minor nit:

maestro/studio.sh:22-e flags are passed before the studio subcommand:

exec maestro -p android --device "$DEVICE" --config "$CONFIG" "${MAESTRO_ENV[@]}" studio

maestro studio may not accept -e env flags (they're a maestro test feature). This could error or silently drop the vars. Since export_maestro_env already exports them as shell env vars (which Maestro should pick up from the environment), you might want to skip MAESTRO_ENV in studio.sh only — or verify maestro studio -e works on your setup. Non-blocking.

No protocol-critical code. LGTM.

🤖 Reviewed by Arkana

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
maestro/complete-tests/subflows/keyboard_enter_sats.yaml (1)

1-8: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Incomplete implementation: no keyboard taps present.

The file is named keyboard_enter_sats.yaml and the comment mentions "coordinate taps," but the file only contains a wait for the "Save" button to appear—no tap actions are defined. Either the keyboard tap sequence is missing or this file should be renamed to reflect its actual behavior (e.g., wait_for_save_button.yaml).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/keyboard_enter_sats.yaml` around lines 1 - 8,
The YAML currently only waits for the "Save" button via the extendedWaitUntil
step but is missing the keyboard coordinate tap sequence implied by the filename
keyboard_enter_sats.yaml; either implement the missing taps or rename the file
to match behavior. To fix, add the keyboard tap steps (e.g., a sequence of
tap/tapCoordinate actions that press the numeric keys and then confirm) after
the extendedWaitUntil step and before any final action, referencing the same
element coordinates or input field used in other keyboard subflows;
alternatively, if no keyboard interaction is needed, rename the file and update
any references so the name reflects the wait-for-save behavior and remove
expectations of keyboard taps. Ensure the changes touch the step sequence around
extendedWaitUntil and any calling flows that expect keyboard_enter_sats.yaml.
maestro/complete-tests/subflows/scroll_quick_actions_into_view.yaml (1)

1-4: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Filename does not match behavior.

The file is named scroll_quick_actions_into_view.yaml but it only executes a 1-second wait with no scroll action. Either the scroll action is missing, or the file should be renamed to reflect its actual behavior (e.g., wait_quick_actions_settle.yaml).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/scroll_quick_actions_into_view.yaml` around
lines 1 - 4, The file scroll_quick_actions_into_view.yaml currently only sets
appId: com.android.chrome and runs ../../subflows/wait_1s.yaml, so its name is
misleading; either add the missing scroll behavior or rename the file. Fix by
either (A) implementing the intended scroll action(s) into this flow (e.g.,
replace or augment runFlow with a subflow that performs the quick-actions scroll
or add explicit scroll steps) referencing this file's existing runFlow symbol,
or (B) rename the file to something accurate like wait_quick_actions_settle.yaml
and update any callers to reference the new name. Ensure the change keeps appId:
com.android.chrome and that any references to this file elsewhere are updated
accordingly.
maestro/complete-tests/flows/01_fresh_wallet.yaml (1)

9-35: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Flow can false-pass because it never verifies wallet creation succeeded.

Lines 9–35 only execute taps/waits, so this scenario may pass even when onboarding fails. Add an explicit post-condition check.

Suggested patch
 - tapOn:
     point: "53%, 91%"
     waitToSettleTimeoutMs: 800
+ - runFlow: ../subflows/wait_wallet_ready_after_create.yaml
+ - runFlow: ../subflows/assert_on_home.yaml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/flows/01_fresh_wallet.yaml` around lines 9 - 35, The
flow currently only performs taps/waits (the tapOn steps and runFlow calls) and
can false-pass; add a post-condition verification after the final tap to ensure
wallet creation succeeded by invoking a check (for example a new runFlow like
runFlow: ../../subflows/check_wallet_created.yaml or an explicit assertion step
such as waitForText/assertVisible) that looks for a definitive success indicator
(success message, account ID, wallet home element, or onboarding-complete
screen). Ensure the new check runs after the last tapOn step and fails the flow
if the expected element/text is not present.
maestro/complete-tests/flows/04_home_open_receive.yaml (1)

10-16: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scenario intent is unverified (opens QR screen) and can false-pass.

After opening Receive/QR, add explicit assertions for receive state and return-to-home state.

Suggested patch
 - runFlow: ../subflows/tap_receive_qr.yaml
 - runFlow: ../../subflows/wait_2s.yaml
+ - runFlow: ../subflows/assert_receive_screen.yaml
 - runFlow: ../subflows/tap_receive_back.yaml
 - runFlow: ../../subflows/wait_2s.yaml
+ - runFlow: ../subflows/assert_on_home.yaml
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/flows/04_home_open_receive.yaml` around lines 10 - 16,
The "opens QR screen" step is unverified and may false-pass; after running the
tap_home_receive flow (runFlow: ../subflows/tap_home_receive.yaml) insert an
explicit assertion subflow that verifies the app is in the receive state (e.g.,
check for QR screen element/state), and after running tap_receive_back.yaml
insert an assertion subflow that verifies the app returned to the home state
(e.g., home screen element/state); use or create assertion subflows (e.g.,
assert_receive_state.yaml and assert_home_state.yaml) and place them immediately
after the respective runFlow calls (keeping wait_2s where needed).
🧹 Nitpick comments (10)
maestro/README.md (2)

72-72: ⚡ Quick win

Document the security model for the hardcoded test password.

Flow 13 uses a "fixed test password" visible in the flow file. While acceptable for ephemeral test wallets, this pattern should be explicitly documented as test-only to prevent accidental reuse with real credentials.

Consider adding a sentence clarifying that test flows must never contain real passwords or seed phrases, and that the unlock flow uses a known-safe test value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/README.md` at line 72, Add a security note to the README documenting
that the "fixed test password" used by the 13_unlock_wallet.yaml flow is
strictly a test-only value: explicitly state that flows containing hardcoded
passwords or seed phrases (e.g., 13_unlock_wallet.yaml) are for ephemeral test
wallets only, must never contain real credentials, and that the unlock flow
relies on a known-safe test value; include a short sentence on recommended
practice (remove/replace any hardcoded secrets before using with real wallets).

182-182: 💤 Low value

Consider redacting or generalizing the device serial.

The ADB serial INBIJVCASW5HS4YX uniquely identifies the reference device. While low-risk for a test device, consider using a placeholder like <DEVICE_SERIAL> in documentation to avoid setting a precedent of exposing device identifiers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/README.md` at line 182, The README currently exposes a concrete ADB
serial value in the table cell under the "ADB serial" heading; replace the
literal string "INBIJVCASW5HS4YX" with a generalized placeholder (e.g.
"<DEVICE_SERIAL>" or "REDACTED") so the table entry under "ADB serial" no longer
contains a unique device identifier; update the README table row where the "ADB
serial" cell appears accordingly.
maestro/scripts/build-report.py (1)

202-202: ⚖️ Poor tradeoff

Consider documenting the 4000-character truncation limit.

Test failure details are truncated to 4000 characters to prevent excessively large Markdown reports. For long stack traces or verbose Maestro output, this may hide relevant diagnostic information. Consider either documenting this limit in a comment or making it configurable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/scripts/build-report.py` at line 202, The truncation of test failure
details is hardcoded via slicing c.details[:4000]; make this limit explicit and
configurable by introducing a named constant (e.g., MAX_DETAIL_LENGTH) or a
CLI/env option (e.g., --max-detail-length) and use it in the build-report logic
where lines.append(c.details[:4000]) occurs, and add a brief inline comment
explaining why truncation is necessary and what the limit means; update any
relevant help text or docs to mention the default 4000-character limit.
maestro/scripts/run-scenario.sh (1)

33-83: ⚡ Quick win

Validate that FLOWS array is non-empty before running maestro test.

If a scenario case returns no flows (e.g., due to a logic error or missing files), maestro test will be invoked with an empty flow list. While Maestro will likely fail gracefully, an explicit check would provide a clearer error message.

🛡️ Proposed validation
   esac
 )
 
+if [[ ${`#FLOWS`[@]} -eq 0 ]]; then
+  echo "error: no flows resolved for scenario: $SCENARIO" >&2
+  exit 1
+fi
+
 # shellcheck source=maestro-device.sh
 source "$SCRIPTS/maestro-device.sh"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/scripts/run-scenario.sh` around lines 33 - 83, The case block can
produce an empty FLOWS array; after the mapfile command that populates FLOWS,
add a validation step that checks the length of the FLOWS array (e.g., test
${`#FLOWS`[@]}), and if zero print a clear error including SCENARIO and
COMPLETE_FLOWS context and exit non‑zero so maestro test is never invoked with
an empty flow list; locate the check immediately after the mapfile -t FLOWS <
<(...) block and reference the FLOWS variable for the condition.
maestro/scripts/maestro-config.sh (1)

17-20: ⚖️ Poor tradeoff

Consider using a YAML parser for more robust config extraction.

The sed pattern assumes a specific YAML format (two-space indent, double quotes, exact key name). While this works for the controlled config.yaml file, it's fragile to formatting changes. For a simple single-value extraction, this is acceptable, but if more config values are needed in the future, consider using yq or a Python YAML parser.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/scripts/maestro-config.sh` around lines 17 - 20, The read_wallet_url
function currently uses a fragile sed regex to extract WALLET_URL; replace that
logic with a proper YAML parser call (e.g., yq or a small python -c snippet
using PyYAML) to robustly read the WALLET_URL value from the provided config
file; update read_wallet_url to invoke the parser and return the first/only
WALLET_URL, ensuring it handles different quoting, indentation and key orders
and falls back gracefully if the key is missing.
maestro/complete-tests/subflows/ensure_on_wallet_home.yaml (1)

7-42: ⚖️ Poor tradeoff

Consider adding a catch-all for unexpected wallet states.

The flow conditionally handles "Continue anyway", "Unlock wallet", "Welcome to Arkade", and "Receive" visibility checks. If the wallet lands in an unexpected state (e.g., error screen, network failure, update prompt), the flow continues silently without validation. Consider adding a final assertion to confirm the wallet is on the home screen, or a catch-all condition to fail explicitly.

Optional: Add final home screen assertion
 - runFlow: scroll_home_to_top.yaml
 - runFlow: ../../subflows/wait_2s.yaml
+
+# Final validation: ensure we're on wallet home.
+- assertVisible: "^Receive$"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/ensure_on_wallet_home.yaml` around lines 7 -
42, The flow lacks a catch-all for unexpected wallet states; after the existing
runFlow blocks (including scroll_home_to_top.yaml and before/after
reset_to_wallet_home.yaml) add a final validation step that asserts the wallet
home is visible (e.g., check visible "Receive" or another home-unique selector)
and fail explicitly if not present, or add a fallback runFlow that captures/logs
unexpected screens and calls reset_to_wallet_home.yaml before proceeding; update
the runFlow sequence to include this final assertion/fallback so
ensure_on_wallet_home.yaml always verifies home state.
maestro/complete-tests/subflows/keyboard_tap_1000.yaml (1)

1-11: ⚡ Quick win

Consider adding delays between keyboard taps.

Similar to keyboard_tap_100.yaml, these rapid taps might miss if the keyboard doesn't register them quickly enough. Adding short delays (e.g., 100-200ms) between taps would improve reliability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/keyboard_tap_1000.yaml` around lines 1 - 11,
The sequence of consecutive tapOn steps in keyboard_tap_1000.yaml (the repeated
tapOn entries at points "16%, 55%" and "50%, 84%") can be missed due to
too-rapid input; insert short delay/wait steps (100–200ms) between each tapOn
step to give the keyboard time to register taps. Locate the repeated tapOn steps
and add a wait/sleep action (using your project's existing wait step name)
between each tapOn call so the flow becomes tapOn → wait(100-200ms) → tapOn →
wait → tapOn etc., ensuring timing is consistent with keyboard_tap_100.yaml’s
approach.
maestro/complete-tests/subflows/scroll_home_to_top.yaml (1)

3-7: ⚡ Quick win

Comment wording may be misleading.

The comment states "Pull content down" but the swipe gesture moves from 32% to 72% vertically (downward finger motion), which typically scrolls content upward. This appears to be scrolling to the top of the page (bringing top content into view). Consider clarifying the comment to say "Scroll to top" or "Bring quick actions into view from above" to match the actual gesture.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/scroll_home_to_top.yaml` around lines 3 - 7,
The inline comment is misleading: the swipe block (swipe: start: "50%, 32%" end:
"50%, 72%" duration: 450) performs a downward finger motion that scrolls the
page content upward, so update the comment to accurately describe the effect
(for example "Scroll to top to bring quick actions into view" or "Scroll content
up to reveal quick actions") to match the actual gesture.
maestro/complete-tests/subflows/keyboard_tap_100.yaml (1)

1-9: ⚡ Quick win

Consider adding delays between keyboard taps.

Similar to the backspace subflow, these rapid taps might miss if the keyboard doesn't register them quickly enough. Adding short delays (e.g., 100-200ms) between taps would improve reliability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/keyboard_tap_100.yaml` around lines 1 - 9,
The three rapid tapOn steps (points "16%, 55%" and two at "50%, 84%") should
have short delays inserted between them to improve reliability; update the YAML
to add a delay/wait action (e.g., 100–200ms) after the first tapOn and again
after the second tapOn so the keyboard has time to register each tap, keeping
the existing tapOn entries and points intact.
maestro/complete-tests/subflows/keyboard_backspace_x4.yaml (1)

3-7: ⚡ Quick win

Add delays between rapid keyboard taps across all keyboard subflows.

Three keyboard subflows (keyboard_backspace_x4.yaml, keyboard_tap_100.yaml, and keyboard_tap_1000.yaml) perform rapid successive taps without delays. If the on-screen keyboard has animation or doesn't register taps immediately, some inputs may be missed. Adding a short delay (e.g., 100-200ms) between each tap would improve reliability across all keyboard interaction subflows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/keyboard_backspace_x4.yaml` around lines 3 -
7, The keyboard subflows (keyboard_backspace_x4.yaml, keyboard_tap_100.yaml,
keyboard_tap_1000.yaml) perform consecutive tapOn actions with no pause; update
each subflow's repeat block (the repeat/times and its commands containing tapOn)
to insert a short delay (about 100–200ms) between each tap — e.g., add a
delay/wait command immediately after the tapOn entry inside the repeat so each
iteration waits before the next tap, ensuring the keyboard has time to
animate/register input.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@maestro/complete-tests/flows/02_startup_restore.yaml`:
- Line 51: The flow currently contains a hardcoded private key in the inputText
field of 02_startup_restore.yaml; remove the literal and replace inputText with
a runtime-injected secret reference (e.g., read from an environment variable or
secret manager) so the key is not stored in source. Update the flow to reference
a placeholder like inputText: "${PRIVATE_KEY}" (or the project's secret
injection syntax), ensure the runtime/test harness (where the flow is executed)
sets PRIVATE_KEY from environment/config or vault, and update any test fixtures
or CI to supply the secret via encrypted vars rather than committing the key.

In `@maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml`:
- Around line 8-13: The flow currently opens the Home Swap sheet (runFlow:
../subflows/tap_home_swap.yaml) and immediately dismisses it (tapOn: point "50%,
88%") without verifying content; insert an assertion step between opening the
sheet and tapping to dismiss that checks the sheet text/element (e.g.,
assertText/expectElement with the "Coming Soon" string or the sheet's unique
selector) and keep the existing wait runs (runFlow: ../../subflows/wait_2s.yaml)
as needed to allow the sheet to render; target the sequence after runFlow:
../subflows/tap_home_swap.yaml and before tapOn to ensure the "Coming Soon"
content is present.

In `@maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml`:
- Line 7: The visible guard ".*wants to see.*" is English-specific and causes
branches to be skipped on non-English UIs; replace this `visible` check in the
dismiss clipboard permission subflow (the property currently set to ".*wants to
see.*") with a locale-agnostic condition — e.g., match a stable element
id/attribute or dialog role/class (resource-id, accessibility-id, or a regex
matching punctuation/structure like ".*clipboard.*" or matching the dialog
container via selector) or add localized alternatives for each supported locale
so the modal-handling branch triggers regardless of UI language.

In `@maestro/complete-tests/subflows/ensure_on_wallet_home.yaml`:
- Line 23: The unlock flow uses inputText: ${WALLET_PASSWORD} in the "Unlock
wallet" branch but doesn't guard empty envs; add a fail-fast check so the runner
or the ensure_on_wallet_home.yaml unlock step explicitly errors when
WALLET_PASSWORD is empty (or alternately skip the input and assert wallet is
unlocked). Locate the "Unlock wallet" branch and the inputText:
${WALLET_PASSWORD} occurrence and either (a) add a pre-step that verifies
WALLET_PASSWORD is non-empty and fails with a clear message, or (b) wrap the
existing unlock step with a condition that only runs if WALLET_PASSWORD is set
and otherwise raises a clear error/abort so tests don’t silently proceed.

In `@maestro/complete-tests/subflows/scroll_settings_down_2.yaml`:
- Around line 5-8: The swipe in the subflow scroll_settings_down_2 is inverted:
the current start "12%, 46%" to end "7%, 77%" moves downward (y increases) but
the other scroll_settings_down_* flows use an upward swipe; fix by making the
end y smaller than the start y so the gesture scrolls up (either swap the
start/end coordinates or adjust the end y to a value less than 46%), updating
the swipe block in scroll_settings_down_2 accordingly.

In `@maestro/config.yaml.example`:
- Line 19: Update the default test configuration to disable device animations by
changing the disableAnimations setting to true in maestro/config.yaml.example;
modify the disableAnimations: false entry to disableAnimations: true (or make it
configurable via an env var) and add a brief comment noting to set it to false
only when testing transition/animation-specific behavior so tests remain
reliable.

In `@maestro/flows/01_startup_restore.yaml`:
- Line 8: Replace the incorrect cased runner path string "./Maestro/run.sh
flows/01_startup_restore.yaml" with the correct "./maestro/run.sh
flows/01_startup_restore.yaml" in the inline hint; search for the exact token
"./Maestro/run.sh" in 01_startup_restore.yaml and update it to use lowercase
"maestro" so the command works on case-sensitive filesystems.
- Line 52: The flow file contains a hardcoded secret in the inputText field;
remove the literal and replace it with a runtime-injected secret (e.g., a
placeholder variable) that your loader resolves from a gitignored config or
environment variable (e.g., RESTORE_PRIVATE_KEY) at runtime; update any code
that parses 01_startup_restore.yaml to read that env/config key instead of the
literal and ensure the real private key is provided via CI secret store, local
gitignored .env/config, or a secrets manager so no credentials are committed.

In `@maestro/record-taps.sh`:
- Around line 19-21: The WM/W/H extraction can yield empty or non-numeric
values; validate WM, W, and H after extracting them (symbols: WM, W, H) and
before invoking the Python parser: check WM is non-empty and that W and H match
numeric-only regex (e.g. digits), and if validation fails log a clear error
(include WM contents and DEVICE) and exit non-zero or set safe defaults; update
the script to perform this numeric validation and early exit so the Python
consumer always receives valid integers.

In `@maestro/scripts/adapt-device.py`:
- Around line 128-136: The parse_wm_size function currently returns the first
"size:" line it finds which may be the Physical size; update parse_wm_size to
prefer an "Override size" line when present by scanning all lines and selecting
the token from a line that contains "override" (case-insensitive) first,
otherwise fall back to the first physical "size" line; keep the existing parsing
logic (split on ":" then "x" and int conversion) and retain the sys.exit error
behavior if no valid size is found.
- Around line 196-204: The chrome-zone remap currently scales x/y by full screen
sizes (src_w/src_h), ignoring differing chrome cutoffs; update the branch in
adapt-device.py that handles in_chrome_zone to map coordinates relative to the
source chrome bounds and then project into the destination chrome bounds using
src_chrome and dst_chrome values rather than raw screen dims. Concretely,
compute the source chrome region min/max (e.g., nav/status/header bounds from
src_chrome), convert x/y to a percentage inside that source chrome region, then
interpolate that percentage into the destination chrome region using dst_chrome
min/max and clamp_pct; keep using clamp_pct and existing variables
(in_chrome_zone, src_chrome, dst_chrome, clamp_pct, src/dst screen sizes) to
locate and replace the current simple src_w/src_h scaling.
- Around line 96-114: adb_shell and adb_devices call subprocess.run without
timeouts and can block indefinitely; add a timeout argument (e.g., timeout=30 or
configurable constant) to both subprocess.run calls in the functions adb_shell
and adb_devices, and catch subprocess.TimeoutExpired to call sys.exit with a
clear error message including the command and that it timed out; keep existing
capture_output, text and check flags and ensure the error handling in adb_shell
still exits with stderr when non-zero returncode.

In `@maestro/scripts/parse-taps.py`:
- Line 73: The out.append call uses an unnecessary f-string: change the call
that appends "- tapOn:" (the out.append(f"- tapOn:") expression referencing the
out list and its append method) to use a regular string literal without the
f-prefix so the code becomes out.append("- tapOn:") to remove the redundant
formatting marker.
- Around line 87-89: The script currently calls int() directly on sys.argv
entries when building touch_x, touch_y and calling parse(Path(sys.argv[1]),
int(sys.argv[2]), int(sys.argv[3]), ...) which will raise ValueError on bad
input; wrap these conversions in a try/except (or extract a small helper like
validate_int(name, value, default=None)) to validate numeric input, catch
ValueError, print a clear user-facing error message indicating which argument is
invalid (e.g., width, height, touch_x, touch_y), and exit with a non-zero status
so parse() only receives valid integers; update the code paths that set touch_x,
touch_y and the int() calls for parse to use this validated conversion.

---

Outside diff comments:
In `@maestro/complete-tests/flows/01_fresh_wallet.yaml`:
- Around line 9-35: The flow currently only performs taps/waits (the tapOn steps
and runFlow calls) and can false-pass; add a post-condition verification after
the final tap to ensure wallet creation succeeded by invoking a check (for
example a new runFlow like runFlow: ../../subflows/check_wallet_created.yaml or
an explicit assertion step such as waitForText/assertVisible) that looks for a
definitive success indicator (success message, account ID, wallet home element,
or onboarding-complete screen). Ensure the new check runs after the last tapOn
step and fails the flow if the expected element/text is not present.

In `@maestro/complete-tests/flows/04_home_open_receive.yaml`:
- Around line 10-16: The "opens QR screen" step is unverified and may
false-pass; after running the tap_home_receive flow (runFlow:
../subflows/tap_home_receive.yaml) insert an explicit assertion subflow that
verifies the app is in the receive state (e.g., check for QR screen
element/state), and after running tap_receive_back.yaml insert an assertion
subflow that verifies the app returned to the home state (e.g., home screen
element/state); use or create assertion subflows (e.g.,
assert_receive_state.yaml and assert_home_state.yaml) and place them immediately
after the respective runFlow calls (keeping wait_2s where needed).

In `@maestro/complete-tests/subflows/keyboard_enter_sats.yaml`:
- Around line 1-8: The YAML currently only waits for the "Save" button via the
extendedWaitUntil step but is missing the keyboard coordinate tap sequence
implied by the filename keyboard_enter_sats.yaml; either implement the missing
taps or rename the file to match behavior. To fix, add the keyboard tap steps
(e.g., a sequence of tap/tapCoordinate actions that press the numeric keys and
then confirm) after the extendedWaitUntil step and before any final action,
referencing the same element coordinates or input field used in other keyboard
subflows; alternatively, if no keyboard interaction is needed, rename the file
and update any references so the name reflects the wait-for-save behavior and
remove expectations of keyboard taps. Ensure the changes touch the step sequence
around extendedWaitUntil and any calling flows that expect
keyboard_enter_sats.yaml.

In `@maestro/complete-tests/subflows/scroll_quick_actions_into_view.yaml`:
- Around line 1-4: The file scroll_quick_actions_into_view.yaml currently only
sets appId: com.android.chrome and runs ../../subflows/wait_1s.yaml, so its name
is misleading; either add the missing scroll behavior or rename the file. Fix by
either (A) implementing the intended scroll action(s) into this flow (e.g.,
replace or augment runFlow with a subflow that performs the quick-actions scroll
or add explicit scroll steps) referencing this file's existing runFlow symbol,
or (B) rename the file to something accurate like wait_quick_actions_settle.yaml
and update any callers to reference the new name. Ensure the change keeps appId:
com.android.chrome and that any references to this file elsewhere are updated
accordingly.

---

Nitpick comments:
In `@maestro/complete-tests/subflows/ensure_on_wallet_home.yaml`:
- Around line 7-42: The flow lacks a catch-all for unexpected wallet states;
after the existing runFlow blocks (including scroll_home_to_top.yaml and
before/after reset_to_wallet_home.yaml) add a final validation step that asserts
the wallet home is visible (e.g., check visible "Receive" or another home-unique
selector) and fail explicitly if not present, or add a fallback runFlow that
captures/logs unexpected screens and calls reset_to_wallet_home.yaml before
proceeding; update the runFlow sequence to include this final assertion/fallback
so ensure_on_wallet_home.yaml always verifies home state.

In `@maestro/complete-tests/subflows/keyboard_backspace_x4.yaml`:
- Around line 3-7: The keyboard subflows (keyboard_backspace_x4.yaml,
keyboard_tap_100.yaml, keyboard_tap_1000.yaml) perform consecutive tapOn actions
with no pause; update each subflow's repeat block (the repeat/times and its
commands containing tapOn) to insert a short delay (about 100–200ms) between
each tap — e.g., add a delay/wait command immediately after the tapOn entry
inside the repeat so each iteration waits before the next tap, ensuring the
keyboard has time to animate/register input.

In `@maestro/complete-tests/subflows/keyboard_tap_100.yaml`:
- Around line 1-9: The three rapid tapOn steps (points "16%, 55%" and two at
"50%, 84%") should have short delays inserted between them to improve
reliability; update the YAML to add a delay/wait action (e.g., 100–200ms) after
the first tapOn and again after the second tapOn so the keyboard has time to
register each tap, keeping the existing tapOn entries and points intact.

In `@maestro/complete-tests/subflows/keyboard_tap_1000.yaml`:
- Around line 1-11: The sequence of consecutive tapOn steps in
keyboard_tap_1000.yaml (the repeated tapOn entries at points "16%, 55%" and
"50%, 84%") can be missed due to too-rapid input; insert short delay/wait steps
(100–200ms) between each tapOn step to give the keyboard time to register taps.
Locate the repeated tapOn steps and add a wait/sleep action (using your
project's existing wait step name) between each tapOn call so the flow becomes
tapOn → wait(100-200ms) → tapOn → wait → tapOn etc., ensuring timing is
consistent with keyboard_tap_100.yaml’s approach.

In `@maestro/complete-tests/subflows/scroll_home_to_top.yaml`:
- Around line 3-7: The inline comment is misleading: the swipe block (swipe:
start: "50%, 32%" end: "50%, 72%" duration: 450) performs a downward finger
motion that scrolls the page content upward, so update the comment to accurately
describe the effect (for example "Scroll to top to bring quick actions into
view" or "Scroll content up to reveal quick actions") to match the actual
gesture.

In `@maestro/README.md`:
- Line 72: Add a security note to the README documenting that the "fixed test
password" used by the 13_unlock_wallet.yaml flow is strictly a test-only value:
explicitly state that flows containing hardcoded passwords or seed phrases
(e.g., 13_unlock_wallet.yaml) are for ephemeral test wallets only, must never
contain real credentials, and that the unlock flow relies on a known-safe test
value; include a short sentence on recommended practice (remove/replace any
hardcoded secrets before using with real wallets).
- Line 182: The README currently exposes a concrete ADB serial value in the
table cell under the "ADB serial" heading; replace the literal string
"INBIJVCASW5HS4YX" with a generalized placeholder (e.g. "<DEVICE_SERIAL>" or
"REDACTED") so the table entry under "ADB serial" no longer contains a unique
device identifier; update the README table row where the "ADB serial" cell
appears accordingly.

In `@maestro/scripts/build-report.py`:
- Line 202: The truncation of test failure details is hardcoded via slicing
c.details[:4000]; make this limit explicit and configurable by introducing a
named constant (e.g., MAX_DETAIL_LENGTH) or a CLI/env option (e.g.,
--max-detail-length) and use it in the build-report logic where
lines.append(c.details[:4000]) occurs, and add a brief inline comment explaining
why truncation is necessary and what the limit means; update any relevant help
text or docs to mention the default 4000-character limit.

In `@maestro/scripts/maestro-config.sh`:
- Around line 17-20: The read_wallet_url function currently uses a fragile sed
regex to extract WALLET_URL; replace that logic with a proper YAML parser call
(e.g., yq or a small python -c snippet using PyYAML) to robustly read the
WALLET_URL value from the provided config file; update read_wallet_url to invoke
the parser and return the first/only WALLET_URL, ensuring it handles different
quoting, indentation and key orders and falls back gracefully if the key is
missing.

In `@maestro/scripts/run-scenario.sh`:
- Around line 33-83: The case block can produce an empty FLOWS array; after the
mapfile command that populates FLOWS, add a validation step that checks the
length of the FLOWS array (e.g., test ${`#FLOWS`[@]}), and if zero print a clear
error including SCENARIO and COMPLETE_FLOWS context and exit non‑zero so maestro
test is never invoked with an empty flow list; locate the check immediately
after the mapfile -t FLOWS < <(...) block and reference the FLOWS variable for
the condition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de616852-55f7-4e0c-8805-2c9720df6a56

📥 Commits

Reviewing files that changed from the base of the PR and between 1637f4d and 0358cfb.

📒 Files selected for processing (100)
  • maestro/.gitignore
  • maestro/README.md
  • maestro/REPORTING.md
  • maestro/complete-tests/README.md
  • maestro/complete-tests/flows/01_fresh_wallet.yaml
  • maestro/complete-tests/flows/02_startup_restore.yaml
  • maestro/complete-tests/flows/03_home_quick_actions_visible.yaml
  • maestro/complete-tests/flows/04_home_open_receive.yaml
  • maestro/complete-tests/flows/05_home_open_send.yaml
  • maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml
  • maestro/complete-tests/flows/07_home_open_activity.yaml
  • maestro/complete-tests/flows/08_home_open_settings.yaml
  • maestro/complete-tests/flows/09_home_tabs_and_asset_filters.yaml
  • maestro/complete-tests/flows/10_receive_copy_sheet.yaml
  • maestro/complete-tests/flows/11_send_form_address_field.yaml
  • maestro/complete-tests/flows/12_home_open_scan.yaml
  • maestro/complete-tests/flows/13_unlock_wallet.yaml
  • maestro/complete-tests/flows/14_home_scroll_balance_sticky.yaml
  • maestro/complete-tests/flows/15_settings_deep_reset_wallet.yaml
  • maestro/complete-tests/flows/_run_all_smoke.yaml
  • maestro/complete-tests/flows/_run_reset_then_create.yaml
  • maestro/complete-tests/subflows/assert_home_quick_actions.yaml
  • maestro/complete-tests/subflows/assert_on_home.yaml
  • maestro/complete-tests/subflows/assert_receive_screen.yaml
  • maestro/complete-tests/subflows/assert_send_form.yaml
  • maestro/complete-tests/subflows/close_copy_sheet.yaml
  • maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml
  • maestro/complete-tests/subflows/ensure_on_wallet_home.yaml
  • maestro/complete-tests/subflows/exercise_send_form.yaml
  • maestro/complete-tests/subflows/go_back_to_home.yaml
  • maestro/complete-tests/subflows/keyboard_backspace_x4.yaml
  • maestro/complete-tests/subflows/keyboard_enter_sats.yaml
  • maestro/complete-tests/subflows/keyboard_tap_100.yaml
  • maestro/complete-tests/subflows/keyboard_tap_1000.yaml
  • maestro/complete-tests/subflows/keyboard_tap_500.yaml
  • maestro/complete-tests/subflows/navigate_home.yaml
  • maestro/complete-tests/subflows/open_receive_from_home.yaml
  • maestro/complete-tests/subflows/open_send_from_home.yaml
  • maestro/complete-tests/subflows/open_settings_from_home.yaml
  • maestro/complete-tests/subflows/receive_add_amount_keyboard.yaml
  • maestro/complete-tests/subflows/receive_copy_share_sheet.yaml
  • maestro/complete-tests/subflows/reset_to_wallet_home.yaml
  • maestro/complete-tests/subflows/scroll_home_to_top.yaml
  • maestro/complete-tests/subflows/scroll_quick_actions_into_view.yaml
  • maestro/complete-tests/subflows/scroll_settings_down_1.yaml
  • maestro/complete-tests/subflows/scroll_settings_down_2.yaml
  • maestro/complete-tests/subflows/scroll_settings_down_3.yaml
  • maestro/complete-tests/subflows/tap_header_activity.yaml
  • maestro/complete-tests/subflows/tap_header_back.yaml
  • maestro/complete-tests/subflows/tap_header_settings.yaml
  • maestro/complete-tests/subflows/tap_home_receive.yaml
  • maestro/complete-tests/subflows/tap_home_scan.yaml
  • maestro/complete-tests/subflows/tap_home_send.yaml
  • maestro/complete-tests/subflows/tap_home_swap.yaml
  • maestro/complete-tests/subflows/tap_receive_add_amount.yaml
  • maestro/complete-tests/subflows/tap_receive_back.yaml
  • maestro/complete-tests/subflows/tap_receive_copy.yaml
  • maestro/complete-tests/subflows/tap_receive_copy_sheet_row.yaml
  • maestro/complete-tests/subflows/tap_receive_qr.yaml
  • maestro/complete-tests/subflows/tap_send_amount.yaml
  • maestro/complete-tests/subflows/tap_send_back.yaml
  • maestro/complete-tests/subflows/tap_send_paste.yaml
  • maestro/complete-tests/subflows/tap_send_recipient.yaml
  • maestro/complete-tests/subflows/tap_send_scan.yaml
  • maestro/complete-tests/subflows/tap_settings_back.yaml
  • maestro/complete-tests/subflows/tap_settings_menu_row_28.yaml
  • maestro/complete-tests/subflows/tap_settings_menu_row_36.yaml
  • maestro/complete-tests/subflows/tap_settings_menu_row_43.yaml
  • maestro/complete-tests/subflows/tap_settings_menu_row_51.yaml
  • maestro/complete-tests/subflows/tap_settings_menu_row_58.yaml
  • maestro/complete-tests/subflows/tap_settings_menu_row_66.yaml
  • maestro/complete-tests/subflows/tap_settings_menu_row_69.yaml
  • maestro/complete-tests/subflows/tap_settings_menu_row_83.yaml
  • maestro/complete-tests/subflows/tap_settings_sub_21.yaml
  • maestro/complete-tests/subflows/tap_settings_sub_63.yaml
  • maestro/complete-tests/subflows/tap_welcome_create_wallet.yaml
  • maestro/complete-tests/subflows/wait_receive_ready.yaml
  • maestro/complete-tests/subflows/wait_wallet_ready_after_create.yaml
  • maestro/config.yaml.example
  • maestro/device-profiles.yaml
  • maestro/flows/01_startup_restore.yaml
  • maestro/record-taps.sh
  • maestro/recordings/.gitkeep
  • maestro/run.sh
  • maestro/scripts/adapt-device.py
  • maestro/scripts/adapt-device.sh
  • maestro/scripts/build-report.py
  • maestro/scripts/maestro-config.sh
  • maestro/scripts/maestro-device.sh
  • maestro/scripts/open-report.sh
  • maestro/scripts/parse-taps.py
  • maestro/scripts/run-scenario.sh
  • maestro/studio.sh
  • maestro/subflows/open_wallet_url.yaml
  • maestro/subflows/tap_header_back.yaml
  • maestro/subflows/wait_1s.yaml
  • maestro/subflows/wait_2s.yaml
  • maestro/subflows/wait_3s.yaml
  • maestro/subflows/wait_4s.yaml
  • maestro/subflows/wait_5s.yaml

Comment thread maestro/complete-tests/flows/02_startup_restore.yaml Outdated
Comment thread maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml Outdated
Comment thread maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml
- tapOn:
point: "50%, 50%"
waitToSettleTimeoutMs: 800
- inputText: ${WALLET_PASSWORD}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if WALLET_PASSWORD is validated or documented.

# Test: Search for WALLET_PASSWORD in config and runner scripts.
rg -n 'WALLET_PASSWORD' maestro/config.yaml.example maestro/scripts/ maestro/README.md --context 3

Repository: arkade-os/wallet

Length of output: 849


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect the exact lines around the password input in ensure_on_wallet_home.yaml
sed -n '1,120p' maestro/complete-tests/subflows/ensure_on_wallet_home.yaml | nl -ba | sed -n '1,120p'

# 2) Find where WALLET_PASSWORD / env.WALLET_PASSWORD is referenced across maestro
rg -n 'WALLET_PASSWORD|env\.WALLET_PASSWORD' maestro -S --hidden --context 3

Repository: arkade-os/wallet

Length of output: 103


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show ensure_on_wallet_home.yaml with line numbers around the unlock/password area
awk 'NR>=1 && NR<=140 {print NR ":" $0}' maestro/complete-tests/subflows/ensure_on_wallet_home.yaml | sed -n '1,140p'

# Locate WALLET_PASSWORD usage and the specific README/config documentation
rg -n 'WALLET_PASSWORD|env\.WALLET_PASSWORD' maestro -S --hidden --context 3

Repository: arkade-os/wallet

Length of output: 2708


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find runner scripts that mention WALLET_PASSWORD
rg -n 'WALLET_PASSWORD|env\.WALLET_PASSWORD|\$\{WALLET_PASSWORD\}' maestro -S --hidden

# If run.sh/run-scenario.sh exist, print them (or relevant parts) to see how env vars are passed to maestro
ls -la maestro | sed -n '1,200p'
find maestro -maxdepth 2 -type f -name 'run*.sh' -o -name '*scenario*' | sort

# Print key runner scripts if found
for f in $(find maestro -maxdepth 3 -type f \( -name 'run.sh' -o -name 'run-scenario.sh' -o -name '*scenario*.sh' \) | sort); do
  echo "---- $f ----"
  awk 'NR<=200{print NR ":" $0}' "$f"
done

Repository: arkade-os/wallet

Length of output: 6611


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show config loader to see how env vars get passed to Maestro
ls -ლა maestro/scripts
sed -n '1,200p' maestro/scripts/maestro-config.sh

# Inspect config.yaml.example fully (it's small)
sed -n '1,200p' maestro/config.yaml.example

# Inspect README section around ensure_on_wallet_home / WALLET_PASSWORD
rg -n "ensure_on_wallet_home|WALLET_PASSWORD" maestro/README.md
sed -n '140,220p' maestro/README.md

Repository: arkade-os/wallet

Length of output: 124


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect config loader
ls -la maestro/scripts
sed -n '1,200p' maestro/scripts/maestro-config.sh

# Inspect config example (small)
sed -n '1,120p' maestro/config.yaml.example

# Inspect README around WALLET_PASSWORD docs
rg -n "ensure_on_wallet_home|WALLET_PASSWORD" maestro/README.md
sed -n '150,210p' maestro/README.md

Repository: arkade-os/wallet

Length of output: 4407


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for how other env vars are referenced in flows (with/without env.)
rg -n '\$\{(WALLET_URL|WALLET_PASSWORD|env\.)' maestro -S

# See how WALLET_PASSWORD is referenced (in case other flows guard it)
rg -n 'WALLET_PASSWORD' maestro/complete-tests -S

# Inspect ensure_on_wallet_home again with a bit more surrounding context
sed -n '1,120p' maestro/complete-tests/subflows/ensure_on_wallet_home.yaml

Repository: arkade-os/wallet

Length of output: 1794


🌐 Web query:

Maestro inputText empty string behavior skip unlock input

💡 Result:

In Maestro, the inputText command is designed to enter a specified text string into a UI element, whether or not a field is currently focused [1][2]. There is no native "skip unlock" parameter or behavior specific to providing an empty string to inputText. If an empty string is provided to inputText (e.g., inputText: ""), the command generally does not trigger an input action or may be treated as a no-op, depending on the underlying driver implementation and target platform. Maestro's documentation does not explicitly define a "skip" behavior for this command; it is primarily used for deterministic text entry [1][3]. Regarding your interest in "skipping" or "unlocking" input, users experiencing issues with inputText often report the following behaviors rather than intentional "skip" functionality: 1. Automatic Focus: Maestro may automatically focus on an available text field (like an email or username field) if the command is executed when no field is explicitly selected [4][3]. This can sometimes lead to unexpected input if multiple fields are present [4]. 2. Flakiness and Character Skipping: On some platforms, particularly when an application experiences jank or stutter, inputText has been known to flakily skip or mangle characters [5][6][7]. This is not a "skip" feature but a synchronization issue between the test runner and the application state [5][7]. 3. Workarounds: If you are looking to bypass or conditionally control input, users often implement custom logic using JavaScript expressions within Maestro (e.g., using evalScript or repeat commands) to control when or how text is entered [6][8][9]. If you are attempting to handle scenarios where you do not want to trigger an input, the standard practice is to use conditional logic within your Maestro Flow, such as assertVisible or checking the state of the UI before calling the inputText command [8][9].

Citations:


Guard WALLET_PASSWORD for the “Unlock wallet” branch

maestro/complete-tests/subflows/ensure_on_wallet_home.yaml uses inputText: ${WALLET_PASSWORD} when the “Unlock wallet” screen is visible. The docs state env.WALLET_PASSWORD empty = skip unlock input (and maestro/config.yaml.example defaults to WALLET_PASSWORD: ""), so password-protected wallets will likely fail to unlock without a clear error. Add a fail-fast/explicit check in the runner or around the unlock/input step when the unlock screen appears but WALLET_PASSWORD is empty.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/ensure_on_wallet_home.yaml` at line 23, The
unlock flow uses inputText: ${WALLET_PASSWORD} in the "Unlock wallet" branch but
doesn't guard empty envs; add a fail-fast check so the runner or the
ensure_on_wallet_home.yaml unlock step explicitly errors when WALLET_PASSWORD is
empty (or alternately skip the input and assert wallet is unlocked). Locate the
"Unlock wallet" branch and the inputText: ${WALLET_PASSWORD} occurrence and
either (a) add a pre-step that verifies WALLET_PASSWORD is non-empty and fails
with a clear message, or (b) wrap the existing unlock step with a condition that
only runs if WALLET_PASSWORD is set and otherwise raises a clear error/abort so
tests don’t silently proceed.

Comment on lines +5 to +8
- swipe:
start: "12%, 46%"
end: "7%, 77%"
duration: 300

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Swipe direction appears inverted for a “scroll down” subflow.

On Line 7, end: "7%, 77%" moves the finger downward from Line 6 (46% -> 77%), which is opposite of the upward swipe used by scroll_settings_down_1 and scroll_settings_down_3. This can make this step scroll the wrong way and destabilize downstream settings-row taps.

Suggested fix
 - swipe:
     start: "12%, 46%"
-    end: "7%, 77%"
+    end: "7%, 27%"
     duration: 300
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/scroll_settings_down_2.yaml` around lines 5 -
8, The swipe in the subflow scroll_settings_down_2 is inverted: the current
start "12%, 46%" to end "7%, 77%" moves downward (y increases) but the other
scroll_settings_down_* flows use an upward swipe; fix by making the end y
smaller than the start y so the gesture scrolls up (either swap the start/end
coordinates or adjust the end y to a value less than 46%), updating the swipe
block in scroll_settings_down_2 accordingly.

Comment on lines +96 to +114
def adb_shell(device: str | None, *args: str) -> str:
cmd = ["adb"]
if device:
cmd.extend(["-s", device])
cmd.extend(["shell", *args])
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
if result.returncode != 0:
sys.exit(f"error: adb failed: {' '.join(cmd)}\n{result.stderr}")
return result.stdout.strip().replace("\r", "")


def adb_devices() -> list[str]:
out = subprocess.run(["adb", "devices"], capture_output=True, text=True, check=True)
serials = []
for line in out.stdout.splitlines()[1:]:
parts = line.split()
if len(parts) >= 2 and parts[1] == "device":
serials.append(parts[0])
return serials

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

ADB subprocesses should have timeouts to avoid indefinite hangs.

Lines 101 and 108 execute external commands without a timeout. If adb stalls, the CLI can block forever and stall scenario automation.

Proposed fix
 def adb_shell(device: str | None, *args: str) -> str:
@@
-    result = subprocess.run(cmd, capture_output=True, text=True, check=False)
+    try:
+        result = subprocess.run(cmd, capture_output=True, text=True, check=False, timeout=15)
+    except subprocess.TimeoutExpired:
+        sys.exit(f"error: adb timed out: {' '.join(cmd)}")
+    except FileNotFoundError:
+        sys.exit("error: adb not found in PATH")
@@
 def adb_devices() -> list[str]:
-    out = subprocess.run(["adb", "devices"], capture_output=True, text=True, check=True)
+    try:
+        out = subprocess.run(["adb", "devices"], capture_output=True, text=True, check=False, timeout=15)
+    except subprocess.TimeoutExpired:
+        sys.exit("error: adb devices timed out")
+    except FileNotFoundError:
+        sys.exit("error: adb not found in PATH")
+    if out.returncode != 0:
+        sys.exit(f"error: adb devices failed:\n{out.stderr}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def adb_shell(device: str | None, *args: str) -> str:
cmd = ["adb"]
if device:
cmd.extend(["-s", device])
cmd.extend(["shell", *args])
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
if result.returncode != 0:
sys.exit(f"error: adb failed: {' '.join(cmd)}\n{result.stderr}")
return result.stdout.strip().replace("\r", "")
def adb_devices() -> list[str]:
out = subprocess.run(["adb", "devices"], capture_output=True, text=True, check=True)
serials = []
for line in out.stdout.splitlines()[1:]:
parts = line.split()
if len(parts) >= 2 and parts[1] == "device":
serials.append(parts[0])
return serials
def adb_shell(device: str | None, *args: str) -> str:
cmd = ["adb"]
if device:
cmd.extend(["-s", device])
cmd.extend(["shell", *args])
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=False, timeout=15)
except subprocess.TimeoutExpired:
sys.exit(f"error: adb timed out: {' '.join(cmd)}")
except FileNotFoundError:
sys.exit("error: adb not found in PATH")
if result.returncode != 0:
sys.exit(f"error: adb failed: {' '.join(cmd)}\n{result.stderr}")
return result.stdout.strip().replace("\r", "")
def adb_devices() -> list[str]:
try:
out = subprocess.run(["adb", "devices"], capture_output=True, text=True, check=False, timeout=15)
except subprocess.TimeoutExpired:
sys.exit("error: adb devices timed out")
except FileNotFoundError:
sys.exit("error: adb not found in PATH")
if out.returncode != 0:
sys.exit(f"error: adb devices failed:\n{out.stderr}")
serials = []
for line in out.stdout.splitlines()[1:]:
parts = line.split()
if len(parts) >= 2 and parts[1] == "device":
serials.append(parts[0])
return serials
🧰 Tools
🪛 Ruff (0.15.15)

[error] 101-101: subprocess call: check for execution of untrusted input

(S603)


[error] 108-108: Starting a process with a partial executable path

(S607)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/scripts/adapt-device.py` around lines 96 - 114, adb_shell and
adb_devices call subprocess.run without timeouts and can block indefinitely; add
a timeout argument (e.g., timeout=30 or configurable constant) to both
subprocess.run calls in the functions adb_shell and adb_devices, and catch
subprocess.TimeoutExpired to call sys.exit with a clear error message including
the command and that it timed out; keep existing capture_output, text and check
flags and ensure the error handling in adb_shell still exits with stderr when
non-zero returncode.

Comment on lines +128 to +136
def parse_wm_size(raw: str) -> tuple[int, int]:
# Physical size: 1220x2712 or Override size: ...
for line in raw.splitlines():
if "size:" in line.lower():
token = line.split(":")[-1].strip()
if "x" in token:
w, h = token.split("x", 1)
return int(w), int(h)
sys.exit(f"error: could not parse wm size:\n{raw}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Prefer Override size over Physical size when both are present.

Line 131 returns the first size: entry. adb shell wm size can report both values; using physical size when override is active produces wrong detected dimensions and therefore wrong remapping.

Proposed fix
 def parse_wm_size(raw: str) -> tuple[int, int]:
-    # Physical size: 1220x2712  or Override size: ...
-    for line in raw.splitlines():
-        if "size:" in line.lower():
-            token = line.split(":")[-1].strip()
-            if "x" in token:
-                w, h = token.split("x", 1)
-                return int(w), int(h)
+    # Prefer override size when present; otherwise fall back to physical size.
+    override_token = None
+    physical_token = None
+    for line in raw.splitlines():
+        lower = line.lower()
+        if "override size:" in lower:
+            override_token = line.split(":", 1)[-1].strip()
+        elif "physical size:" in lower or "size:" in lower:
+            physical_token = line.split(":", 1)[-1].strip()
+    token = override_token or physical_token
+    if token and "x" in token:
+        w, h = token.split("x", 1)
+        return int(w), int(h)
     sys.exit(f"error: could not parse wm size:\n{raw}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/scripts/adapt-device.py` around lines 128 - 136, The parse_wm_size
function currently returns the first "size:" line it finds which may be the
Physical size; update parse_wm_size to prefer an "Override size" line when
present by scanning all lines and selecting the token from a line that contains
"override" (case-insensitive) first, otherwise fall back to the first physical
"size" line; keep the existing parsing logic (split on ":" then "x" and int
conversion) and retain the sys.exit error behavior if no valid size is found.

Comment on lines +196 to +204
if in_chrome_zone(x, y, src_chrome) or in_chrome_zone(x, y, dst_chrome):
# Status bar, nav bar, header back — scale across full screen percentages
src_w = src["screen_width"]
src_h = src["screen_height"]
dst_w = dst["screen_width"]
dst_h = dst["screen_height"]
new_x = clamp_pct(round(x * dst_w / src_w)) if src_w else x
new_y = clamp_pct(round(y * dst_h / src_h)) if src_h else y
return new_x, new_y

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Chrome-zone mapping ignores chrome boundary deltas between profiles.

On Line 196, chrome points are remapped only by screen_width/screen_height. For profiles with equal resolution but different chrome cutoffs (e.g., nav_bar_y_min_pct 92 → 97), a source nav-bar tap like y=94 stays 94, so it no longer lands in the target nav-bar zone.

Proposed fix
-    if in_chrome_zone(x, y, src_chrome) or in_chrome_zone(x, y, dst_chrome):
-        # Status bar, nav bar, header back — scale across full screen percentages
-        src_w = src["screen_width"]
-        src_h = src["screen_height"]
-        dst_w = dst["screen_width"]
-        dst_h = dst["screen_height"]
-        new_x = clamp_pct(round(x * dst_w / src_w)) if src_w else x
-        new_y = clamp_pct(round(y * dst_h / src_h)) if src_h else y
-        return new_x, new_y
+    # Map chrome regions against their corresponding chrome ranges.
+    if y <= src_chrome["header_y_max_pct"]:
+        return (
+            map_axis(x, 0, 100, 0, 100),
+            map_axis(y, 0, src_chrome["header_y_max_pct"], 0, dst_chrome["header_y_max_pct"]),
+        )
+    if y >= src_chrome["nav_bar_y_min_pct"]:
+        return (
+            map_axis(x, 0, 100, 0, 100),
+            map_axis(y, src_chrome["nav_bar_y_min_pct"], 100, dst_chrome["nav_bar_y_min_pct"], 100),
+        )
+    if x <= src_chrome["side_x_max_pct"]:
+        return (
+            map_axis(x, 0, src_chrome["side_x_max_pct"], 0, dst_chrome["side_x_max_pct"]),
+            map_axis(y, 0, 100, 0, 100),
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/scripts/adapt-device.py` around lines 196 - 204, The chrome-zone
remap currently scales x/y by full screen sizes (src_w/src_h), ignoring
differing chrome cutoffs; update the branch in adapt-device.py that handles
in_chrome_zone to map coordinates relative to the source chrome bounds and then
project into the destination chrome bounds using src_chrome and dst_chrome
values rather than raw screen dims. Concretely, compute the source chrome region
min/max (e.g., nav/status/header bounds from src_chrome), convert x/y to a
percentage inside that source chrome region, then interpolate that percentage
into the destination chrome region using dst_chrome min/max and clamp_pct; keep
using clamp_pct and existing variables (in_chrome_zone, src_chrome, dst_chrome,
clamp_pct, src/dst screen sizes) to locate and replace the current simple
src_w/src_h scaling.

Comment thread maestro/scripts/parse-taps.py Outdated
Comment thread maestro/scripts/parse-taps.py Outdated
Move all flows and subflows into complete-tests/, remove duplicate top-level
flows/ and subflows/, and generate gitignored secret inputs via
sync-secret-subflows.py. Update paths, config, adapt-device, and README
with setup instructions for WALLET_NSEC and WALLET_PASSWORD.

Co-authored-by: Cursor <cursoragent@cursor.com>

@arkanaai arkanaai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Incremental Review — 62bf5522

Reviewed the new commit (consolidate under complete-tests/, add sync-secret-subflows.py). Not protocol-critical — test infra only.

Previous findings addressed ✅ — secrets moved to gitignored generated subflows, inputText: ${WALLET_NSEC} replaced with runFlow: _input_wallet_nsec.yaml.

New findings

1. Bug — Wrong casing in error messages (Maestro/ vs maestro/)

maestro/scripts/maestro-config.sh lines 11-12, 57, 63, 69:

echo "error: missing Maestro/config.yaml" >&2
echo "  cp Maestro/config.yaml.example Maestro/config.yaml" >&2

The directory is maestro/ (lowercase). These error messages will mislead users on case-sensitive filesystems (Linux). Same issue in:

  • maestro/scripts/sync-secret-subflows.py:63"set WALLET_NSEC in Maestro/config.yaml"
  • maestro/REPORTING.md:4-5./Maestro/studio.sh, ./Maestro/run.sh
  • maestro/complete-tests/subflows/_input_wallet_nsec.yaml.example:3Maestro/scripts/sync-secret-subflows.py

2. Low — config.yaml.example includes subflows in flows: glob

flows:
  - complete-tests/flows/**
  - complete-tests/subflows/**

Subflows aren't standalone tests. If someone runs maestro test (discovery mode), Maestro will try to execute every subflow as a top-level flow — most will fail or produce nonsense results. Should be complete-tests/flows/** only.


Everything else looks clean: path consolidation is correct, .gitignore covers the generated secret files, sync-secret-subflows.py is called by all three entrypoints, YAML escaping handles \ and " properly. 👍

🤖 Reviewed by Arkana

Use lowercase maestro/ in CLI messages and docs so Linux users see
correct paths. Drop complete-tests/subflows/** from config flows glob
so discovery mode does not run subflows as top-level tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

@arkanaai arkanaai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Incremental Review — 07516b5a

Reviewed the new commit since 62bf5522. Changes are strictly cosmetic:

  1. Path casing fixMaestro/maestro/ in error messages, docs, comments, and usage strings across 5 files. Matches the actual directory name. ✅
  2. Flows glob fix — Removed complete-tests/subflows/** from config.yaml.example flows: list. Correct — subflows are helper fragments, not standalone tests; including them would cause spurious failures. ✅

No protocol code touched. No logic changes. No security implications. LGTM.

🤖 Reviewed by Arkana

@arkanaai arkanaai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Incremental Review — 07516b5a

Reviewed the new commit since 62bf5522. Changes are strictly cosmetic:

  1. Path casing fixMaestro/maestro/ in error messages, docs, comments, and usage strings across 5 files. Matches the actual directory name. ✅
  2. Flows glob fix — Removed complete-tests/subflows/** from config.yaml.example flows: list. Correct — subflows are helper fragments, not standalone tests; including them would cause spurious failures. ✅

No protocol code touched. No logic changes. No security implications. LGTM.

🤖 Reviewed by Arkana

Remove keyboard period taps left from manual recording; inputText
already types the trailing dot in WALLET_PASSWORD.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@maestro/scripts/sync-secret-subflows.py`:
- Line 51: Replace the unnecessary f-string literal f"# AUTO-GENERATED by
sync-secret-subflows.py — do not edit" with a plain string "# AUTO-GENERATED by
sync-secret-subflows.py — do not edit" (remove the leading "f") wherever it
appears in the script to avoid the redundant f-string prefix and clarify intent.
- Line 37: The env value unescape only handles escaped quotes (env[key] =
value.replace('\\"', '"')) causing asymmetric handling with the writer which
escapes backslashes and quotes; fix by first unescaping backslashes then
unescaping quotes (i.e., apply the inverse of the writer's escaping in reverse
order) where env values are parsed so that literal "\" in secrets become single
backslashes before later re-escaping in the write routine.
- Around line 22-38: The read_config_env function currently regex-parses YAML;
replace that with PyYAML safe_load to robustly parse the file: import yaml, open
and safe_load the file contents, verify existence of the top-level "env"
mapping, return it as a dict[str,str] (coerce/str-cast values if needed), and
keep the same sys.exit behavior when the path is missing; update references to
ENV_RE (remove if unused) and ensure any unescaping logic for quoted values is
preserved when converting YAML values to strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2f480c8a-fb55-4507-9615-79beb26889e6

📥 Commits

Reviewing files that changed from the base of the PR and between 5ff4c06 and 07516b5.

📒 Files selected for processing (51)
  • maestro/.gitignore
  • maestro/README.md
  • maestro/REPORTING.md
  • maestro/complete-tests/README.md
  • maestro/complete-tests/flows/01_fresh_wallet.yaml
  • maestro/complete-tests/flows/02_startup_restore.yaml
  • maestro/complete-tests/flows/04_home_open_receive.yaml
  • maestro/complete-tests/flows/05_home_open_send.yaml
  • maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml
  • maestro/complete-tests/flows/07_home_open_activity.yaml
  • maestro/complete-tests/flows/08_home_open_settings.yaml
  • maestro/complete-tests/flows/09_home_tabs_and_asset_filters.yaml
  • maestro/complete-tests/flows/10_receive_copy_sheet.yaml
  • maestro/complete-tests/flows/11_send_form_address_field.yaml
  • maestro/complete-tests/flows/13_unlock_wallet.yaml
  • maestro/complete-tests/flows/14_home_scroll_balance_sticky.yaml
  • maestro/complete-tests/flows/15_settings_deep_reset_wallet.yaml
  • maestro/complete-tests/flows/_run_all_smoke.yaml
  • maestro/complete-tests/flows/_run_reset_then_create.yaml
  • maestro/complete-tests/subflows/_input_wallet_nsec.yaml.example
  • maestro/complete-tests/subflows/assert_home_quick_actions.yaml
  • maestro/complete-tests/subflows/assert_on_home.yaml
  • maestro/complete-tests/subflows/assert_receive_screen.yaml
  • maestro/complete-tests/subflows/assert_send_form.yaml
  • maestro/complete-tests/subflows/close_copy_sheet.yaml
  • maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml
  • maestro/complete-tests/subflows/ensure_on_wallet_home.yaml
  • maestro/complete-tests/subflows/exercise_send_form.yaml
  • maestro/complete-tests/subflows/open_receive_from_home.yaml
  • maestro/complete-tests/subflows/open_send_from_home.yaml
  • maestro/complete-tests/subflows/open_settings_from_home.yaml
  • maestro/complete-tests/subflows/open_wallet_url.yaml
  • maestro/complete-tests/subflows/receive_add_amount_keyboard.yaml
  • maestro/complete-tests/subflows/receive_copy_share_sheet.yaml
  • maestro/complete-tests/subflows/reset_to_wallet_home.yaml
  • maestro/complete-tests/subflows/scroll_home_to_top.yaml
  • maestro/complete-tests/subflows/scroll_quick_actions_into_view.yaml
  • maestro/complete-tests/subflows/wait_1s.yaml
  • maestro/complete-tests/subflows/wait_2s.yaml
  • maestro/complete-tests/subflows/wait_3s.yaml
  • maestro/complete-tests/subflows/wait_4s.yaml
  • maestro/complete-tests/subflows/wait_5s.yaml
  • maestro/complete-tests/subflows/wait_wallet_ready_after_create.yaml
  • maestro/config.yaml.example
  • maestro/run.sh
  • maestro/scripts/adapt-device.py
  • maestro/scripts/maestro-config.sh
  • maestro/scripts/parse-taps.py
  • maestro/scripts/run-scenario.sh
  • maestro/scripts/sync-secret-subflows.py
  • maestro/studio.sh
💤 Files with no reviewable changes (8)
  • maestro/complete-tests/subflows/wait_2s.yaml
  • maestro/complete-tests/subflows/open_wallet_url.yaml
  • maestro/complete-tests/subflows/wait_5s.yaml
  • maestro/complete-tests/subflows/wait_4s.yaml
  • maestro/complete-tests/subflows/wait_3s.yaml
  • maestro/complete-tests/subflows/wait_1s.yaml
  • maestro/config.yaml.example
  • maestro/scripts/adapt-device.py
✅ Files skipped from review due to trivial changes (7)
  • maestro/complete-tests/subflows/_input_wallet_nsec.yaml.example
  • maestro/complete-tests/subflows/assert_send_form.yaml
  • maestro/complete-tests/subflows/open_send_from_home.yaml
  • maestro/complete-tests/subflows/scroll_home_to_top.yaml
  • maestro/README.md
  • maestro/complete-tests/subflows/assert_receive_screen.yaml
  • maestro/complete-tests/flows/10_receive_copy_sheet.yaml
🚧 Files skipped from review as they are similar to previous changes (29)
  • maestro/REPORTING.md
  • maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml
  • maestro/complete-tests/subflows/assert_on_home.yaml
  • maestro/complete-tests/flows/11_send_form_address_field.yaml
  • maestro/complete-tests/flows/07_home_open_activity.yaml
  • maestro/complete-tests/subflows/assert_home_quick_actions.yaml
  • maestro/complete-tests/subflows/scroll_quick_actions_into_view.yaml
  • maestro/complete-tests/subflows/receive_copy_share_sheet.yaml
  • maestro/complete-tests/subflows/receive_add_amount_keyboard.yaml
  • maestro/.gitignore
  • maestro/complete-tests/subflows/close_copy_sheet.yaml
  • maestro/complete-tests/flows/_run_reset_then_create.yaml
  • maestro/complete-tests/flows/09_home_tabs_and_asset_filters.yaml
  • maestro/complete-tests/flows/05_home_open_send.yaml
  • maestro/complete-tests/flows/_run_all_smoke.yaml
  • maestro/scripts/maestro-config.sh
  • maestro/complete-tests/subflows/open_receive_from_home.yaml
  • maestro/complete-tests/subflows/ensure_on_wallet_home.yaml
  • maestro/complete-tests/subflows/open_settings_from_home.yaml
  • maestro/complete-tests/subflows/exercise_send_form.yaml
  • maestro/complete-tests/flows/08_home_open_settings.yaml
  • maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml
  • maestro/complete-tests/subflows/reset_to_wallet_home.yaml
  • maestro/complete-tests/subflows/wait_wallet_ready_after_create.yaml
  • maestro/complete-tests/flows/13_unlock_wallet.yaml
  • maestro/scripts/run-scenario.sh
  • maestro/complete-tests/flows/14_home_scroll_balance_sticky.yaml
  • maestro/complete-tests/flows/15_settings_deep_reset_wallet.yaml
  • maestro/scripts/parse-taps.py

Comment on lines +22 to +38
def read_config_env(path: Path) -> dict[str, str]:
if not path.is_file():
sys.exit(f"error: missing {path} — cp config.yaml.example config.yaml")
env: dict[str, str] = {}
in_env = False
for line in path.read_text().splitlines():
if line.strip() == "env:":
in_env = True
continue
if in_env:
if line and not line.startswith(" "):
break
m = ENV_RE.match(line)
if m:
key, value = m.groups()
env[key] = value.replace('\\"', '"')
return env

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Prefer PyYAML library over manual regex parsing.

The script manually parses YAML using regex instead of using a proper YAML library. Based on the PR objectives, PyYAML is already a project dependency (mentioned for adapt-device.py). Manual parsing is fragile and won't handle YAML edge cases (unquoted values, comments, multiline strings, indentation variations).

📚 Recommended refactor to use PyYAML
+import yaml
+
 from pathlib import Path

 MAESTRO_DIR = Path(__file__).resolve().parents[1]
 CONFIG = MAESTRO_DIR / "config.yaml"
 SUBFLOWS = MAESTRO_DIR / "complete-tests" / "subflows"

-ENV_RE = re.compile(r'^  ([A-Z_]+): *"(.*)"\s*$')
-

 def read_config_env(path: Path) -> dict[str, str]:
     if not path.is_file():
         sys.exit(f"error: missing {path} — cp config.yaml.example config.yaml")
-    env: dict[str, str] = {}
-    in_env = False
-    for line in path.read_text().splitlines():
-        if line.strip() == "env:":
-            in_env = True
-            continue
-        if in_env:
-            if line and not line.startswith("  "):
-                break
-            m = ENV_RE.match(line)
-            if m:
-                key, value = m.groups()
-                env[key] = value.replace('\\"', '"')
-    return env
+    config = yaml.safe_load(path.read_text())
+    return config.get("env", {})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/scripts/sync-secret-subflows.py` around lines 22 - 38, The
read_config_env function currently regex-parses YAML; replace that with PyYAML
safe_load to robustly parse the file: import yaml, open and safe_load the file
contents, verify existence of the top-level "env" mapping, return it as a
dict[str,str] (coerce/str-cast values if needed), and keep the same sys.exit
behavior when the path is missing; update references to ENV_RE (remove if
unused) and ensure any unescaping logic for quoted values is preserved when
converting YAML values to strings.

Comment thread maestro/scripts/sync-secret-subflows.py Outdated
Comment thread maestro/scripts/sync-secret-subflows.py Outdated

@arkanaai arkanaai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Incremental Review — a9b2df05

Single file changed: maestro/complete-tests/flows/13_unlock_wallet.yaml (+4 −10)

Removes three now-redundant tapOn gestures that were manually tapping the period key on the soft keyboard. Since inputText in the _input_wallet_password subflow already includes the trailing ., these taps were producing a double-period.

Verdict: Clean fix. No protocol code touched. No concerns.

🤖 Reviewed by Arkana

Harden sync-secret-subflows and parse-taps parsing, validate screen
dimensions in record-taps, assert Coming Soon on swap smoke, broaden
clipboard permission guards for more locales, and document locked-wallet
password requirements for smoke scenarios.

Co-authored-by: Cursor <cursoragent@cursor.com>

@arkanaai arkanaai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Incremental Review — 16f4115e

Reviewed the delta from a9b2df05. This commit addresses prior CodeRabbit feedback — all changes are test tooling, no protocol-level code.

Changes reviewed

File Verdict
maestro/scripts/sync-secret-subflows.py unescape_yaml_double_quoted correctly inverts yaml_double_quoted (backslashes first, then quotes). F-string lint fix is correct.
maestro/scripts/parse-taps.py try/except ValueError around int() parsing is a proper fix. F-string lint fix correct.
maestro/record-taps.sh:23-26 ✅ Screen dimension validation prevents garbage values propagating to parse-taps.py.
maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml ✅ Adding assertVisible: "Coming Soon" before dismiss tap gives the test an actual assertion instead of blindly tapping.
maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml ✅ Locale guards for IT/ES/DE clipboard dialogs. See nit below.
maestro/README.md, complete-tests/README.md ✅ Good docs on locked-wallet/empty-password edge case.
maestro/config.yaml.example ✅ Comment clarification.

Nit (non-blocking)

dismiss_clipboard_permission.yaml — The five locale blocks (lines 5-128) are ~25 lines each with identical tap targets, differing only in the when.visible regex. Consider extracting the tap sequence into a shared subflow and calling it from each guard, or collapsing into a single when.visible: ".*([Cc]lipboard|[Aa]ppunti|portapapeles|Zwischenablage|wants to see).*" block. Not blocking — it's test tooling and correctness is fine as-is.

No protocol-critical changes. LGTM.

🤖 Reviewed by Arkana

@arkanaai arkanaai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arkana Incremental Review — 16f4115e

Reviewed the delta from a9b2df05. This commit addresses prior CodeRabbit feedback — all changes are test tooling, no protocol-level code.

Changes reviewed

File Verdict
maestro/scripts/sync-secret-subflows.py unescape_yaml_double_quoted correctly inverts yaml_double_quoted (backslashes first, then quotes). F-string lint fix is correct.
maestro/scripts/parse-taps.py try/except ValueError around int() parsing is a proper fix. F-string lint fix correct.
maestro/record-taps.sh:23-26 ✅ Screen dimension validation prevents garbage values propagating to parse-taps.py.
maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml ✅ Adding assertVisible: "Coming Soon" before dismiss tap gives the test an actual assertion instead of blindly tapping.
maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml ✅ Locale guards for IT/ES/DE clipboard dialogs. See nit below.
maestro/README.md, complete-tests/README.md ✅ Good docs on locked-wallet/empty-password edge case.
maestro/config.yaml.example ✅ Comment clarification.

Nit (non-blocking)

dismiss_clipboard_permission.yaml — The five locale blocks (lines 5-128) are ~25 lines each with identical tap targets, differing only in the when.visible regex. Consider extracting the tap sequence into a shared subflow and calling it from each guard, or collapsing into a single when.visible: ".*([Cc]lipboard|[Aa]ppunti|portapapeles|Zwischenablage|wants to see).*" block. Not blocking — it's test tooling and correctness is fine as-is.

No protocol-critical changes. LGTM.

🤖 Reviewed by Arkana

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml (1)

5-128: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Multiple blocks execute for a single permission dialog.

When a Chrome clipboard permission dialog contains text matching multiple regex patterns (e.g., "Chrome wants to see clipboard" matches both ".*wants to see.*" at line 7 and ".*[Cc]lipboard.*" at line 32), Maestro executes all matching runFlow blocks in sequence. This causes:

  1. Redundant dismissal attempts — each matched block repeats the full tap sequence on an already-dismissed dialog
  2. Unnecessary delays — each matched block adds 1+ seconds via wait_1s.yaml
  3. Fragility — tapping on a dismissed dialog's remnants may interact with the underlying screen

Consolidate into a single runFlow block with a combined regex guard that matches any locale variant:

- runFlow:
    when:
      visible: ".*(?:wants to see|[Cc]lipboard|[Aa]ppunti|portapapeles|Zwischenablage).*"
    commands:
      # (single unified tap sequence here)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml` around
lines 5 - 128, Multiple overlapping runFlow blocks cause repeated dismissal
attempts and delays; consolidate them into one runFlow block by replacing the
separate entries (the runFlow blocks with when.visible patterns ".*wants to
see.*", ".*[Cc]lipboard.*", ".*[Aa]ppunti.*", ".*portapapeles.*",
".*Zwischenablage.*") with a single runFlow that uses a combined regex in
when.visible (e.g., ".*(?:wants to
see|[Cc]lipboard|[Aa]ppunti|portapapeles|Zwischenablage).*") and keep one
unified commands sequence (the tapOn steps and the runFlow: wait_1s.yaml) so the
dialog is dismissed only once and no duplicate waits occur.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml`:
- Around line 109-127: The tap sequence for dismissing the clipboard permission
is missing German localized button text: add tapOn entries for the German labels
(e.g., "Zulassen" and/or "Erlauben") alongside the existing "Allow" and
"Consenti" entries so the flow can match German devices before falling back to
the coordinate tap (the existing tapOn entries with text "Allow", "Consenti" and
the coordinate point "75%, 62%"); make the same additions to the other four
matching tap blocks to keep all locales consistent.
- Around line 84-102: The Spanish "Allow" label is missing from the
clipboard-permission tap sequence; add a tapOn entry with text "Permitir" and
optional: true alongside the existing tapOn entries (the same place where tapOn
text: "Allow", "Consenti", "While using the app", "Only this time", "Solo esta
vez"/coordinate tap are defined) so Spanish devices can be handled; apply the
identical addition to the other four similar permission blocks to keep behavior
consistent across all copies of the tap sequence.

---

Outside diff comments:
In `@maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml`:
- Around line 5-128: Multiple overlapping runFlow blocks cause repeated
dismissal attempts and delays; consolidate them into one runFlow block by
replacing the separate entries (the runFlow blocks with when.visible patterns
".*wants to see.*", ".*[Cc]lipboard.*", ".*[Aa]ppunti.*", ".*portapapeles.*",
".*Zwischenablage.*") with a single runFlow that uses a combined regex in
when.visible (e.g., ".*(?:wants to
see|[Cc]lipboard|[Aa]ppunti|portapapeles|Zwischenablage).*") and keep one
unified commands sequence (the tapOn steps and the runFlow: wait_1s.yaml) so the
dialog is dismissed only once and no duplicate waits occur.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 417705e2-bdc8-4de0-b414-1ac9e55b593f

📥 Commits

Reviewing files that changed from the base of the PR and between 07516b5 and 16f4115.

📒 Files selected for processing (9)
  • maestro/README.md
  • maestro/complete-tests/README.md
  • maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml
  • maestro/complete-tests/flows/13_unlock_wallet.yaml
  • maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml
  • maestro/config.yaml.example
  • maestro/record-taps.sh
  • maestro/scripts/parse-taps.py
  • maestro/scripts/sync-secret-subflows.py
✅ Files skipped from review due to trivial changes (2)
  • maestro/config.yaml.example
  • maestro/complete-tests/README.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • maestro/complete-tests/flows/06_home_swap_coming_soon_sheet.yaml
  • maestro/record-taps.sh
  • maestro/README.md
  • maestro/complete-tests/flows/13_unlock_wallet.yaml
  • maestro/scripts/sync-secret-subflows.py
  • maestro/scripts/parse-taps.py

Comment on lines +84 to +102
- tapOn:
text: "Allow"
optional: true
- tapOn:
text: "Consenti"
optional: true
- tapOn:
text: "While using the app"
optional: true
- tapOn:
text: "Only this time"
optional: true
- tapOn:
text: "Solo questa volta"
optional: true
- tapOn:
point: "75%, 62%"
optional: true
waitToSettleTimeoutMs: 800

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Major: Missing Spanish "Allow" button text.

The Spanish clipboard guard (line 82: ".*portapapeles.*") lacks the localized "Allow" equivalent. The tap sequence attempts only "Allow" (English, line 85) and "Consenti" (Italian, line 88), then falls back to a device-specific coordinate tap at line 100. On a Spanish-localized Android device, the button text is typically "Permitir" — add it to ensure robust dismissal:

       - tapOn:
           text: "Consenti"
           optional: true
+      - tapOn:
+          text: "Permitir"
+          optional: true

Apply the same fix to the other four blocks for consistency (even though Spanish dialogs should only match this block once consolidated).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- tapOn:
text: "Allow"
optional: true
- tapOn:
text: "Consenti"
optional: true
- tapOn:
text: "While using the app"
optional: true
- tapOn:
text: "Only this time"
optional: true
- tapOn:
text: "Solo questa volta"
optional: true
- tapOn:
point: "75%, 62%"
optional: true
waitToSettleTimeoutMs: 800
- tapOn:
text: "Allow"
optional: true
- tapOn:
text: "Consenti"
optional: true
- tapOn:
text: "Permitir"
optional: true
- tapOn:
text: "While using the app"
optional: true
- tapOn:
text: "Only this time"
optional: true
- tapOn:
text: "Solo questa volta"
optional: true
- tapOn:
point: "75%, 62%"
optional: true
waitToSettleTimeoutMs: 800
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml` around
lines 84 - 102, The Spanish "Allow" label is missing from the
clipboard-permission tap sequence; add a tapOn entry with text "Permitir" and
optional: true alongside the existing tapOn entries (the same place where tapOn
text: "Allow", "Consenti", "While using the app", "Only this time", "Solo esta
vez"/coordinate tap are defined) so Spanish devices can be handled; apply the
identical addition to the other four similar permission blocks to keep behavior
consistent across all copies of the tap sequence.

Comment on lines +109 to +127
- tapOn:
text: "Allow"
optional: true
- tapOn:
text: "Consenti"
optional: true
- tapOn:
text: "While using the app"
optional: true
- tapOn:
text: "Only this time"
optional: true
- tapOn:
text: "Solo questa volta"
optional: true
- tapOn:
point: "75%, 62%"
optional: true
waitToSettleTimeoutMs: 800

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Major: Missing German "Allow" button text.

The German clipboard guard (line 107: ".*Zwischenablage.*") lacks the localized "Allow" equivalent. The tap sequence attempts only "Allow" (English, line 110) and "Consenti" (Italian, line 113), then falls back to the coordinate tap at line 125. On a German-localized Android device, the button text is typically "Zulassen" or "Erlauben" — add it:

       - tapOn:
           text: "Consenti"
           optional: true
+      - tapOn:
+          text: "Zulassen"
+          optional: true
+      - tapOn:
+          text: "Erlauben"
+          optional: true

Apply the same fix to the other four blocks for consistency.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- tapOn:
text: "Allow"
optional: true
- tapOn:
text: "Consenti"
optional: true
- tapOn:
text: "While using the app"
optional: true
- tapOn:
text: "Only this time"
optional: true
- tapOn:
text: "Solo questa volta"
optional: true
- tapOn:
point: "75%, 62%"
optional: true
waitToSettleTimeoutMs: 800
- tapOn:
text: "Allow"
optional: true
- tapOn:
text: "Consenti"
optional: true
- tapOn:
text: "Zulassen"
optional: true
- tapOn:
text: "Erlauben"
optional: true
- tapOn:
text: "While using the app"
optional: true
- tapOn:
text: "Only this time"
optional: true
- tapOn:
text: "Solo questa volta"
optional: true
- tapOn:
point: "75%, 62%"
optional: true
waitToSettleTimeoutMs: 800
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@maestro/complete-tests/subflows/dismiss_clipboard_permission.yaml` around
lines 109 - 127, The tap sequence for dismissing the clipboard permission is
missing German localized button text: add tapOn entries for the German labels
(e.g., "Zulassen" and/or "Erlauben") alongside the existing "Allow" and
"Consenti" entries so the flow can match German devices before falling back to
the coordinate tap (the existing tapOn entries with text "Allow", "Consenti" and
the coordinate point "75%, 62%"); make the same additions to the other four
matching tap blocks to keep all locales consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant