zig-mobile-runner 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/CHANGELOG.md +484 -0
  2. package/CONTRIBUTING.md +42 -0
  3. package/FEATURES.md +112 -0
  4. package/LICENSE +21 -0
  5. package/README.md +255 -0
  6. package/SECURITY.md +34 -0
  7. package/build.zig +38 -0
  8. package/build.zig.zon +7 -0
  9. package/clients/README.md +144 -0
  10. package/clients/go/README.md +24 -0
  11. package/clients/go/examples/fake-session/main.go +93 -0
  12. package/clients/go/go.mod +3 -0
  13. package/clients/go/zmr/client.go +432 -0
  14. package/clients/kotlin/README.md +35 -0
  15. package/clients/kotlin/build.gradle.kts +35 -0
  16. package/clients/kotlin/settings.gradle.kts +15 -0
  17. package/clients/kotlin/src/main/kotlin/dev/zmr/FakeSession.kt +86 -0
  18. package/clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt +67 -0
  19. package/clients/python/README.md +29 -0
  20. package/clients/python/examples/fake_session.py +48 -0
  21. package/clients/python/pyproject.toml +13 -0
  22. package/clients/python/zmr_client.py +202 -0
  23. package/clients/rust/Cargo.lock +107 -0
  24. package/clients/rust/Cargo.toml +10 -0
  25. package/clients/rust/README.md +19 -0
  26. package/clients/rust/examples/fake_session.rs +70 -0
  27. package/clients/rust/src/lib.rs +461 -0
  28. package/clients/swift/Package.swift +16 -0
  29. package/clients/swift/README.md +36 -0
  30. package/clients/swift/Sources/ZMRClient/ZMRClient.swift +114 -0
  31. package/clients/swift/Sources/ZMRFakeSession/main.swift +86 -0
  32. package/clients/typescript/README.md +34 -0
  33. package/clients/typescript/examples/fake-session.mjs +36 -0
  34. package/clients/typescript/index.d.ts +144 -0
  35. package/clients/typescript/index.mjs +192 -0
  36. package/clients/typescript/package.json +8 -0
  37. package/docs/adr/0001-agent-native-runner-boundary.md +31 -0
  38. package/docs/adr/0002-app-local-zmr-contract.md +39 -0
  39. package/docs/adr/0003-ios-simulator-xctest-shim.md +41 -0
  40. package/docs/adr/0004-benchmark-claims-and-baseline-collection.md +37 -0
  41. package/docs/adr/README.md +12 -0
  42. package/docs/ai-agents.md +156 -0
  43. package/docs/app-integration.md +316 -0
  44. package/docs/benchmarking.md +275 -0
  45. package/docs/client-installation.md +141 -0
  46. package/docs/clients.md +98 -0
  47. package/docs/config.md +175 -0
  48. package/docs/demo.md +259 -0
  49. package/docs/dsl.md +57 -0
  50. package/docs/install.md +233 -0
  51. package/docs/market-positioning.md +70 -0
  52. package/docs/npm.md +359 -0
  53. package/docs/protocol-fixtures/README.md +8 -0
  54. package/docs/protocol-fixtures/core-session.requests.jsonl +8 -0
  55. package/docs/protocol-fixtures/core-session.responses.jsonl +8 -0
  56. package/docs/protocol-versioning.md +65 -0
  57. package/docs/protocol.md +560 -0
  58. package/docs/publication.md +77 -0
  59. package/docs/release-audit.md +99 -0
  60. package/docs/release-candidate.md +111 -0
  61. package/docs/release-evidence.md +188 -0
  62. package/docs/release-notes-template.md +58 -0
  63. package/docs/roadmap.md +334 -0
  64. package/docs/scenario-authoring.md +88 -0
  65. package/docs/shipping.md +170 -0
  66. package/docs/trace-privacy.md +88 -0
  67. package/docs/troubleshooting.md +256 -0
  68. package/examples/android-app-auth-probe.json +89 -0
  69. package/examples/android-app-error-state.json +13 -0
  70. package/examples/android-app-login-smoke.json +192 -0
  71. package/examples/android-app-onboarding.json +12 -0
  72. package/examples/android-app-referral-deep-link.json +12 -0
  73. package/examples/android-shim-smoke.json +19 -0
  74. package/examples/demo-failure.json +12 -0
  75. package/examples/demo-fake.json +14 -0
  76. package/examples/ios-dev-client-open-link.json +26 -0
  77. package/examples/ios-dev-client-route-snapshot.json +24 -0
  78. package/examples/ios-shim-smoke.json +23 -0
  79. package/examples/ios-smoke.json +9 -0
  80. package/go.work +3 -0
  81. package/npm/agents.mjs +183 -0
  82. package/npm/app-config.mjs +95 -0
  83. package/npm/build-zmr.mjs +21 -0
  84. package/npm/commands.mjs +104 -0
  85. package/npm/generated-files.mjs +50 -0
  86. package/npm/index.mjs +75 -0
  87. package/npm/init-app.mjs +80 -0
  88. package/npm/package-scripts.mjs +72 -0
  89. package/npm/postinstall.mjs +21 -0
  90. package/npm/scaffold.mjs +179 -0
  91. package/npm/scenarios.mjs +93 -0
  92. package/npm/setup.mjs +69 -0
  93. package/npm/wizard.mjs +117 -0
  94. package/npm/zmr.mjs +23 -0
  95. package/package.json +114 -0
  96. package/prebuilds/darwin-arm64/zmr +0 -0
  97. package/prebuilds/darwin-x64/zmr +0 -0
  98. package/prebuilds/linux-arm64/zmr +0 -0
  99. package/prebuilds/linux-x64/zmr +0 -0
  100. package/schemas/README.md +26 -0
  101. package/schemas/action-result.schema.json +27 -0
  102. package/schemas/capabilities-output.schema.json +98 -0
  103. package/schemas/devices-output.schema.json +25 -0
  104. package/schemas/doctor-output.schema.json +51 -0
  105. package/schemas/explain-output.schema.json +51 -0
  106. package/schemas/import-output.schema.json +23 -0
  107. package/schemas/init-output.schema.json +71 -0
  108. package/schemas/json-rpc.schema.json +55 -0
  109. package/schemas/release-manifest.schema.json +43 -0
  110. package/schemas/release-readiness-output.schema.json +127 -0
  111. package/schemas/run-output.schema.json +43 -0
  112. package/schemas/scenario.schema.json +128 -0
  113. package/schemas/schemas-output.schema.json +26 -0
  114. package/schemas/semantic-snapshot.schema.json +116 -0
  115. package/schemas/snapshot.schema.json +60 -0
  116. package/schemas/trace-event.schema.json +14 -0
  117. package/schemas/trace-manifest.schema.json +59 -0
  118. package/schemas/validate-output.schema.json +42 -0
  119. package/schemas/version-output.schema.json +23 -0
  120. package/schemas/zmr-config.schema.json +75 -0
  121. package/scripts/android-emulator.sh +126 -0
  122. package/scripts/assert-ios-physical-ready.sh +213 -0
  123. package/scripts/benchmark-command.sh +307 -0
  124. package/scripts/benchmark.sh +359 -0
  125. package/scripts/benchmark_gate.py +117 -0
  126. package/scripts/benchmark_result_row.py +88 -0
  127. package/scripts/compare-benchmarks.py +288 -0
  128. package/scripts/create-android-demo-app.sh +342 -0
  129. package/scripts/create-ios-demo-app.sh +261 -0
  130. package/scripts/demo-android-real.sh +232 -0
  131. package/scripts/demo-ios-real.sh +270 -0
  132. package/scripts/demo.sh +464 -0
  133. package/scripts/device-matrix.sh +338 -0
  134. package/scripts/ensure-ios-shim-target.rb +237 -0
  135. package/scripts/install-android-shim.sh +281 -0
  136. package/scripts/install-ios-shim.sh +589 -0
  137. package/scripts/pilot-gate.sh +560 -0
  138. package/scripts/release-readiness.py +838 -0
  139. package/scripts/release-readiness.sh +91 -0
  140. package/scripts/run-android-pilot.sh +561 -0
  141. package/scripts/run-ios-pilot.sh +509 -0
  142. package/shims/android/README.md +21 -0
  143. package/shims/android/ZMRShimInstrumentedTest.java +152 -0
  144. package/shims/android/protocol.md +18 -0
  145. package/shims/ios/README.md +50 -0
  146. package/shims/ios/ZMRShim.swift +110 -0
  147. package/shims/ios/ZMRShimUITestCase.swift +475 -0
  148. package/shims/ios/protocol.md +74 -0
  149. package/skills/zmr-mobile-testing/SKILL.md +127 -0
  150. package/src/android.zig +344 -0
  151. package/src/android_device_info.zig +99 -0
  152. package/src/android_emulator.zig +154 -0
  153. package/src/android_screen_recording.zig +112 -0
  154. package/src/android_shell.zig +112 -0
  155. package/src/bundle.zig +124 -0
  156. package/src/bundle_redaction.zig +272 -0
  157. package/src/bundle_tar.zig +123 -0
  158. package/src/cli_devices.zig +97 -0
  159. package/src/cli_doctor.zig +114 -0
  160. package/src/cli_import.zig +70 -0
  161. package/src/cli_info.zig +39 -0
  162. package/src/cli_init.zig +72 -0
  163. package/src/cli_output.zig +467 -0
  164. package/src/cli_run.zig +259 -0
  165. package/src/cli_serve.zig +287 -0
  166. package/src/cli_trace.zig +111 -0
  167. package/src/cli_validate.zig +41 -0
  168. package/src/command.zig +211 -0
  169. package/src/config.zig +305 -0
  170. package/src/config_diagnostics.zig +212 -0
  171. package/src/config_paths.zig +49 -0
  172. package/src/device_registry.zig +37 -0
  173. package/src/doctor.zig +412 -0
  174. package/src/doctor_hints.zig +52 -0
  175. package/src/errors.zig +55 -0
  176. package/src/fake_device.zig +163 -0
  177. package/src/health.zig +28 -0
  178. package/src/importer.zig +343 -0
  179. package/src/importer_json.zig +100 -0
  180. package/src/importer_model.zig +103 -0
  181. package/src/ios.zig +399 -0
  182. package/src/ios_devices.zig +219 -0
  183. package/src/ios_lifecycle.zig +72 -0
  184. package/src/ios_shim.zig +242 -0
  185. package/src/ios_snapshot.zig +20 -0
  186. package/src/json_fields.zig +80 -0
  187. package/src/json_rpc.zig +150 -0
  188. package/src/json_rpc_methods.zig +318 -0
  189. package/src/json_rpc_observation.zig +31 -0
  190. package/src/json_rpc_params.zig +52 -0
  191. package/src/json_rpc_protocol.zig +110 -0
  192. package/src/json_rpc_trace.zig +73 -0
  193. package/src/main.zig +135 -0
  194. package/src/mcp.zig +234 -0
  195. package/src/mcp_protocol.zig +64 -0
  196. package/src/mcp_trace.zig +83 -0
  197. package/src/report.zig +346 -0
  198. package/src/report_html.zig +63 -0
  199. package/src/report_values.zig +27 -0
  200. package/src/run_options.zig +152 -0
  201. package/src/runner.zig +280 -0
  202. package/src/runner_actions.zig +109 -0
  203. package/src/runner_config.zig +6 -0
  204. package/src/runner_diagnostics.zig +268 -0
  205. package/src/runner_events.zig +170 -0
  206. package/src/runner_native.zig +88 -0
  207. package/src/runner_waits.zig +300 -0
  208. package/src/scaffold.zig +472 -0
  209. package/src/scenario.zig +346 -0
  210. package/src/scenario_fields.zig +50 -0
  211. package/src/schema_registry.zig +53 -0
  212. package/src/selector.zig +84 -0
  213. package/src/semantic.zig +171 -0
  214. package/src/trace.zig +315 -0
  215. package/src/trace_json.zig +340 -0
  216. package/src/trace_summary.zig +218 -0
  217. package/src/trace_summary_diagnostic.zig +202 -0
  218. package/src/types.zig +120 -0
  219. package/src/uiautomator.zig +164 -0
  220. package/src/validation.zig +187 -0
  221. package/src/version.zig +22 -0
  222. package/viewer/app.js +373 -0
  223. package/viewer/index.html +126 -0
  224. package/viewer/parser.js +233 -0
  225. package/viewer/styles.css +585 -0
@@ -0,0 +1,156 @@
1
+ # AI Agent Guide
2
+
3
+ ZMR is built for external agents. The runner provides device state, typed
4
+ actions, waits, assertions, and trace export; the agent decides the next step.
5
+
6
+ ## Agent Setup Loop
7
+
8
+ Start inside the app checkout:
9
+
10
+ ```bash
11
+ zmr doctor --json --config .zmr/config.json
12
+ zmr validate --json .zmr/android-smoke.json
13
+ zmr validate --json .zmr/ios-smoke.json
14
+ zmr schemas --json
15
+ ```
16
+
17
+ Use `zmr doctor --strict --json` in CI or setup flows that should fail on any
18
+ warning. Prefer JSON output for automation because it includes stable error
19
+ codes, field paths, and remediation hints.
20
+
21
+ ## Live JSON-RPC Session
22
+
23
+ Agents should prefer `zmr serve` for interactive work:
24
+
25
+ ```bash
26
+ zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent
27
+ ```
28
+
29
+ Recommended flow:
30
+
31
+ 1. Call `runner.capabilities` and check protocol/platform support.
32
+ 2. Call `session.create`.
33
+ 3. Call `observe.semanticSnapshot` when choosing the next action, or
34
+ `observe.snapshot` when raw adapter details are needed.
35
+ 4. Choose one typed action or assertion.
36
+ 5. Let ZMR settle, then observe again.
37
+ 6. Poll `trace.events` during long runs.
38
+ 7. Call `trace.export` with `redact: true` before sharing artifacts.
39
+ 8. Call `session.close`.
40
+
41
+ Do not parse screenshots or terminal text when the same fact is available from
42
+ snapshot nodes, action results, CLI JSON, or trace events.
43
+
44
+ If `zmr run --json` returns `status: "partial"`, inspect `partialFailure`.
45
+ For iOS visual captures, `artifactStatus: "captured"` with
46
+ `semanticStatus: "failed"` means screenshot proof exists but accessibility or
47
+ XCTest hierarchy extraction failed. Use `zmr explain --json <trace-dir>` for
48
+ the same diagnostic shape after the run.
49
+
50
+ ## MCP Session
51
+
52
+ Agents that support the Model Context Protocol can use ZMR directly as a local
53
+ stdio MCP server:
54
+
55
+ ```bash
56
+ zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent
57
+ ```
58
+
59
+ The MCP server exposes mobile-specific tools:
60
+
61
+ - `snapshot`: raw ZMR observation JSON
62
+ - `semantic_snapshot`: normalized roles, names, selectors, bounds, and
63
+ recommended actions
64
+ - `tap`, `type`, `press_back`, and `open_link`
65
+ - `wait_visible`
66
+ - `trace_events` and `trace_export`
67
+
68
+ Prefer `semantic_snapshot` for action planning. It avoids forcing an agent to
69
+ infer intent from platform-specific Android/UI Automator or XCTest class names.
70
+
71
+ ## Scenario File Workflow
72
+
73
+ For repeatable tests, generate or edit `.zmr/*.json` scenarios:
74
+
75
+ ```bash
76
+ zmr validate --json .zmr/login-smoke.json
77
+ zmr run .zmr/login-smoke.json --json --trace-dir traces/zmr-login-smoke
78
+ zmr explain --json traces/zmr-login-smoke
79
+ zmr export traces/zmr-login-smoke --out traces/zmr-login-smoke-redacted.zmrtrace --redact
80
+ ```
81
+
82
+ Use stable selectors in this order when available:
83
+
84
+ - app accessibility identifiers or resource ids
85
+ - content descriptions or accessibility labels
86
+ - exact visible text for stable product copy
87
+ - `textContains` only when the visible text legitimately varies
88
+ - coordinate actions only as a last resort
89
+
90
+ Use `waitAny` for screens with legitimate branches, and `whenVisible` for
91
+ optional platform or dev-client screens. Keep credentials and app-private data
92
+ in the app repository or environment, not in public scenarios.
93
+
94
+ ## Failure Triage
95
+
96
+ When a run fails, inspect:
97
+
98
+ - `zmr run --json` terminal summary
99
+ - `zmr explain --json <trace-dir>`
100
+ - `trace.json`
101
+ - `events.jsonl`
102
+ - the last snapshot JSON
103
+ - the trace viewer report from `zmr report`
104
+
105
+ Selector failures include active app context, visible text, disabled/hidden or
106
+ offscreen exact candidates, and nearest text matches when available. Treat
107
+ those diagnostics as the source of truth before changing a selector.
108
+
109
+ ## Benchmarking
110
+
111
+ Use ZMR repeated runs first:
112
+
113
+ ```bash
114
+ zmr-benchmark --zmr .zmr/android-smoke.json --platform android --device emulator-5554 --app-id com.example.mobiletest --app-build <build-id-or-artifact> --runs 20 --trace-root traces/zmr-android-reliability --results traces/bench-comparison/results.jsonl --replace --min-pass-rate 100 --max-failures 0
115
+ ```
116
+
117
+ For a fair comparison with an app-local baseline command, collect normalized
118
+ rows and compare them:
119
+
120
+ ```bash
121
+ zmr-benchmark-command --tool baseline --platform android --device emulator-5554 --app-id com.example.mobiletest --scenario .zmr/android-smoke.json --app-build <build-id-or-artifact> --runs 20 --trace-root traces/baseline --results traces/bench-comparison/results.jsonl -- <baseline command>
122
+ zmr-compare-benchmarks --results traces/bench-comparison/results.jsonl --candidate zmr --baseline baseline --min-candidate-pass-rate 100 --max-candidate-failures 0 --min-mean-speedup 1.25 --min-p95-speedup 1.25 --out traces/bench-comparison/comparison.md --evidence-out traces/bench-comparison/evidence.jsonl
123
+ ```
124
+
125
+ Only publish claims when the candidate and baseline exercise equivalent app
126
+ paths under the same device state. Market-claim evidence must show the same
127
+ benchmark context: `platform`, `device`, `appId`, `scenario`, and `appBuild`.
128
+ It must also include at least 20 candidate rows and at least 20 baseline rows.
129
+
130
+ ## Release Claims
131
+
132
+ Before saying a release, production rollout, or market comparison is ready,
133
+ evaluate the collected evidence:
134
+
135
+ ```bash
136
+ zmr-release-readiness --json \
137
+ --evidence traces/release-candidate/<run>/evidence.jsonl \
138
+ --target dev-preview
139
+ ```
140
+
141
+ Use `satisfied` for proven requirements and `blocked`, `missing`,
142
+ `insufficient`, `failed`, and `planned` for remaining work. Use
143
+ `recommendedWording` for the human-facing status and keep
144
+ `claimLimitations` intact; never upgrade a dev-preview result into a
145
+ production-stable or competitive claim. When blocked, run
146
+ `nextSteps[].commands` in order and use `nextSteps[].covers` to map each
147
+ command back to the blocked requirements it resolves.
148
+
149
+ ## Safety Rules
150
+
151
+ - Run `tests/public-safety-test.sh` before publishing docs, examples, or traces.
152
+ - Do not commit app-private traces, screenshots, credentials, tokens, bundle
153
+ identifiers, or private app names.
154
+ - Prefer `zmr export --redact`; add `--omit-screenshots` for public bundles
155
+ when visual artifacts may contain sensitive data.
156
+ - Keep app-local state under `.zmr/` and generated run output under `traces/`.
@@ -0,0 +1,316 @@
1
+ # App Integration
2
+
3
+ ZMR is intentionally a separate runner. A mobile app repo does not need to vendor ZMR, but it should expose a small, stable test surface so agents can drive the app deterministically.
4
+
5
+ Most app teams should install ZMR as a dev dependency:
6
+
7
+ ```bash
8
+ npm install --save-dev zig-mobile-runner
9
+ npx zmr-wizard --app-id com.example.mobiletest --package-json
10
+ ```
11
+
12
+ That keeps scenarios and app scripts in the app repo while the runner remains versioned through npm.
13
+ For Expo development builds, add `--expo-dev-client-scheme <scheme>` to scaffold
14
+ Android and iOS open-link smoke scenarios that load Metro before selector
15
+ assertions run.
16
+
17
+ ## What The App Provides
18
+
19
+ For Android:
20
+
21
+ - A debug/test APK.
22
+ - A stable application id, for example `com.example.mobiletest`.
23
+ - Optional deep links for direct navigation into test states.
24
+ - Accessibility labels, text, or resource ids for important controls.
25
+ - A test server or local dev server when the app requires one.
26
+ - Optional Android instrumentation shim command for faster hierarchy and
27
+ selector-grade actions.
28
+
29
+ Create the app-local Android shim command from the ZMR package or checkout:
30
+
31
+ ```bash
32
+ npx zmr-install-android-shim \
33
+ --app-root . \
34
+ --test-package com.example.mobiletest.test \
35
+ --runner androidx.test.runner.AndroidJUnitRunner \
36
+ --android-module android/app \
37
+ --gradle-file android/app/build.gradle
38
+ ```
39
+
40
+ With `--android-module`, the installer copies the shim into the app module's
41
+ standard `src/androidTest/java/dev/zmr/shim/` tree. With `--gradle-file`, it
42
+ appends guarded Gradle blocks once for `testInstrumentationRunner` and AndroidX
43
+ Test/UI Automator dependencies. If the Gradle file already declares a custom
44
+ `testInstrumentationRunner` and `--runner` is omitted, the generated shim command
45
+ uses the existing runner. Omit those flags when you prefer to wire source and
46
+ dependencies yourself from the generated `.zmr/ZMRShimInstrumentedTest.java`.
47
+ The generated
48
+ `.zmr/android-shim` executable is the value to pass to `--android-shim` or
49
+ `tools.androidShimPath`.
50
+
51
+ For iOS:
52
+
53
+ - A simulator `.app` build.
54
+ - A stable bundle id, for example `com.example.mobiletest`.
55
+ - Optional deep links for direct navigation into test states.
56
+ - Accessibility labels for important controls.
57
+ - Optional simulator XCTest/XCUIAutomation shim command for hierarchy and
58
+ selector-grade actions.
59
+
60
+ Create the app-local shim command from the ZMR package or checkout:
61
+
62
+ ```bash
63
+ npx zmr-install-ios-shim \
64
+ --app-root . \
65
+ --scheme SampleUITests \
66
+ --test-target SampleUITests \
67
+ --workspace ios/Sample.xcworkspace \
68
+ --app-target SampleApp \
69
+ --derived-data-path ios/build/ZMRDerivedData \
70
+ --bundle-id com.example.mobiletest \
71
+ --patch-xcodeproj
72
+ ```
73
+
74
+ Run `.zmr/ensure-ios-shim-target.sh` to create/update the UI test target, add
75
+ the generated `.zmr/ZMRShimUITestCase.swift` and
76
+ `.zmr/shims/ios/ZMRShim.swift` files, configure
77
+ `.zmr/ZMRShimUITests-Info.plist`, and write a shared scheme. The helper uses the
78
+ Ruby `xcodeproj` gem. With `--workspace`, it resolves the referenced
79
+ `.xcodeproj` automatically when there is one project, or when exactly one
80
+ project contains `--app-target`, or when `--bundle-id` disambiguates matching
81
+ app targets. Pass `--project ios/Sample.xcodeproj` explicitly for
82
+ still-ambiguous multi-project workspaces or project-only apps.
83
+
84
+ The generated `.zmr/ios-shim` executable is written into
85
+ `tools.iosShimPath` in `.zmr/config.json`, and can still be passed explicitly
86
+ with `--ios-shim`. It caches `build-for-testing` output and uses
87
+ `test-without-building` for selector commands through `.zmr/ios-shim-state/`.
88
+ Set `ZMR_IOS_SHIM_FORCE_REBUILD=1` after app-side target changes, or
89
+ `ZMR_IOS_SHIM_ONESHOT=1` when you need to debug the slower cold-start path.
90
+
91
+ ## Recommended App Repo Layout
92
+
93
+ The exact layout is app-specific, but this shape works well:
94
+
95
+ ```text
96
+ mobile-app/
97
+ android/app/build/outputs/apk/debug/app-debug.apk
98
+ build/Debug-iphonesimulator/Sample.app
99
+ .zmr/
100
+ config.json
101
+ android-auth-probe.json
102
+ android-login-smoke.json
103
+ ios-smoke.json
104
+ ```
105
+
106
+ Keep app-owned scenarios and ZMR defaults in `.zmr/` when they are app-specific. Keep generic examples in the ZMR repo. ZMR auto-discovers `.zmr/config.json` from the app repo; explicit CLI flags still override config defaults.
107
+
108
+ ## Android App Pilot Command
109
+
110
+ ```bash
111
+ /path/to/zig-mobile-runner/scripts/run-android-pilot.sh \
112
+ --app-root /path/to/mobile-app \
113
+ --app-id com.example.mobiletest \
114
+ --device emulator-5554
115
+ ```
116
+
117
+ Use a saved emulator snapshot for repeatability:
118
+
119
+ ```bash
120
+ /path/to/zig-mobile-runner/scripts/run-android-pilot.sh \
121
+ --app-root /path/to/mobile-app \
122
+ --app-id com.example.mobiletest \
123
+ --device emulator-5554 \
124
+ --avd Small_Phone \
125
+ --reset-emulator \
126
+ --restore-snapshot zmr-clean \
127
+ --screen-record
128
+ ```
129
+
130
+ `--screen-record` writes `screenrecord.mp4` under the pilot trace root. For
131
+ direct traced runs, use `zmr run --android-avd Small_Phone
132
+ --create-avd-if-missing --avd-system-image
133
+ 'system-images;android-35;google_apis;arm64-v8a' --avd-device pixel_6
134
+ --restore-snapshot zmr-clean --wait-emulator --screen-record`, or set the
135
+ equivalent `android.avdName`, `android.createAvdIfMissing`,
136
+ `android.avdSystemImage`, `android.avdDeviceProfile`,
137
+ `android.restoreSnapshot`, `android.waitReady`, and
138
+ `artifacts.screenRecording` values in `.zmr/config.json`. Treat recordings like
139
+ screenshots: keep them local or share only when the app state is safe.
140
+
141
+ The Android wrapper expects the default APK path under the app root. Override it when needed:
142
+
143
+ ```bash
144
+ /path/to/zig-mobile-runner/scripts/run-android-pilot.sh \
145
+ --app-root /path/to/mobile-app \
146
+ --apk /path/to/app-debug.apk \
147
+ --device emulator-5554
148
+ ```
149
+
150
+ ## Public Android Demo Command
151
+
152
+ For a generic public Android app:
153
+
154
+ ```bash
155
+ npx zmr-demo-android --out /tmp/zmr-android-demo --device emulator-5554 --avd <avd-name>
156
+ ```
157
+
158
+ This writes a signed debug APK plus `.zmr/android-smoke.json` without Gradle or
159
+ network access, then installs and runs it on the requested Android target. For
160
+ manual inspection or customization:
161
+
162
+ ```bash
163
+ npx zmr-create-android-demo-app --out /tmp/zmr-android-demo
164
+ adb install -r /tmp/zmr-android-demo/build/app-debug.apk
165
+ /path/to/zig-mobile-runner/zig-out/bin/zmr run /tmp/zmr-android-demo/.zmr/android-smoke.json \
166
+ --device emulator-5554 \
167
+ --app-id com.example.mobiletest \
168
+ --trace-dir /tmp/zmr-android-demo/traces/android-demo
169
+ ```
170
+
171
+ Use this path to prove local Android install, launch, selector action, typing,
172
+ snapshot, and trace capture before wiring ZMR into a private app.
173
+
174
+ ## iOS Demo Command
175
+
176
+ For a generic public demo app with the shim already installed:
177
+
178
+ ```bash
179
+ npx zmr-demo-ios --out /tmp/zmr-ios-demo --device booted
180
+ ```
181
+
182
+ That command creates the demo app, boots an available simulator when needed,
183
+ builds it, runs the iOS pilot, and writes redacted trace bundles. To inspect or
184
+ customize the generated app before running the pilot manually:
185
+
186
+ ```bash
187
+ npx zmr-create-ios-demo-app --out /tmp/zmr-ios-demo
188
+ cd /tmp/zmr-ios-demo
189
+ xcodebuild -project ios/ZMRDemo.xcodeproj -scheme ZMRDemo -destination 'generic/platform=iOS Simulator' -derivedDataPath DerivedData build
190
+ ```
191
+
192
+ Then boot a simulator and run:
193
+
194
+ ```bash
195
+ /path/to/zig-mobile-runner/scripts/run-ios-pilot.sh \
196
+ --app-root /tmp/zmr-ios-demo \
197
+ --app-path /tmp/zmr-ios-demo/DerivedData/Build/Products/Debug-iphonesimulator/ZMRDemo.app \
198
+ --app-id com.example.mobiletest \
199
+ --device booted \
200
+ --ios-shim /tmp/zmr-ios-demo/.zmr/ios-shim
201
+ ```
202
+
203
+ Build the app for an iOS simulator, boot a simulator, then run:
204
+
205
+ ```bash
206
+ /path/to/zig-mobile-runner/scripts/run-ios-pilot.sh \
207
+ --app-root /path/to/mobile-app \
208
+ --app-path /path/to/mobile-app/build/Debug-iphonesimulator/Sample.app \
209
+ --app-id com.example.mobiletest \
210
+ --device booted \
211
+ --ios-shim /path/to/mobile-app/.zmr/ios-shim
212
+ ```
213
+
214
+ Without `--ios-shim`, the iOS path is a smoke demo: install, launch/open-link,
215
+ screenshot, logs, trace, report, and redacted export. With `--ios-shim`, ZMR
216
+ also runs `examples/ios-shim-smoke.json`, producing a second report and
217
+ redacted bundle for selector-grade native waits, tap, type, and bounded
218
+ snapshot actions. If a selector wait times out, ZMR records a final XCTest
219
+ snapshot when possible so reports and agents can see the active app context,
220
+ visible labels, hidden/disabled/offscreen candidates, and nearest text matches.
221
+ When the app is already running, ZMR uses the shim `appState` response as an
222
+ idempotent launch confirmation if `simctl launch` itself returns an error.
223
+
224
+ On iOS simulators, `clearState` means best-effort app uninstall by bundle id.
225
+ For physical iOS devices, lifecycle commands go through `devicectl` and
226
+ selector commands go through the same app-local XCTest shim, subject to signing,
227
+ provisioning, Developer Mode, and local Xcode availability. Screenshot
228
+ artifacts use the XCTest shim; log artifact capture is simulator-first in this
229
+ release.
230
+ Use a simulator-built `iphonesimulator` `.app` for simulator runs. A signed
231
+ device `.ipa` must be run with `--ios-device-type physical`; the pilot wrapper
232
+ rejects device IPAs on simulator runs before installing anything.
233
+ Use `--ios-device-type physical` with a concrete device identifier from
234
+ `zmr devices` for physical pilot runs:
235
+
236
+ ```bash
237
+ /path/to/zig-mobile-runner/scripts/run-ios-pilot.sh \
238
+ --app-root /path/to/mobile-app \
239
+ --app-path /path/to/mobile-app/build/Release-iphoneos/Sample.ipa \
240
+ --ios-device-type physical \
241
+ --device <physical-device-id> \
242
+ --ios-shim /path/to/mobile-app/.zmr/ios-shim \
243
+ --runs 20 \
244
+ --min-pass-rate 100 \
245
+ --max-failures 0
246
+ ```
247
+
248
+ If the app is already missing, ZMR treats the simulator as clean and continues.
249
+ Install the simulator `.app` again before launch/open-link steps that need it.
250
+
251
+ ## Direct CLI Use
252
+
253
+ Android:
254
+
255
+ ```bash
256
+ zmr run .zmr/android-auth-probe.json \
257
+ --device emulator-5554 \
258
+ --app-id com.example.mobiletest \
259
+ --android-shim ./.zmr/android-shim \
260
+ --trace-dir traces/android-auth
261
+ ```
262
+
263
+ Or use app-local defaults:
264
+
265
+ ```bash
266
+ zmr run --config .zmr/config.json
267
+ ```
268
+
269
+ iOS:
270
+
271
+ ```bash
272
+ xcrun simctl install booted /path/to/Sample.app
273
+ zmr run .zmr/ios-shim-smoke.json \
274
+ --platform ios \
275
+ --device booted \
276
+ --app-id com.example.mobiletest \
277
+ --ios-shim ./.zmr/ios-shim \
278
+ --trace-dir traces/ios-smoke
279
+ ```
280
+
281
+ ## Agent JSON-RPC Use
282
+
283
+ Start a local server next to the device. App repos scaffolded by
284
+ `zmr-wizard --package-json` can use the generated scripts:
285
+
286
+ ```bash
287
+ npm run zmr:serve
288
+ npm run zmr:mcp
289
+ ```
290
+
291
+ External agents can call:
292
+
293
+ - `runner.capabilities`
294
+ - `session.create`
295
+ - `app.launch`
296
+ - `app.openLink`
297
+ - `observe.snapshot`
298
+ - `observe.semanticSnapshot`
299
+ - `ui.tap`
300
+ - `wait.until`
301
+ - `assert.visible`
302
+ - `trace.export`
303
+
304
+ Use `observe.semanticSnapshot` before choosing actions, and use
305
+ `observe.snapshot` when raw adapter details are needed. Every action should
306
+ settle and observe again. Scenario runs call the adapter-level settle hook after
307
+ mutating actions; native shims can wait for platform idle while shell-only paths
308
+ keep a bounded sleep fallback. Start `serve` with `--trace-dir` so
309
+ `trace.export` can produce a redacted `.zmrtrace` bundle for the whole agent
310
+ session.
311
+
312
+ ## Public Artifact Rules
313
+
314
+ - Share `*-redacted.zmrtrace` bundles.
315
+ - Do not publish raw Metro logs, simulator logs, or unredacted screenshot bundles from private apps.
316
+ - Run `bash tests/public-safety-test.sh` before publishing this repo.