zeno-mobile-runner 0.1.8 → 0.2.1
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.
- package/CHANGELOG.md +72 -0
- package/FEATURES.md +1 -1
- package/README.md +175 -238
- package/clients/kotlin/README.md +1 -1
- package/clients/kotlin/build.gradle.kts +1 -1
- package/clients/python/pyproject.toml +1 -1
- package/clients/rust/Cargo.lock +1 -1
- package/clients/rust/Cargo.toml +1 -1
- package/clients/typescript/package.json +1 -1
- package/docs/agent-discovery.md +10 -0
- package/docs/ai-agents.md +18 -0
- package/docs/benchmarking.md +39 -0
- package/docs/benchmarks/2026-06-09-android-workflow.md +73 -0
- package/docs/benchmarks/2026-06-09-android-workflow.results.jsonl +20 -0
- package/docs/benchmarks/2026-06-09-framework-baseline-status.md +32 -0
- package/docs/benchmarks/2026-06-09-ios-appium-comparison.md +115 -0
- package/docs/benchmarks/2026-06-09-ios-appium-comparison.results.jsonl +40 -0
- package/docs/benchmarks/2026-06-09-ios-demo.md +90 -0
- package/docs/benchmarks/2026-06-09-ios-demo.results.jsonl +20 -0
- package/docs/benchmarks/2026-06-09-ios-maestro-comparison.md +128 -0
- package/docs/benchmarks/2026-06-09-ios-maestro-comparison.results.jsonl +40 -0
- package/docs/benchmarks/2026-06-09-ios-workflow-comparison.md +143 -0
- package/docs/benchmarks/2026-06-09-ios-workflow-comparison.results.jsonl +40 -0
- package/docs/benchmarks/2026-06-09-ios-xctest-floor.md +106 -0
- package/docs/benchmarks/2026-06-09-ios-xctest-floor.results.jsonl +40 -0
- package/docs/benchmarks/README.md +36 -0
- package/docs/benchmarks/benchmark-lab-v1.json +155 -0
- package/docs/benchmarks/benchmark-lab-v1.md +95 -0
- package/docs/clients.md +16 -0
- package/docs/demo.md +36 -1
- package/docs/frameworks.md +10 -0
- package/docs/npm.md +44 -2
- package/docs/protocol-fixtures/core-session.responses.jsonl +1 -1
- package/docs/protocol.md +10 -10
- package/docs/scenario-authoring.md +15 -0
- package/docs/trace-privacy.md +9 -0
- package/docs/troubleshooting.md +6 -0
- package/examples/android-workflow.json +79 -0
- package/examples/ios-dev-client-open-link.json +24 -13
- package/examples/ios-dev-client-route-snapshot.json +33 -8
- package/examples/ios-shim-workflow.json +79 -0
- package/examples/react-native-expo-workflow.json +75 -0
- package/npm/scenarios.mjs +15 -8
- package/npm/wizard.mjs +1 -1
- package/package.json +6 -1
- package/prebuilds/darwin-arm64/zmr +0 -0
- package/prebuilds/darwin-x64/zmr +0 -0
- package/prebuilds/linux-arm64/zmr +0 -0
- package/prebuilds/linux-x64/zmr +0 -0
- package/scripts/benchmark-lab.py +253 -0
- package/scripts/create-android-demo-app.sh +324 -29
- package/scripts/create-ios-demo-app.sh +174 -7
- package/scripts/create-react-native-expo-demo-app.sh +727 -0
- package/scripts/demo.sh +3 -0
- package/scripts/install-ios-shim.sh +2 -2
- package/shims/ios/ZMRShim.swift +10 -0
- package/shims/ios/ZMRShimUITestCase.swift +49 -1
- package/shims/ios/protocol.md +1 -0
- package/src/cli_import.zig +31 -15
- package/src/cli_trace.zig +38 -16
- package/src/cli_validate.zig +12 -6
- package/src/ios.zig +44 -11
- package/src/ios_shim.zig +36 -2
- package/src/main.zig +6 -0
- package/src/version.zig +1 -1
- package/viewer/app.js +23 -3
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"name": "Benchmark Lab v1",
|
|
4
|
+
"purpose": "Define reproducible mobile benchmark fixtures, modes, adapters, and claim boundaries for ZMR against framework-relevant local runners.",
|
|
5
|
+
"claimPolicy": {
|
|
6
|
+
"minimumRuns": 20,
|
|
7
|
+
"candidatePassRate": 100,
|
|
8
|
+
"candidateFailures": 0,
|
|
9
|
+
"requiresSameContext": true,
|
|
10
|
+
"requiresCommittedRows": true,
|
|
11
|
+
"requiresSanitizedCommands": true,
|
|
12
|
+
"forbiddenClaim": "Do not present fixture results as universal speed or reliability claims."
|
|
13
|
+
},
|
|
14
|
+
"modes": [
|
|
15
|
+
{
|
|
16
|
+
"id": "cold-command",
|
|
17
|
+
"label": "Cold command",
|
|
18
|
+
"description": "Each row measures the command surface a user would run from a shell, including runner startup."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "warm-suite",
|
|
22
|
+
"label": "Warm suite",
|
|
23
|
+
"description": "The app, device, and runner bridge are prepared before timed rows so the benchmark isolates repeated scenario execution."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "native-floor",
|
|
27
|
+
"label": "Native floor",
|
|
28
|
+
"description": "A lower-bound timing for the platform shim path. It is diagnostic evidence, not a product comparison."
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"fixtures": [
|
|
32
|
+
{
|
|
33
|
+
"id": "native-ios-workflow",
|
|
34
|
+
"label": "Generated native iOS workflow",
|
|
35
|
+
"framework": "native-ios",
|
|
36
|
+
"platforms": ["ios"],
|
|
37
|
+
"status": "evidence-committed",
|
|
38
|
+
"scenario": "examples/ios-shim-workflow.json",
|
|
39
|
+
"appGenerator": "scripts/create-ios-demo-app.sh",
|
|
40
|
+
"workflow": [
|
|
41
|
+
"launch",
|
|
42
|
+
"profile-entry",
|
|
43
|
+
"catalog-scroll",
|
|
44
|
+
"item-detail",
|
|
45
|
+
"save-item",
|
|
46
|
+
"review-assertion"
|
|
47
|
+
],
|
|
48
|
+
"evidence": "docs/benchmarks/2026-06-09-ios-workflow-comparison.md"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "native-android-workflow",
|
|
52
|
+
"label": "Generated native Android workflow",
|
|
53
|
+
"framework": "native-android",
|
|
54
|
+
"platforms": ["android"],
|
|
55
|
+
"status": "evidence-committed",
|
|
56
|
+
"scenario": "examples/android-workflow.json",
|
|
57
|
+
"appGenerator": "scripts/create-android-demo-app.sh",
|
|
58
|
+
"workflow": [
|
|
59
|
+
"launch",
|
|
60
|
+
"profile-entry",
|
|
61
|
+
"catalog-scroll",
|
|
62
|
+
"item-detail",
|
|
63
|
+
"save-item",
|
|
64
|
+
"review-assertion"
|
|
65
|
+
],
|
|
66
|
+
"evidence": "docs/benchmarks/2026-06-09-android-workflow.md"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "react-native-expo-workflow",
|
|
70
|
+
"label": "React Native and Expo workflow",
|
|
71
|
+
"framework": "react-native-expo",
|
|
72
|
+
"platforms": ["android", "ios"],
|
|
73
|
+
"status": "fixture-available",
|
|
74
|
+
"scenario": "examples/react-native-expo-workflow.json",
|
|
75
|
+
"appGenerator": "scripts/create-react-native-expo-demo-app.sh",
|
|
76
|
+
"workflow": [
|
|
77
|
+
"deep-link-open",
|
|
78
|
+
"profile-entry",
|
|
79
|
+
"list-scroll",
|
|
80
|
+
"stateful-save",
|
|
81
|
+
"review-assertion"
|
|
82
|
+
],
|
|
83
|
+
"notes": "Generated fixture uses testID values, accessibility labels, and deep links. Public ZMR rows and baseline runner rows are not collected yet."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "flutter-semantics-workflow",
|
|
87
|
+
"label": "Flutter semantics workflow",
|
|
88
|
+
"framework": "flutter",
|
|
89
|
+
"platforms": ["android", "ios"],
|
|
90
|
+
"status": "planned",
|
|
91
|
+
"workflow": [
|
|
92
|
+
"deep-link-open",
|
|
93
|
+
"semantics-label-selection",
|
|
94
|
+
"scroll",
|
|
95
|
+
"stateful-save",
|
|
96
|
+
"review-assertion"
|
|
97
|
+
],
|
|
98
|
+
"notes": "Measure app-level Android/iOS automation through semantics labels and deep links. Do not claim widget-tree driver support."
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"runnerAdapters": [
|
|
102
|
+
{
|
|
103
|
+
"id": "zmr",
|
|
104
|
+
"label": "ZMR",
|
|
105
|
+
"status": "available",
|
|
106
|
+
"collector": "scripts/benchmark.sh",
|
|
107
|
+
"modes": ["cold-command", "warm-suite"]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"id": "maestro",
|
|
111
|
+
"label": "Maestro",
|
|
112
|
+
"status": "evidence-committed",
|
|
113
|
+
"collector": "scripts/benchmark-command.sh",
|
|
114
|
+
"modes": ["cold-command"]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"id": "appium",
|
|
118
|
+
"label": "Appium",
|
|
119
|
+
"status": "partial",
|
|
120
|
+
"collector": "scripts/benchmark-command.sh",
|
|
121
|
+
"modes": ["cold-command"],
|
|
122
|
+
"notes": "The current public iOS workflow attempt failed while starting WebDriverAgent, so fixture-specific setup hardening is required before timing rows."
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"id": "detox",
|
|
126
|
+
"label": "Detox",
|
|
127
|
+
"status": "planned",
|
|
128
|
+
"collector": "scripts/benchmark-command.sh",
|
|
129
|
+
"modes": ["warm-suite"],
|
|
130
|
+
"notes": "Requires a React Native fixture with project-local native build targets and a test harness."
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
"nextSlices": [
|
|
134
|
+
{
|
|
135
|
+
"id": "android-native-rows",
|
|
136
|
+
"status": "done",
|
|
137
|
+
"description": "Collect 20-run Android rows for the generated native workflow on a clean emulator."
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "react-native-expo-fixture",
|
|
141
|
+
"status": "done",
|
|
142
|
+
"description": "Add a generated React Native/Expo fixture with stable test IDs, deep links, and matching ZMR workflow files."
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "flutter-semantics-fixture",
|
|
146
|
+
"status": "next",
|
|
147
|
+
"description": "Add a Flutter fixture that proves app-level semantics support without widget-tree claims."
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"id": "warm-suite-runner",
|
|
151
|
+
"status": "later",
|
|
152
|
+
"description": "Add suite-level benchmark collection so bridge prewarm and repeated scenario execution are measured separately from cold command startup."
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Benchmark Lab v1
|
|
2
|
+
|
|
3
|
+
Benchmark Lab v1 is the public evidence plan for ZMR. It keeps framework
|
|
4
|
+
fixtures, runner adapters, timing modes, and claim boundaries explicit so speed
|
|
5
|
+
or reliability statements are reproducible instead of anecdotal.
|
|
6
|
+
It is the framework-level evidence map for React Native, Expo, Flutter, native
|
|
7
|
+
Android, and native iOS fixtures.
|
|
8
|
+
|
|
9
|
+
The machine-readable source is
|
|
10
|
+
[benchmark-lab-v1.json](benchmark-lab-v1.json). Render or validate it with:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
zmr-benchmark-lab --manifest docs/benchmarks/benchmark-lab-v1.json --format markdown
|
|
14
|
+
zmr-benchmark-lab --manifest docs/benchmarks/benchmark-lab-v1.json --format json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Direction
|
|
18
|
+
|
|
19
|
+
ZMR should compete first where mobile teams already make framework choices:
|
|
20
|
+
React Native, Expo, Flutter, native Android, and native iOS. The lab is not a
|
|
21
|
+
generic benchmark scoreboard. Each fixture must represent an app workflow a
|
|
22
|
+
developer can inspect, build, run, and adapt.
|
|
23
|
+
|
|
24
|
+
The near-term wedge is agent-native mobile testing: structured observation,
|
|
25
|
+
selector-grade actions, trace-first debugging, and reviewable scenario
|
|
26
|
+
generation. Benchmarks should prove the local runner path is fast and reliable
|
|
27
|
+
without overstating what one fixture demonstrates.
|
|
28
|
+
|
|
29
|
+
## Fixtures
|
|
30
|
+
|
|
31
|
+
| Fixture | Framework | Platforms | Status | Scenario |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| Generated native iOS workflow | native-ios | iOS | evidence committed | `examples/ios-shim-workflow.json` |
|
|
34
|
+
| Generated native Android workflow | native-android | Android | evidence committed | `examples/android-workflow.json` |
|
|
35
|
+
| React Native and Expo workflow | react-native-expo | Android, iOS | fixture available | `examples/react-native-expo-workflow.json` |
|
|
36
|
+
| Flutter semantics workflow | flutter | Android, iOS | planned | pending |
|
|
37
|
+
|
|
38
|
+
The first richer iOS evidence pack is
|
|
39
|
+
[2026-06-09 iOS simulator workflow comparison](2026-06-09-ios-workflow-comparison.md).
|
|
40
|
+
It covers launch, profile entry, catalog scroll, item detail, save, review, and
|
|
41
|
+
final-state assertion on the generated native iOS demo app.
|
|
42
|
+
|
|
43
|
+
The first Android workflow evidence pack is
|
|
44
|
+
[2026-06-09 Android emulator workflow](2026-06-09-android-workflow.md). It
|
|
45
|
+
records 20 repeated ZMR runs through the platform UIAutomator path.
|
|
46
|
+
|
|
47
|
+
The React Native/Expo fixture is available through
|
|
48
|
+
`scripts/create-react-native-expo-demo-app.sh`. It generates an Expo app with
|
|
49
|
+
stable `testID` values, accessibility labels, a deep-link scheme, and matching
|
|
50
|
+
Android/iOS ZMR workflow scenarios. Public timing rows are still pending.
|
|
51
|
+
|
|
52
|
+
## Runner Adapters
|
|
53
|
+
|
|
54
|
+
| Adapter | Status | Collector | Notes |
|
|
55
|
+
| --- | --- | --- | --- |
|
|
56
|
+
| ZMR | available | `scripts/benchmark.sh` | Candidate runner for all fixtures. |
|
|
57
|
+
| Maestro | evidence committed | `scripts/benchmark-command.sh` | Use YAML flows that match the same visible app state. |
|
|
58
|
+
| Appium | partial | `scripts/benchmark-command.sh` | The current public iOS workflow attempt failed while starting WebDriverAgent, so setup needs hardening before timing rows. |
|
|
59
|
+
| Detox | planned | `scripts/benchmark-command.sh` | Requires a React Native fixture with native build targets and a project-local test harness. |
|
|
60
|
+
|
|
61
|
+
Other local runner rows can be collected with the same generic command wrapper,
|
|
62
|
+
but public docs should only name tools when a fixture-specific evidence pack is
|
|
63
|
+
available or when a status row explains why evidence is missing.
|
|
64
|
+
|
|
65
|
+
## Modes
|
|
66
|
+
|
|
67
|
+
| Mode | Meaning |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| Cold command | Measures the shell command a user runs, including runner startup. |
|
|
70
|
+
| Warm suite | Prepares the app, device, and runner bridge before timed rows, isolating repeated scenario execution. |
|
|
71
|
+
| Native floor | Measures a direct platform shim path as diagnostic lower-bound evidence, not a product comparison. |
|
|
72
|
+
|
|
73
|
+
Cold-command rows are the best default for user-facing claims. Warm-suite rows
|
|
74
|
+
are the best way to prove the core execution path can become faster without
|
|
75
|
+
hiding setup work. Native-floor rows show where remaining overhead lives.
|
|
76
|
+
|
|
77
|
+
## Claim Rules
|
|
78
|
+
|
|
79
|
+
- Use at least 20 candidate rows and 20 baseline rows for public comparison
|
|
80
|
+
evidence.
|
|
81
|
+
- Require 100% candidate pass rate and zero candidate failures for any public
|
|
82
|
+
speed claim.
|
|
83
|
+
- Compare only rows with the same host class, OS/toolchain, device state, app
|
|
84
|
+
id, app build, scenario, and timing mode.
|
|
85
|
+
- Commit sanitized result rows and commands. Do not commit raw trace logs when
|
|
86
|
+
they contain local absolute paths or app data.
|
|
87
|
+
- Phrase every result as fixture-specific evidence. Do not describe one lab run
|
|
88
|
+
as a universal product claim.
|
|
89
|
+
|
|
90
|
+
## Next Slices
|
|
91
|
+
|
|
92
|
+
1. Add a Flutter semantics fixture that proves app-level Android/iOS support
|
|
93
|
+
without claiming widget-tree automation.
|
|
94
|
+
2. Add warm-suite collection so bridge prewarm and repeated execution can be
|
|
95
|
+
measured separately from command startup.
|
package/docs/clients.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
ZMR clients are reference implementations for the JSON-RPC protocol used by
|
|
4
4
|
`zmr serve`. They are intentionally small and dependency-light.
|
|
5
5
|
|
|
6
|
+
TypeScript and Python are the most common starting points for app teams and
|
|
7
|
+
agent harnesses. Go, Rust, Swift, and Kotlin clients are reference integrations
|
|
8
|
+
for teams that want to embed the protocol from those ecosystems. Go and Rust
|
|
9
|
+
include typed trace discovery and scenario validation helpers for host-side
|
|
10
|
+
agent loops; Swift and Kotlin include lightweight discovery and validation
|
|
11
|
+
helpers for host-side automation.
|
|
12
|
+
|
|
13
|
+
| Language | Entry point | Example |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| TypeScript | `clients/typescript/index.mjs` + `index.d.ts` | `node clients/typescript/examples/fake-session.mjs` |
|
|
16
|
+
| Python | `clients/python/zmr_client.py` + `pyproject.toml` | `python3 clients/python/examples/fake_session.py` |
|
|
17
|
+
| Go | `clients/go/zmr/client.go` | `go run ./clients/go/examples/fake-session` |
|
|
18
|
+
| Rust | `clients/rust/src/lib.rs` | `cargo run --manifest-path clients/rust/Cargo.toml --example fake_session` |
|
|
19
|
+
| Swift | `clients/swift/Sources/ZMRClient` | `swift build --package-path clients/swift` |
|
|
20
|
+
| Kotlin | `clients/kotlin/src/main/kotlin/dev/zmr` | `gradle -p clients/kotlin build` |
|
|
21
|
+
|
|
6
22
|
## What Clients Mean
|
|
7
23
|
|
|
8
24
|
The runner is still the Zig binary. A client starts or connects to:
|
package/docs/demo.md
CHANGED
|
@@ -17,10 +17,13 @@ The script builds `zig-out/bin/zmr`, then runs:
|
|
|
17
17
|
- `zmr validate examples/android-app-referral-deep-link.json`
|
|
18
18
|
- `zmr validate examples/android-app-error-state.json`
|
|
19
19
|
- `zmr validate examples/android-shim-smoke.json`
|
|
20
|
+
- `zmr validate examples/android-workflow.json`
|
|
21
|
+
- `zmr validate examples/react-native-expo-workflow.json`
|
|
20
22
|
- `zmr validate examples/ios-smoke.json`
|
|
21
23
|
- `zmr validate examples/ios-dev-client-open-link.json`
|
|
22
24
|
- `zmr validate examples/ios-dev-client-route-snapshot.json`
|
|
23
25
|
- `zmr validate examples/ios-shim-smoke.json`
|
|
26
|
+
- `zmr validate examples/ios-shim-workflow.json`
|
|
24
27
|
- expected-failing `zmr validate --json` output that shows `fieldPath`, `line`,
|
|
25
28
|
and `column` for invalid scenarios covered by `schemas/validate-output.schema.json`
|
|
26
29
|
- `zmr doctor --adb ./tests/fake-adb.sh --xcrun ./tests/fake-xcrun.sh --ios-shim ./tests/fake-ios-shim.sh`
|
|
@@ -81,6 +84,8 @@ The Swift and Kotlin reference client flows verify host-side native-language
|
|
|
81
84
|
agent/test-harness integration for iOS and Android teams.
|
|
82
85
|
The fake Android shim flow exercises shim-backed hierarchy, wait, tap, type,
|
|
83
86
|
hide-keyboard, and snapshot handling.
|
|
87
|
+
The React Native/Expo workflow example validates the framework fixture scenario
|
|
88
|
+
that uses deep links, accessibility labels, and stable `testID` values.
|
|
84
89
|
The fake iOS flow exercises simulator lifecycle, deep-link opening, screenshot
|
|
85
90
|
artifact capture, log capture, and snapshot trace writing. The fake iOS shim
|
|
86
91
|
flow exercises shim-backed hierarchy, wait, tap, type, hide-keyboard, and
|
|
@@ -88,7 +93,11 @@ snapshot handling.
|
|
|
88
93
|
|
|
89
94
|
Load any generated `.zmrtrace` in `viewer/index.html` to inspect the replay
|
|
90
95
|
timeline, payloads, screenshot, UI tree, selected node details, and raw
|
|
91
|
-
artifacts side-by-side.
|
|
96
|
+
artifacts side-by-side. When the viewer and the bundle are served over HTTP,
|
|
97
|
+
link straight to a loaded trace with `viewer/index.html?bundle=<url>` — useful
|
|
98
|
+
for CI artifact links and shared triage.
|
|
99
|
+
|
|
100
|
+

|
|
92
101
|
|
|
93
102
|
## Real Android Pilot Demo
|
|
94
103
|
|
|
@@ -185,6 +194,32 @@ zmr run /tmp/zmr-android-demo/.zmr/android-smoke.json \
|
|
|
185
194
|
The scenario launches the app, waits for visible text, taps a button, types
|
|
186
195
|
text into a field, and captures a trace-backed snapshot.
|
|
187
196
|
|
|
197
|
+
## React Native And Expo Fixture
|
|
198
|
+
|
|
199
|
+
To generate a public React Native and Expo app with stable test IDs,
|
|
200
|
+
accessibility labels, deep-link routing, and matching Android/iOS ZMR workflow
|
|
201
|
+
scenarios:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npx zmr-create-react-native-expo-demo-app --out /tmp/zmr-rn-expo-demo
|
|
205
|
+
cd /tmp/zmr-rn-expo-demo
|
|
206
|
+
bun install
|
|
207
|
+
bunx expo start
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
After installing a development build on the target device, run the generated
|
|
211
|
+
scenario for the platform you are measuring:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
zmr run .zmr/react-native-expo-android-workflow.json \
|
|
215
|
+
--device emulator-5554 \
|
|
216
|
+
--app-id com.example.mobiletest \
|
|
217
|
+
--trace-dir traces/zmr-rn-expo-android
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
The fixture includes `expo-dev-client` and is available for benchmark
|
|
221
|
+
collection, but it does not have public timing rows yet.
|
|
222
|
+
|
|
188
223
|
## Real iOS Simulator Demo
|
|
189
224
|
|
|
190
225
|
To generate a small public demo app with the ZMR XCTest shim already installed:
|
package/docs/frameworks.md
CHANGED
|
@@ -20,6 +20,16 @@ Keep generated ZMR files under `.zmr/` and run the wizard from the app repo:
|
|
|
20
20
|
npx zmr-wizard --app-id com.example.mobiletest --package-json
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
To inspect a generated public fixture with a longer workflow:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx zmr-create-react-native-expo-demo-app --out /tmp/zmr-rn-expo-demo
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
That fixture includes `expo-dev-client`, deep-link setup, stable `testID`
|
|
30
|
+
values, accessibility labels, and Android/iOS ZMR workflow scenarios under
|
|
31
|
+
`.zmr/`.
|
|
32
|
+
|
|
23
33
|
## Expo
|
|
24
34
|
|
|
25
35
|
Expo development builds work like React Native apps once they are installed on a
|
package/docs/npm.md
CHANGED
|
@@ -33,6 +33,9 @@ The package exposes:
|
|
|
33
33
|
matching `.zmr/` smoke scenario for public demos and emulator pilots.
|
|
34
34
|
- `zmr-create-ios-demo-app`: creates a generic SwiftUI simulator app with
|
|
35
35
|
`.zmr/` scenarios and the iOS shim already installed for public demos.
|
|
36
|
+
- `zmr-create-react-native-expo-demo-app`: creates a generic React Native and
|
|
37
|
+
Expo app with stable `testID` values, accessibility labels, deep-link config,
|
|
38
|
+
and Android/iOS `.zmr/` workflow scenarios.
|
|
36
39
|
- `zmr-demo-android`: creates, installs, and runs the generated Android demo
|
|
37
40
|
through a real emulator/device.
|
|
38
41
|
- `zmr-demo-ios`: creates, builds, and runs the generated iOS simulator demo
|
|
@@ -216,6 +219,24 @@ zmr run /tmp/zmr-android-demo/.zmr/android-smoke.json \
|
|
|
216
219
|
--trace-dir /tmp/zmr-android-demo/traces/android-demo
|
|
217
220
|
```
|
|
218
221
|
|
|
222
|
+
## React Native And Expo Demo Fixture
|
|
223
|
+
|
|
224
|
+
Generate a public React Native and Expo app when you need a framework-level
|
|
225
|
+
benchmark fixture before collecting timing rows:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
npx zmr-create-react-native-expo-demo-app --out /tmp/zmr-rn-expo-demo
|
|
229
|
+
cd /tmp/zmr-rn-expo-demo
|
|
230
|
+
bun install
|
|
231
|
+
bunx expo start
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The generated app includes `expo-dev-client`, stable `testID` values,
|
|
235
|
+
accessibility labels, an Expo deep-link scheme, and platform-specific ZMR
|
|
236
|
+
workflow scenarios under `.zmr/`. After installing a development build on the
|
|
237
|
+
target device, run the generated Android or iOS workflow scenario from the app
|
|
238
|
+
directory.
|
|
239
|
+
|
|
219
240
|
## iOS Demo App
|
|
220
241
|
|
|
221
242
|
For a clean public iOS demo that does not depend on a private app:
|
|
@@ -349,8 +370,12 @@ on the tag workflow:
|
|
|
349
370
|
|
|
350
371
|
- Package: `zeno-mobile-runner`
|
|
351
372
|
- Provider: GitHub Actions
|
|
352
|
-
-
|
|
353
|
-
-
|
|
373
|
+
- Organization or user: `johnmikel`
|
|
374
|
+
- Repository: `zeno-mobile-runner`
|
|
375
|
+
- Workflow filename: `release.yml`
|
|
376
|
+
- Environment name: leave blank unless the release job also declares a GitHub
|
|
377
|
+
deployment environment.
|
|
378
|
+
- Allowed actions: `npm publish`
|
|
354
379
|
|
|
355
380
|
The release workflow already requests `id-token: write`, builds the npm tarball
|
|
356
381
|
from the tag, attests the generated release artifacts, uploads the GitHub
|
|
@@ -361,6 +386,23 @@ Trusted publishing requires a current npm runtime. The tag workflow uses Node
|
|
|
361
386
|
24 so the npm CLI can exchange the GitHub Actions OIDC identity for publish
|
|
362
387
|
authorization.
|
|
363
388
|
|
|
389
|
+
With `npm@11.10.0` or newer, maintainers can also configure the same trust
|
|
390
|
+
relationship from an authenticated local shell:
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
npm trust list zeno-mobile-runner
|
|
394
|
+
npm trust github zeno-mobile-runner \
|
|
395
|
+
--repo johnmikel/zeno-mobile-runner \
|
|
396
|
+
--file release.yml \
|
|
397
|
+
--allow-publish
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
If `npm trust` is not available, update npm or use the package settings page on
|
|
401
|
+
npmjs.com. A failed publish with `E404` for an existing package usually means
|
|
402
|
+
the trusted-publisher configuration is missing, points at a different GitHub
|
|
403
|
+
owner/repository/workflow filename, names an environment that the workflow does
|
|
404
|
+
not use, or does not allow `npm publish`.
|
|
405
|
+
|
|
364
406
|
### Manual publish with passkey or 2FA
|
|
365
407
|
|
|
366
408
|
Use trusted publishing for normal tagged releases. If you need to publish a
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.1
|
|
1
|
+
{"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.2.1","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","scenario.validate","trace.events","trace.explore","trace.discover","trace.explain","trace.export"]}}
|
|
2
2
|
{"jsonrpc":"2.0","id":2,"result":[{"serial":"fake-device-1","state":"device","ready":true}]}
|
|
3
3
|
{"jsonrpc":"2.0","id":3,"result":{"sessionId":"default"}}
|
|
4
4
|
{"jsonrpc":"2.0","id":4,"result":true}
|
package/docs/protocol.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
ZMR exposes newline-delimited JSON-RPC 2.0 over stdio or localhost TCP in v1. Each request is one JSON object followed by `\n`. Each response is one JSON object followed by `\n`.
|
|
4
4
|
|
|
5
|
-
Current runner version: `0.1
|
|
5
|
+
Current runner version: `0.2.1`.
|
|
6
6
|
|
|
7
7
|
Current protocol version: `2026-04-28`.
|
|
8
8
|
|
|
@@ -47,7 +47,7 @@ and protocol versions. The response is covered by
|
|
|
47
47
|
`schemas/inspect-output.schema.json`:
|
|
48
48
|
|
|
49
49
|
```json
|
|
50
|
-
{"ok":true,"status":"ready","schemaVersion":1,"runnerVersion":"0.1
|
|
50
|
+
{"ok":true,"status":"ready","schemaVersion":1,"runnerVersion":"0.2.1","protocolVersion":"2026-04-28","dir":".","configPath":".zmr/config.json","configExists":true,"agentInstructionsPath":".zmr/AGENTS.md","agentInstructionsExists":true,"platforms":[{"name":"android","enabled":true,"defaultDevice":"emulator-5554","smokeScenario":".zmr/android-smoke.json","smokeScenarioExists":true,"traceDir":"traces/zmr-android"},{"name":"ios","enabled":true,"defaultDevice":"booted","smokeScenario":".zmr/ios-smoke.json","smokeScenarioExists":true,"traceDir":"traces/zmr-ios"}],"recommendedCommands":["zmr doctor --strict --json --config .zmr/config.json","zmr schemas --json","zmr validate --json .zmr/android-smoke.json","zmr validate --json .zmr/ios-smoke.json","zmr serve --transport stdio --config .zmr/config.json --trace-dir traces/zmr-agent","zmr mcp --config .zmr/config.json --trace-dir traces/zmr-agent"],"claimsPolicy":["verify runs with trace evidence before making readiness claims","do not claim Flutter widget-tree inspection"],"limitations":["inspect is read-only and does not launch devices","autonomous crawling is not shipped; generate or edit scenarios for human review"]}
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
`zmr discover --from-trace <trace-dir> --out <scenario.json> --validate --json`
|
|
@@ -60,7 +60,7 @@ invent credentials, or commit files. The response is covered by
|
|
|
60
60
|
`schemas/discover-output.schema.json`:
|
|
61
61
|
|
|
62
62
|
```json
|
|
63
|
-
{"ok":true,"mode":"discover","schemaVersion":1,"runnerVersion":"0.1
|
|
63
|
+
{"ok":true,"mode":"discover","schemaVersion":1,"runnerVersion":"0.2.1","protocolVersion":"2026-04-28","out":".zmr/discovered/replay-smoke.json","traceDir":"traces/zmr-agent","sourceSnapshot":"traces/zmr-agent/artifacts/snapshot-2.json","name":"draft from login smoke","appId":"com.example.mobiletest","selectorCount":2,"stepCount":6,"replay":{"enabled":true,"eventCount":4,"stepCount":3,"skippedEventCount":1},"warnings":["draft requires human review before commit"],"validated":true,"validation":{"ok":true,"path":".zmr/discovered/replay-smoke.json","name":"draft from login smoke","appId":"com.example.mobiletest","stepCount":6},"nextCommands":["zmr validate --json .zmr/discovered/replay-smoke.json","zmr run .zmr/discovered/replay-smoke.json --json --trace-dir traces/zmr-agent"]}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
`zmr explore --from-trace <trace-dir> --out <scenario.json> --goal <goal>
|
|
@@ -71,7 +71,7 @@ launch devices, crawl the app, invent missing actions, discover credentials, or
|
|
|
71
71
|
commit files. The response is covered by `schemas/explore-output.schema.json`:
|
|
72
72
|
|
|
73
73
|
```json
|
|
74
|
-
{"ok":true,"mode":"explore","schemaVersion":1,"runnerVersion":"0.1
|
|
74
|
+
{"ok":true,"mode":"explore","schemaVersion":1,"runnerVersion":"0.2.1","protocolVersion":"2026-04-28","goal":"find a stable login smoke","autonomous":false,"reviewRequired":true,"guardrails":["writes from existing trace evidence only","does not crawl the app","does not discover credentials or secrets","requires human review before commit"],"out":".zmr/discovered/login-smoke.json","traceDir":"traces/zmr-agent","sourceSnapshot":"traces/zmr-agent/artifacts/snapshot-2.json","name":"draft from login smoke","appId":"com.example.mobiletest","selectorCount":2,"stepCount":6,"replay":{"enabled":true,"eventCount":4,"stepCount":3,"skippedEventCount":1},"warnings":["draft requires human review before commit"],"validated":true,"validation":{"ok":true,"path":".zmr/discovered/login-smoke.json","name":"draft from login smoke","appId":"com.example.mobiletest","stepCount":6},"nextCommands":["zmr validate --json .zmr/discovered/login-smoke.json","zmr run .zmr/discovered/login-smoke.json --json --trace-dir traces/zmr-agent"]}
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
`zmr draft --from-trace <trace-dir> --out <scenario.json> --json` is the lower
|
|
@@ -84,7 +84,7 @@ into fields, or commit files. The response is covered by
|
|
|
84
84
|
`schemas/draft-output.schema.json`:
|
|
85
85
|
|
|
86
86
|
```json
|
|
87
|
-
{"ok":true,"mode":"draft","schemaVersion":1,"runnerVersion":"0.1
|
|
87
|
+
{"ok":true,"mode":"draft","schemaVersion":1,"runnerVersion":"0.2.1","protocolVersion":"2026-04-28","out":".zmr/discovered/surface-smoke.json","traceDir":"traces/zmr-agent","sourceSnapshot":"traces/zmr-agent/artifacts/snapshot-2.json","name":"draft from login smoke","appId":"com.example.mobiletest","selectorCount":2,"stepCount":4,"replay":{"enabled":false,"eventCount":0,"stepCount":0,"skippedEventCount":0},"warnings":["draft requires human review before commit"],"nextCommands":["zmr validate --json .zmr/discovered/surface-smoke.json","zmr run .zmr/discovered/surface-smoke.json --json --trace-dir traces/zmr-agent"]}
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
`zmr draft --include-actions` additionally parses `events.jsonl` and prepends
|
|
@@ -214,7 +214,7 @@ installers, setup scripts, and generated clients. The response is covered by
|
|
|
214
214
|
`schemas/version-output.schema.json`:
|
|
215
215
|
|
|
216
216
|
```json
|
|
217
|
-
{"name":"zmr","version":"0.1
|
|
217
|
+
{"name":"zmr","version":"0.2.1","protocolVersion":"2026-04-28","minimumCompatibleProtocolVersion":"2026-04-28","stability":"dev-preview","breakingChangePolicy":"version-and-changelog"}
|
|
218
218
|
```
|
|
219
219
|
|
|
220
220
|
## Capabilities Output Contract
|
|
@@ -226,7 +226,7 @@ and method inventory for JSON-RPC clients. The result object is covered by
|
|
|
226
226
|
iOS simulator, or physical iOS workflows are available.
|
|
227
227
|
|
|
228
228
|
```json
|
|
229
|
-
{"name":"zmr","version":"0.1
|
|
229
|
+
{"name":"zmr","version":"0.2.1","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","scenario.validate","trace.events","trace.explore","trace.discover","trace.explain","trace.export"]}
|
|
230
230
|
```
|
|
231
231
|
|
|
232
232
|
## Doctor Output Contract
|
|
@@ -432,7 +432,7 @@ Request:
|
|
|
432
432
|
Response:
|
|
433
433
|
|
|
434
434
|
```json
|
|
435
|
-
{"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.1
|
|
435
|
+
{"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.2.1","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","scenario.validate","trace.events","trace.explore","trace.discover","trace.explain","trace.export"]}}
|
|
436
436
|
```
|
|
437
437
|
|
|
438
438
|
### `trace.events`
|
|
@@ -514,7 +514,7 @@ Request:
|
|
|
514
514
|
Response:
|
|
515
515
|
|
|
516
516
|
```json
|
|
517
|
-
{"jsonrpc":"2.0","id":25,"result":{"ok":true,"mode":"discover","schemaVersion":1,"runnerVersion":"0.1
|
|
517
|
+
{"jsonrpc":"2.0","id":25,"result":{"ok":true,"mode":"discover","schemaVersion":1,"runnerVersion":"0.2.1","protocolVersion":"2026-04-28","out":".zmr/discovered/agent-smoke.json","traceDir":"traces/agent-session","sourceSnapshot":"traces/agent-session/artifacts/snapshot-1.json","name":"agent smoke","appId":"com.example.mobiletest","selectorCount":1,"stepCount":4,"replay":{"enabled":true,"eventCount":2,"stepCount":1,"skippedEventCount":1},"warnings":["draft requires human review before commit"],"validated":true,"validation":{"ok":true,"path":".zmr/discovered/agent-smoke.json","name":"agent smoke","appId":"com.example.mobiletest","stepCount":4},"nextCommands":["zmr validate --json .zmr/discovered/agent-smoke.json","zmr run .zmr/discovered/agent-smoke.json --json --trace-dir traces/agent-session"]}}
|
|
518
518
|
```
|
|
519
519
|
|
|
520
520
|
Without `--trace-dir`, it returns `ok: false` with `traceDir: null`. Generated
|
|
@@ -537,7 +537,7 @@ Request:
|
|
|
537
537
|
Response:
|
|
538
538
|
|
|
539
539
|
```json
|
|
540
|
-
{"jsonrpc":"2.0","id":27,"result":{"ok":true,"mode":"explore","schemaVersion":1,"runnerVersion":"0.1
|
|
540
|
+
{"jsonrpc":"2.0","id":27,"result":{"ok":true,"mode":"explore","schemaVersion":1,"runnerVersion":"0.2.1","protocolVersion":"2026-04-28","out":".zmr/discovered/agent-goal.json","traceDir":"traces/agent-session","sourceSnapshot":"traces/agent-session/artifacts/snapshot-1.json","name":"agent goal smoke","appId":"com.example.mobiletest","selectorCount":1,"stepCount":4,"replay":{"enabled":true,"eventCount":2,"stepCount":1,"skippedEventCount":1},"warnings":["draft requires human review before commit"],"validated":true,"validation":{"ok":true,"path":".zmr/discovered/agent-goal.json","name":"agent goal smoke","appId":"com.example.mobiletest","stepCount":4},"nextCommands":["zmr validate --json .zmr/discovered/agent-goal.json","zmr run .zmr/discovered/agent-goal.json --json --trace-dir traces/agent-session"],"goal":"find a stable login smoke","autonomous":false,"reviewRequired":true,"guardrails":["writes from existing trace evidence only","does not crawl the app","does not discover credentials or secrets","requires human review before commit"]}}
|
|
541
541
|
```
|
|
542
542
|
|
|
543
543
|
Without `--trace-dir`, it returns `ok: false` with `traceDir: null`.
|
|
@@ -4,6 +4,19 @@ ZMR scenarios are JSON so agents can generate and mutate them without a second
|
|
|
4
4
|
DSL. JSON is strict, schema-validatable, and easy for agents and code generators
|
|
5
5
|
to emit. Keep scenarios explicit, short, and biased toward stable selectors.
|
|
6
6
|
|
|
7
|
+
Scenarios can be written by hand, or generated review-first from the trace of
|
|
8
|
+
a live session:
|
|
9
|
+
|
|
10
|
+
```mermaid
|
|
11
|
+
flowchart LR
|
|
12
|
+
SESSION["Live agent session<br/>or zmr run"] --> TRACE["Trace directory"]
|
|
13
|
+
TRACE --> DISCOVER["zmr discover / draft / explore<br/>--from-trace"]
|
|
14
|
+
DISCOVER --> CANDIDATE["Scenario candidate<br/>.zmr/discovered/*.json"]
|
|
15
|
+
CANDIDATE --> REVIEW["Human / agent review"]
|
|
16
|
+
REVIEW --> VALIDATE["zmr validate --json"]
|
|
17
|
+
VALIDATE --> CI["zmr run in CI<br/>report.html · junit.xml"]
|
|
18
|
+
```
|
|
19
|
+
|
|
7
20
|
## Selector Strategy
|
|
8
21
|
|
|
9
22
|
Prefer selectors in this order:
|
|
@@ -83,8 +96,10 @@ The example directory includes templates for common app flows:
|
|
|
83
96
|
- `examples/android-app-onboarding.json`
|
|
84
97
|
- `examples/android-app-referral-deep-link.json`
|
|
85
98
|
- `examples/android-app-error-state.json`
|
|
99
|
+
- `examples/android-workflow.json`
|
|
86
100
|
- `examples/ios-dev-client-open-link.json`
|
|
87
101
|
- `examples/ios-dev-client-route-snapshot.json`
|
|
102
|
+
- `examples/ios-shim-workflow.json`
|
|
88
103
|
|
|
89
104
|
Run `zmr validate --json <scenario.json>` before touching a device. Invalid
|
|
90
105
|
scenarios report `fieldPath`, `line`, and `column` when ZMR can identify the
|
package/docs/trace-privacy.md
CHANGED
|
@@ -3,6 +3,15 @@
|
|
|
3
3
|
ZMR traces are debugging artifacts. They can contain sensitive app state even
|
|
4
4
|
when scenario files are generic.
|
|
5
5
|
|
|
6
|
+
```mermaid
|
|
7
|
+
flowchart LR
|
|
8
|
+
RUN["zmr run / live session"] --> DIR["Trace directory<br/>events.jsonl · screenshots ·<br/>UI trees · logs · timings"]
|
|
9
|
+
DIR --> REPORT["zmr report<br/>report.html · junit.xml"]
|
|
10
|
+
DIR --> EXPLAIN["zmr explain<br/>failure diagnosis"]
|
|
11
|
+
DIR --> EXPORT["zmr export --redact<br/>.zmrtrace bundle"]
|
|
12
|
+
EXPORT --> VIEWER["Static trace viewer<br/>or shared evidence"]
|
|
13
|
+
```
|
|
14
|
+
|
|
6
15
|
Raw trace directories may include:
|
|
7
16
|
|
|
8
17
|
- screenshots
|
package/docs/troubleshooting.md
CHANGED
|
@@ -188,6 +188,12 @@ app targets. Pass `--project` explicitly for still-ambiguous multi-project
|
|
|
188
188
|
workspaces. Run with `--ios-shim ./.zmr/ios-shim` or set
|
|
189
189
|
`tools.iosShimPath` in `.zmr/config.json`.
|
|
190
190
|
|
|
191
|
+
A clean prebuild can push the shim's first `build-for-testing` through a full
|
|
192
|
+
native dependency compile. ZMR waits up to 90 minutes by default; on slower CI
|
|
193
|
+
hardware, raise the ceiling with the `ZMR_IOS_SHIM_TIMEOUT_MS` environment
|
|
194
|
+
variable (milliseconds), for example `ZMR_IOS_SHIM_TIMEOUT_MS=10800000` for
|
|
195
|
+
three hours.
|
|
196
|
+
|
|
191
197
|
If a real iOS run fails with CoreSimulator or Xcode cache errors such as
|
|
192
198
|
`Operation not permitted`, `CoreSimulatorService connection became invalid`, or
|
|
193
199
|
an unexpected workspace/build database error, rerun from a normal terminal or CI
|