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
package/docs/npm.md ADDED
@@ -0,0 +1,359 @@
1
+ # npm Package
2
+
3
+ ZMR can be installed in a mobile app codebase as a dev dependency:
4
+
5
+ ```bash
6
+ npm install --save-dev zig-mobile-runner
7
+ ```
8
+
9
+ The package exposes:
10
+
11
+ - `zmr`: CLI binary wrapper.
12
+ - `zmr-init`: app-local scenario scaffolder.
13
+ - `zmr-wizard`: guided setup and dependency checker.
14
+ - `zmr-benchmark`: repeated-run wrapper with pass-rate and duration gates.
15
+ - `zmr-benchmark-command`: repeated-run wrapper for app-local baseline commands
16
+ so existing runner flows can be compared without custom glue.
17
+ - `zmr-compare-benchmarks`: generic comparison report for ZMR and app-local
18
+ baseline benchmark rows.
19
+ - `zmr-device-matrix`: local multi-device Android/iOS matrix runner with
20
+ simulator and physical iOS row support plus pass-rate gates.
21
+ - `zmr-pilot-gate`: external release pilot gate that delegates to the Android
22
+ and iOS app pilot wrappers on machines with real targets.
23
+ - `zmr-assert-ios-physical-ready`: verifies that a requested physical iOS
24
+ device is connected, trusted, and ready before physical-device pilots; pass
25
+ `--xcrun <path>` when using a custom Xcode toolchain and
26
+ `--evidence-out traces/zmr-pilots/evidence.jsonl` to append a
27
+ release-readiness row.
28
+ - `zmr-release-readiness`: checks one or more release/pilot evidence files for
29
+ dev-preview, production, or market-claim readiness, lists missing, insufficient, failed, and planned blockers, and emits safe claim wording.
30
+ - `zmr-install-android-shim`: writes the app-local Android instrumentation
31
+ shim command and source file.
32
+ - `zmr-install-ios-shim`: writes the app-local iOS XCTest shim command and
33
+ source files.
34
+ - `zmr-create-android-demo-app`: creates a generic native Android APK with a
35
+ matching `.zmr/` smoke scenario for public demos and emulator pilots.
36
+ - `zmr-create-ios-demo-app`: creates a generic SwiftUI simulator app with
37
+ `.zmr/` scenarios and the iOS shim already installed for public demos.
38
+ - `zmr-demo-android`: creates, installs, and runs the generated Android demo
39
+ through a real emulator/device.
40
+ - `zmr-demo-ios`: creates, builds, and runs the generated iOS simulator demo
41
+ through the real iOS pilot wrapper.
42
+ - `import { runZmr, spawnZmr, resolveBinary } from "zig-mobile-runner"` for Node scripts.
43
+ - packaged docs, schemas, examples, reference clients, and the reusable
44
+ `skills/zmr-mobile-testing` agent skill.
45
+
46
+ Maintainer release-candidate checks live in the source checkout, not the app-install npm package. Use `./scripts/release-candidate.sh` from the ZMR
47
+ repository when preparing a ZMR release; use `zmr-release-readiness` from an
48
+ app repo to evaluate evidence produced by app-local pilot gates.
49
+
50
+ ## App Setup
51
+
52
+ From the app repo:
53
+
54
+ ```bash
55
+ npx zmr-wizard --app-id com.example.mobiletest
56
+ ```
57
+
58
+ This creates:
59
+
60
+ ```text
61
+ .zmr/
62
+ config.json
63
+ android-smoke.json
64
+ ios-smoke.json
65
+ device-matrix.json
66
+ AGENTS.md
67
+ ```
68
+
69
+ `.zmr/config.json` is the app-local source of truth for default devices, trace directories, smoke scenario paths, and suggested script commands. `.zmr/device-matrix.json` gives CI a ready Android/iOS matrix starting point. ZMR auto-discovers config from the app repo, and explicit CLI flags override it. The wizard does not inspect or depend on any other mobile test runner configuration.
70
+ `.zmr/AGENTS.md` gives AI agents an app-local operating note with strict
71
+ doctor/validate commands, schema discovery, direct `zmr run` smoke commands,
72
+ JSON-RPC and MCP startup commands, selector guidance, the exact
73
+ `zmr explain traces/zmr-agent --json` failure-triage command, the exact
74
+ `zmr export traces/zmr-agent --out traces/zmr-agent-redacted.zmrtrace --redact`
75
+ redacted trace export command, and a
76
+ `zmr-release-readiness` claim guard for release summaries.
77
+ `zmr-init` and wizard runs without `--package-json` write direct commands in `.zmr/AGENTS.md` so agents can execute the generated guidance immediately.
78
+ `zmr-init` accepts the same platform, shim, and Expo dev-client scaffold flags as the wizard, plus `--package-json` for non-interactive app templates that do not need dependency checks.
79
+ `zmr-init` prints direct `Next steps` commands before the package-script snippet so humans and agents can run the generated smoke, reliability, matrix, pilot, JSON-RPC, MCP, failure-triage, and redacted-export commands without editing `package.json`.
80
+ For setup scripts and AI agents that need a machine-readable handoff, use
81
+ `npx zmr-init --json --dir . --app-id com.example.mobiletest` or
82
+ `npx zmr-wizard --json --dir . --app-id com.example.mobiletest --android --ios`.
83
+ The JSON form is covered by `schemas/init-output.schema.json` and includes
84
+ the generated config, scenario, Expo dev-client scenario, device matrix, and
85
+ `AGENTS.md` paths plus `nextCommands`, `scriptCount`, and `scriptNames`.
86
+ Wizard runs with `--package-json` write npm script commands in `.zmr/AGENTS.md` because the wizard installs those scripts into `package.json`. Run `npm run zmr:validate` after editing generated scenarios and before starting longer smoke, matrix, or pilot runs.
87
+
88
+ Add app-local scripts:
89
+
90
+ ```json
91
+ {
92
+ "scripts": {
93
+ "zmr:doctor": "zmr doctor --strict --json --config .zmr/config.json",
94
+ "zmr:schemas": "zmr schemas --json",
95
+ "zmr:validate": "zmr validate --json .zmr/android-smoke.json && zmr validate --json .zmr/ios-smoke.json",
96
+ "zmr:android": "zmr run .zmr/android-smoke.json --device emulator-5554 --trace-dir traces/zmr-android",
97
+ "zmr:android:report": "zmr report traces/zmr-android --out traces/zmr-android/report.html",
98
+ "zmr:android:reliability": "export ZMR_BIN=\"${ZMR_BIN:-zmr}\"; zmr-benchmark --zmr .zmr/android-smoke.json --device emulator-5554 --app-id com.example.mobiletest --runs 20 --trace-root traces/zmr-android-reliability --min-pass-rate 100 --max-failures 0 --max-p95-ms 30000 && \"$ZMR_BIN\" report traces/zmr-android-reliability --out traces/zmr-android-reliability/report.html",
99
+ "zmr:matrix": "ZMR_BIN=${ZMR_BIN:-zmr} zmr-device-matrix --matrix .zmr/device-matrix.json --trace-root traces/zmr-matrix --min-pass-rate 100 --max-failures 0",
100
+ "zmr:ios": "zmr run .zmr/ios-smoke.json --platform ios --device booted --trace-dir traces/zmr-ios",
101
+ "zmr:ios:report": "zmr report traces/zmr-ios --out traces/zmr-ios/report.html",
102
+ "zmr:ios:reliability": "export ZMR_BIN=\"${ZMR_BIN:-zmr}\"; zmr-benchmark --zmr .zmr/ios-smoke.json --platform ios --device booted --app-id com.example.mobiletest --xcrun xcrun --runs 20 --trace-root traces/zmr-ios-reliability --min-pass-rate 100 --max-failures 0 --max-p95-ms 45000 && \"$ZMR_BIN\" report traces/zmr-ios-reliability --out traces/zmr-ios-reliability/report.html",
103
+ "zmr:pilot": "zmr-pilot-gate --android --ios --android-app-root . --android-app-id com.example.mobiletest --android-device emulator-5554 --ios-app-root . --ios-app-path ./build/Debug-iphonesimulator/Sample.app --ios-app-id com.example.mobiletest --ios-device booted --runs 20 --min-pass-rate 100 --max-failures 0 --evidence-out traces/zmr-pilots/evidence.jsonl",
104
+ "zmr:readiness": "zmr-release-readiness --evidence traces/zmr-pilots/evidence.jsonl --target production --json",
105
+ "zmr:serve": "zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent",
106
+ "zmr:mcp": "zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent",
107
+ "zmr:explain": "zmr explain traces/zmr-agent --json",
108
+ "zmr:export": "zmr export traces/zmr-agent --out traces/zmr-agent-redacted.zmrtrace --redact"
109
+ }
110
+ }
111
+ ```
112
+
113
+ Reliability scripts export one `ZMR_BIN` value and reuse it for both
114
+ `zmr-benchmark` and report generation, so CI can pin a custom runner binary
115
+ without mixing binaries between the run and report steps.
116
+
117
+ For non-interactive CI or template setup:
118
+
119
+ ```bash
120
+ npx zmr-wizard \
121
+ --yes \
122
+ --app-id com.example.mobiletest \
123
+ --android \
124
+ --android-shim ./.zmr/android-shim \
125
+ --ios \
126
+ --ios-shim ./.zmr/ios-shim \
127
+ --expo-dev-client-scheme mobiletest \
128
+ --package-json
129
+ ```
130
+
131
+ The wizard checks Node, ZMR, ADB, `xcrun`, and Zig when applicable. It scaffolds `.zmr` scenarios and can patch `package.json` scripts.
132
+ It also ensures `traces/` is ignored in the app repo.
133
+ When `--expo-dev-client-scheme` is set, it also writes
134
+ `.zmr/android-dev-client-smoke.json` and `.zmr/ios-dev-client-open-link.json`.
135
+ Package-script setup also adds `zmr:android:dev-client`,
136
+ `zmr:android:dev-client:report`, `zmr:ios:dev-client`, and
137
+ `zmr:ios:dev-client:report` for the generated dev-client traces.
138
+ The Android scenario opens Metro through `10.0.2.2:8081`; the iOS simulator
139
+ scenario opens `127.0.0.1:8081`.
140
+ Rerunning the wizard refreshes generated `.zmr/config.json`,
141
+ `.zmr/device-matrix.json`, and `.zmr/AGENTS.md` for the selected platforms,
142
+ while existing scenario files are left in place so local flow edits are not
143
+ overwritten.
144
+ `zmr-init` can be used for the same non-interactive scaffold without dependency
145
+ checks:
146
+
147
+ ```bash
148
+ npx zmr-init \
149
+ --dir . \
150
+ --app-id com.example.mobiletest \
151
+ --ios \
152
+ --ios-shim ./.zmr/ios-shim \
153
+ --expo-dev-client-scheme mobiletest \
154
+ --package-json
155
+ ```
156
+
157
+ When platform flags are omitted, `zmr-init` scaffolds both Android and iOS.
158
+ With `--package-json`, `zmr-init` patches `package.json` directly and writes
159
+ `.zmr/AGENTS.md` with `npm run zmr:*` commands. Without `--package-json`, it
160
+ prints the script map for copy-free review and keeps `.zmr/AGENTS.md` on direct
161
+ `zmr` commands.
162
+ Rerunning `zmr init --app` refreshes generated `.zmr/config.json`,
163
+ `.zmr/device-matrix.json`, and `.zmr/AGENTS.md` the same way, while preserving
164
+ existing scenario files. Pass `--force` only when you intentionally want to
165
+ replace the generated smoke scenarios too.
166
+ The reliability scripts use `zmr-benchmark` with `100%` pass-rate and zero-failure
167
+ defaults; tune p95 thresholds only after capturing stable local baseline runs.
168
+ The wizard only adds `zmr:readiness` for Android+iOS setups because the
169
+ production readiness target requires Android, iOS simulator, and physical iOS
170
+ evidence; single-platform setups should use `zmr:pilot` and the platform
171
+ reliability script until the full matrix is enabled.
172
+ For release validation, `zmr-pilot-gate` is safe to run from the app checkout:
173
+ relative app roots, APK paths, simulator app paths, shim paths, and trace roots
174
+ are resolved against the current app directory before the packaged runner
175
+ scripts are invoked. Pass `--zmr-bin ./node_modules/.bin/zmr` when CI needs an
176
+ explicit runner binary instead of relying on `PATH` or `ZMR_BIN`. Add
177
+ `--evidence-out traces/zmr-pilots/evidence.jsonl` so production-readiness rows
178
+ can be evaluated with `zmr-release-readiness`.
179
+
180
+ The standalone CLI has the same non-interactive app-local bootstrap for
181
+ source or release-archive installs:
182
+
183
+ ```bash
184
+ zmr init --app --json --dir . --app-id com.example.mobiletest
185
+ zmr doctor --strict --json --config .zmr/config.json
186
+ ```
187
+
188
+ See [ai-agents.md](ai-agents.md) for JSON-RPC agent workflows and
189
+ [`../skills/zmr-mobile-testing/SKILL.md`](../skills/zmr-mobile-testing/SKILL.md)
190
+ for the packaged agent skill.
191
+
192
+ Omit `--android-shim` or `--ios-shim` for shell/screenshot-only smoke runs.
193
+ Include them when the app repo provides native shim commands for faster
194
+ hierarchy and selector actions.
195
+
196
+ ## Android Demo App
197
+
198
+ For a clean public Android demo APK that does not depend on a private app:
199
+
200
+ ```bash
201
+ npx zmr-demo-android --out /tmp/zmr-android-demo --device emulator-5554 --avd <avd-name>
202
+ ```
203
+
204
+ That command uses Android SDK command-line tools directly, so it does not need
205
+ Gradle or network access. It creates the demo APK, boots the named AVD when
206
+ the requested device is not already ready, installs the app, runs the smoke
207
+ scenario, and writes traces under `<out>/traces/pilot`.
208
+
209
+ To inspect or customize the generated app before running manually:
210
+
211
+ ```bash
212
+ npx zmr-create-android-demo-app --out /tmp/zmr-android-demo
213
+ adb install -r /tmp/zmr-android-demo/build/app-debug.apk
214
+ zmr run /tmp/zmr-android-demo/.zmr/android-smoke.json \
215
+ --device emulator-5554 \
216
+ --app-id com.example.mobiletest \
217
+ --trace-dir /tmp/zmr-android-demo/traces/android-demo
218
+ ```
219
+
220
+ ## iOS Demo App
221
+
222
+ For a clean public iOS demo that does not depend on a private app:
223
+
224
+ ```bash
225
+ npx zmr-demo-ios --out /tmp/zmr-ios-demo --device booted --cleanup-build-products
226
+ ```
227
+
228
+ That command creates the demo app, boots an available simulator when needed,
229
+ builds it with Xcode, runs the iOS pilot, and writes trace reports plus
230
+ redacted bundles. `--cleanup-build-products` removes generated Xcode
231
+ `DerivedData` after the trace reports are written, which keeps repeated demo
232
+ runs from filling local disk. To inspect or customize the app before running
233
+ the pilot manually:
234
+
235
+ ```bash
236
+ npx zmr-create-ios-demo-app --out /tmp/zmr-ios-demo
237
+ cd /tmp/zmr-ios-demo
238
+ xcodebuild -project ios/ZMRDemo.xcodeproj -scheme ZMRDemo -destination 'generic/platform=iOS Simulator' -derivedDataPath DerivedData build
239
+ ```
240
+
241
+ Then boot a simulator and run the pilot wrapper:
242
+
243
+ ```bash
244
+ zmr-pilot-gate \
245
+ --ios \
246
+ --ios-app-root /tmp/zmr-ios-demo \
247
+ --ios-app-path /tmp/zmr-ios-demo/DerivedData/Build/Products/Debug-iphonesimulator/ZMRDemo.app \
248
+ --ios-app-id com.example.mobiletest \
249
+ --ios-device booted \
250
+ --ios-shim /tmp/zmr-ios-demo/.zmr/ios-shim \
251
+ --zmr-bin ./node_modules/.bin/zmr
252
+ ```
253
+
254
+ When `--ios-shim` is set, the pilot prewarms the XCTest shim before scenario
255
+ timing with an `appState` command. Pass `--skip-shim-prewarm` only when
256
+ intentionally measuring cold shim startup.
257
+
258
+ To scaffold the Android shim command into an app repo:
259
+
260
+ ```bash
261
+ npx zmr-install-android-shim \
262
+ --app-root . \
263
+ --test-package com.example.mobiletest.test \
264
+ --runner androidx.test.runner.AndroidJUnitRunner \
265
+ --android-module android/app \
266
+ --gradle-file android/app/build.gradle
267
+ ```
268
+
269
+ `--android-module` copies the shim into
270
+ `android/app/src/androidTest/java/dev/zmr/shim/ZMRShimInstrumentedTest.java`.
271
+ `--gradle-file` appends guarded Gradle blocks for `testInstrumentationRunner`,
272
+ AndroidX Test runner, JUnit extension, and UI Automator. If the Gradle file
273
+ already has a custom `testInstrumentationRunner` and `--runner` is omitted, the
274
+ generated `.zmr/android-shim` command uses that runner. Run ZMR with
275
+ `--android-shim ./.zmr/android-shim`.
276
+
277
+ To scaffold the iOS shim command into an app repo:
278
+
279
+ ```bash
280
+ npx zmr-install-ios-shim \
281
+ --app-root . \
282
+ --scheme SampleUITests \
283
+ --test-target SampleUITests \
284
+ --workspace ios/Sample.xcworkspace \
285
+ --app-target SampleApp \
286
+ --derived-data-path ios/build/ZMRDerivedData \
287
+ --bundle-id com.example.mobiletest \
288
+ --patch-xcodeproj
289
+ ```
290
+
291
+ The installer writes:
292
+
293
+ - `.zmr/ios-shim`
294
+ - `.zmr/ensure-ios-shim-target.sh`
295
+ - `.zmr/ensure-ios-shim-target.rb`
296
+ - `.zmr/ZMRShimUITestCase.swift`
297
+ - `.zmr/shims/ios/ZMRShim.swift`
298
+ - `.zmr/ZMRShimUITests-Info.plist`
299
+ - `.zmr/config.json` `tools.iosShimPath`, creating the config file when needed
300
+
301
+ Run `.zmr/ensure-ios-shim-target.sh` to create/update the UI test target, add
302
+ the Swift files, configure the generated Info.plist, and write a shared scheme.
303
+ The helper uses the Ruby `xcodeproj` gem. With `--workspace`, it resolves the
304
+ referenced `.xcodeproj` automatically when there is one project, or when exactly
305
+ one project contains `--app-target`, or when `--bundle-id` disambiguates
306
+ matching app targets. Pass `--project ios/Sample.xcodeproj` explicitly for
307
+ still-ambiguous multi-project workspaces or project-only apps.
308
+
309
+ Run ZMR with `--ios-shim ./.zmr/ios-shim`, or rely on the generated
310
+ `tools.iosShimPath` in `.zmr/config.json`.
311
+ The generated command caches `build-for-testing` output under
312
+ `.zmr/ios-shim-state/`, uses `test-without-building` for selector commands, and
313
+ prints the last Xcode log lines when XCTest fails. Set
314
+ `ZMR_IOS_SHIM_FORCE_REBUILD=1` after app-side target changes, or
315
+ `ZMR_IOS_SHIM_ONESHOT=1` for a cold-start fallback while debugging app-side Xcode
316
+ wiring.
317
+
318
+ ## Native Binary Resolution
319
+
320
+ The npm wrapper resolves `zmr` in this order:
321
+
322
+ 1. `ZMR_BIN=/path/to/zmr`
323
+ 2. bundled `prebuilds/<platform>-<arch>/zmr`
324
+ 3. local source build at `zig-out/bin/zmr`
325
+
326
+ Shipped shell helpers such as `zmr-pilot-gate`, `zmr-demo-ios`, and the pilot
327
+ wrappers resolve the runner in this order: `ZMR_BIN`, `PATH` `zmr`, then the
328
+ source-checkout `zig-out/bin/zmr` fallback. That keeps app installs on the npm
329
+ wrapper path while preserving source-checkout development.
330
+ Relative app paths passed to pilot wrappers are resolved from the app directory
331
+ where the command was started, not from the installed package directory.
332
+
333
+ If no binary is found, install Zig and run:
334
+
335
+ ```bash
336
+ npm run build:zmr
337
+ ```
338
+
339
+ For release publishing, build npm tarballs with:
340
+
341
+ ```bash
342
+ npm run pack:npm
343
+ ```
344
+
345
+ That command builds release binaries, copies them into `prebuilds/`, and runs `npm pack`.
346
+
347
+ ## Node API
348
+
349
+ ```js
350
+ import { runZmr } from "zig-mobile-runner";
351
+
352
+ await runZmr([
353
+ "run",
354
+ "--config",
355
+ ".zmr/config.json",
356
+ ]);
357
+ ```
358
+
359
+ Use the CLI for normal app scripts and the JS API for custom toolchains or agent orchestration.
@@ -0,0 +1,8 @@
1
+ # Protocol Fixtures
2
+
3
+ These JSONL fixtures pin exact JSON-RPC request and response shapes for stable
4
+ core methods. The Zig unit suite feeds the request fixtures through the local
5
+ dispatcher and compares byte-for-byte response output.
6
+
7
+ When changing any response shape, update these fixtures deliberately and bump or
8
+ document protocol compatibility as needed.
@@ -0,0 +1,8 @@
1
+ {"jsonrpc":"2.0","id":1,"method":"runner.capabilities","params":{}}
2
+ {"jsonrpc":"2.0","id":2,"method":"device.list","params":{}}
3
+ {"jsonrpc":"2.0","id":3,"method":"session.create","params":{}}
4
+ {"jsonrpc":"2.0","id":4,"method":"app.launch","params":{}}
5
+ {"jsonrpc":"2.0","id":5,"method":"app.openLink","params":{"url":"exampleapp://fixture"}}
6
+ {"jsonrpc":"2.0","id":6,"method":"app.clearState","params":{}}
7
+ {"jsonrpc":"2.0","id":7,"method":"trace.export","params":{}}
8
+ {"jsonrpc":"2.0","id":8,"method":"session.close","params":{}}
@@ -0,0 +1,8 @@
1
+ {"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.1.0","protocolVersion":"2026-04-28","protocol":{"version":"2026-04-28","minimumCompatibleVersion":"2026-04-28","stability":"dev-preview","breakingChangePolicy":"version-and-changelog"},"platforms":["android","ios"],"platformSupport":{"android":{"status":"supported","deviceTypes":["emulator","physical"],"automation":["adb","uiautomator","android-shim"]},"ios":{"status":"supported","deviceTypes":["simulator","physical"],"automation":["simctl","devicectl","xctest-shim"],"physicalDevices":true}},"iosPreview":false,"transports":["stdio","tcp"],"methods":["runner.capabilities","device.list","session.create","session.close","app.install","app.launch","app.stop","app.openLink","app.clearState","observe.snapshot","observe.semanticSnapshot","ui.tap","ui.type","ui.eraseText","ui.hideKeyboard","ui.swipe","ui.pressBack","ui.scrollUntilVisible","wait.until","wait.any","wait.gone","assert.visible","assert.notVisible","assert.healthy","trace.events","trace.export"]}}
2
+ {"jsonrpc":"2.0","id":2,"result":[{"serial":"fake-device-1","state":"device","ready":true}]}
3
+ {"jsonrpc":"2.0","id":3,"result":{"sessionId":"default"}}
4
+ {"jsonrpc":"2.0","id":4,"result":true}
5
+ {"jsonrpc":"2.0","id":5,"result":true}
6
+ {"jsonrpc":"2.0","id":6,"result":true}
7
+ {"jsonrpc":"2.0","id":7,"result":{"traceDir":null,"message":"start zmr serve with --trace-dir to enable live RPC trace export"}}
8
+ {"jsonrpc":"2.0","id":8,"result":true}
@@ -0,0 +1,65 @@
1
+ # Protocol Versioning
2
+
3
+ ZMR exposes two public automation surfaces:
4
+
5
+ - scenario JSON files consumed by `zmr run`
6
+ - JSON-RPC methods exposed by `zmr serve`
7
+
8
+ The current protocol version is a date string. Before `v1.0.0`, breaking changes
9
+ are allowed only when the protocol version and changelog are updated together.
10
+ `runner.capabilities` exposes this policy in machine-readable form:
11
+
12
+ ```json
13
+ {
14
+ "protocol": {
15
+ "version": "2026-04-28",
16
+ "minimumCompatibleVersion": "2026-04-28",
17
+ "stability": "dev-preview",
18
+ "breakingChangePolicy": "version-and-changelog"
19
+ }
20
+ }
21
+ ```
22
+
23
+ Clients should continue reading the top-level `protocolVersion` field for older
24
+ servers, but new clients should prefer `protocol.version` and reject servers
25
+ older than `protocol.minimumCompatibleVersion` unless they intentionally support
26
+ that older shape.
27
+
28
+ ## Compatibility Rules
29
+
30
+ - Adding optional fields is non-breaking.
31
+ - Adding new methods or actions is non-breaking when existing behavior remains.
32
+ - Removing fields, renaming fields, changing required params, or changing error
33
+ codes is breaking.
34
+ - Native shim protocols are internal and not covered by the public compatibility
35
+ promise until explicitly documented as stable.
36
+
37
+ ## Test Requirements
38
+
39
+ Protocol changes must update:
40
+
41
+ - `docs/protocol.md`
42
+ - `schemas/`
43
+ - JSON-RPC or scenario parser tests
44
+ - `CHANGELOG.md`
45
+
46
+ ## Governance
47
+
48
+ The protocol is reviewed as a product contract, not an implementation detail.
49
+ Any change to scenario JSON, JSON-RPC methods, stable error codes, trace
50
+ schemas, or `runner.capabilities` must call out compatibility impact in the
51
+ pull request or release notes.
52
+
53
+ Governance rules:
54
+
55
+ - Non-breaking additions may ship in the current protocol version when existing
56
+ clients keep working unchanged.
57
+ - Breaking changes require a protocol version bump, changelog entry, fixture
58
+ update, and migration note in `docs/protocol.md`.
59
+ - Removing a documented field or method requires a deprecation window unless
60
+ the project is still before its first stable release and the changelog names
61
+ the break explicitly.
62
+ - Client authors should be able to discover support from `runner.capabilities`
63
+ without probing by failure.
64
+ - The protocol fixture files under `docs/protocol-fixtures/` are treated as
65
+ golden examples for agent integrations and must stay deterministic.