zeno-mobile-runner 0.2.14 → 0.2.15

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 CHANGED
@@ -4,6 +4,17 @@ All notable changes to Zeno Mobile Runner are tracked here.
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.2.15 (2026-06-24)
8
+
9
+ ### Fixed
10
+
11
+ - Generated iOS shim commands now remove app-local ZMR-owned derived data paths
12
+ ending in `ZMRDerivedData` before `build-for-testing` refreshes. This avoids
13
+ reusing stale Xcode absolute paths when app checkouts are copied, while
14
+ refusing to delete arbitrary shared DerivedData locations.
15
+ - Release gates now verify that `ZMR_VERSION`, `package.json`, `src/version.zig`,
16
+ archive names, and release-smoked binaries agree before publishing.
17
+
7
18
  ## 0.2.14 (2026-06-23)
8
19
 
9
20
  ### Added
package/FEATURES.md CHANGED
@@ -142,7 +142,7 @@ state, and writes deterministic traces. It does not embed an LLM.
142
142
 
143
143
  ## Current Limitations
144
144
 
145
- - Current release status is `0.2.14`, a public developer preview rather than
145
+ - Current release status is `0.2.15`, a public developer preview rather than
146
146
  a production-stable `1.0.0`.
147
147
  - Physical iOS log capture is still simulator-first. Physical iOS screenshots
148
148
  are available when the XCTest/XCUIAutomation shim is configured.
package/README.md CHANGED
@@ -199,7 +199,7 @@ comparisons against your current E2E tool, and multi-device matrices, see
199
199
  Slow CI hardware can extend the generated iOS shim build timeout with
200
200
  `ZMR_IOS_SHIM_BUILD_TIMEOUT_SECONDS`; `ZMR_IOS_SHIM_RESPONSE_TIMEOUT_SECONDS`
201
201
  bounds each in-flight request, and `ZMR_IOS_SHIM_TIMEOUT_MS` remains the outer
202
- process ceiling. Current release: `0.2.14` developer preview.
202
+ process ceiling. Current release: `0.2.15` developer preview.
203
203
  Protocol version: `2026-04-28`.
204
204
 
205
205
  ## Optional protocol clients
@@ -27,7 +27,7 @@ gradle -p clients/kotlin runFakeSession \
27
27
  ```
28
28
 
29
29
  ```kotlin
30
- implementation(files("path/to/zeno-mobile-runner/clients/kotlin/build/libs/zmr-client-0.2.14.jar"))
30
+ implementation(files("path/to/zeno-mobile-runner/clients/kotlin/build/libs/zmr-client-0.2.15.jar"))
31
31
  ```
32
32
 
33
33
  ```kotlin
@@ -4,7 +4,7 @@ plugins {
4
4
  }
5
5
 
6
6
  group = "dev.zmr"
7
- version = "0.2.14"
7
+ version = "0.2.15"
8
8
 
9
9
  kotlin {
10
10
  jvmToolchain(17)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "zmr-client"
7
- version = "0.2.14.dev1"
7
+ version = "0.2.15.dev1"
8
8
  description = "Python JSON-RPC client for Zeno Mobile Runner."
9
9
  requires-python = ">=3.9"
10
10
  license = { text = "MIT" }
@@ -100,7 +100,7 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
100
100
 
101
101
  [[package]]
102
102
  name = "zmr-client"
103
- version = "0.2.14"
103
+ version = "0.2.15"
104
104
  dependencies = [
105
105
  "serde",
106
106
  "serde_json",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "zmr-client"
3
- version = "0.2.14"
3
+ version = "0.2.15"
4
4
  edition = "2021"
5
5
  license = "MIT"
6
6
  description = "Rust JSON-RPC client for Zeno Mobile Runner."
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zmr/client",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "type": "module",
5
5
  "main": "index.mjs",
6
6
  "types": "index.d.ts",
@@ -101,6 +101,10 @@ with `--ios-shim`. It caches `build-for-testing` output and uses
101
101
  `test-without-building` for selector commands through `.zmr/ios-shim-state/`.
102
102
  Set `ZMR_IOS_SHIM_FORCE_REBUILD=1` after app-side target changes, or
103
103
  `ZMR_IOS_SHIM_ONESHOT=1` when you need to debug the slower cold-start path.
104
+ When `--derived-data-path` points at a ZMR-owned path ending in
105
+ `ZMRDerivedData`, the generated shim removes that directory before each
106
+ `build-for-testing` refresh so copied app checkouts do not reuse stale absolute
107
+ Xcode paths. It refuses to delete arbitrary shared DerivedData locations.
104
108
 
105
109
  ## Recommended App Repo Layout
106
110
 
package/docs/npm.md CHANGED
@@ -333,7 +333,10 @@ The generated command caches `build-for-testing` output under
333
333
  prints the last Xcode log lines when XCTest fails. Set
334
334
  `ZMR_IOS_SHIM_FORCE_REBUILD=1` after app-side target changes, or
335
335
  `ZMR_IOS_SHIM_ONESHOT=1` for a cold-start fallback while debugging app-side Xcode
336
- wiring.
336
+ wiring. When `--derived-data-path` points at a ZMR-owned path ending in
337
+ `ZMRDerivedData`, the generated command removes that directory before each
338
+ `build-for-testing` refresh so copied app checkouts do not reuse stale absolute
339
+ Xcode paths. It refuses to delete arbitrary shared DerivedData locations.
337
340
 
338
341
  ## Native Binary Resolution
339
342
 
@@ -1,4 +1,4 @@
1
- {"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.2.14","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"]}}
1
+ {"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.2.15","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.2.14`.
5
+ Current runner version: `0.2.15`.
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.2.14","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"]}
50
+ {"ok":true,"status":"ready","schemaVersion":1,"runnerVersion":"0.2.15","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.2.14","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"]}
63
+ {"ok":true,"mode":"discover","schemaVersion":1,"runnerVersion":"0.2.15","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.2.14","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"]}
74
+ {"ok":true,"mode":"explore","schemaVersion":1,"runnerVersion":"0.2.15","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.2.14","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"]}
87
+ {"ok":true,"mode":"draft","schemaVersion":1,"runnerVersion":"0.2.15","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.2.14","protocolVersion":"2026-04-28","minimumCompatibleProtocolVersion":"2026-04-28","stability":"dev-preview","breakingChangePolicy":"version-and-changelog"}
217
+ {"name":"zmr","version":"0.2.15","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.2.14","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"]}
229
+ {"name":"zmr","version":"0.2.15","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
@@ -433,7 +433,7 @@ Request:
433
433
  Response:
434
434
 
435
435
  ```json
436
- {"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.2.14","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
+ {"jsonrpc":"2.0","id":1,"result":{"name":"zmr","version":"0.2.15","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"]}}
437
437
  ```
438
438
 
439
439
  ### `trace.events`
@@ -515,7 +515,7 @@ Request:
515
515
  Response:
516
516
 
517
517
  ```json
518
- {"jsonrpc":"2.0","id":25,"result":{"ok":true,"mode":"discover","schemaVersion":1,"runnerVersion":"0.2.14","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
+ {"jsonrpc":"2.0","id":25,"result":{"ok":true,"mode":"discover","schemaVersion":1,"runnerVersion":"0.2.15","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"]}}
519
519
  ```
520
520
 
521
521
  Without `--trace-dir`, it returns `ok: false` with `traceDir: null`. Generated
@@ -538,7 +538,7 @@ Request:
538
538
  Response:
539
539
 
540
540
  ```json
541
- {"jsonrpc":"2.0","id":27,"result":{"ok":true,"mode":"explore","schemaVersion":1,"runnerVersion":"0.2.14","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
+ {"jsonrpc":"2.0","id":27,"result":{"ok":true,"mode":"explore","schemaVersion":1,"runnerVersion":"0.2.15","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"]}}
542
542
  ```
543
543
 
544
544
  Without `--trace-dir`, it returns `ok: false` with `traceDir: null`.
@@ -208,8 +208,10 @@ This avoids failing the whole demo when one local simulator cannot start
208
208
  `launchd_sim`, while still surfacing a setup error if every available simulator
209
209
  fails to boot.
210
210
 
211
- If a previous Xcode build was interrupted, remove only the app-local ZMR derived
212
- data path configured for the shim, then rerun the shim once to prewarm it:
211
+ The generated shim now removes app-local ZMR derived data paths ending in
212
+ `ZMRDerivedData` before each `build-for-testing` refresh. If you intentionally
213
+ configured a different shared DerivedData path, clean only the app-local ZMR
214
+ cache you own, then rerun the shim once to prewarm it:
213
215
 
214
216
  ```bash
215
217
  rm -rf ios/build/ZMRDerivedData
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeno-mobile-runner",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "description": "Agent-native mobile app test runner for React Native, Expo, Flutter, and native Android/iOS.",
5
5
  "main": "npm/index.mjs",
6
6
  "repository": {
@@ -82,6 +82,7 @@
82
82
  "!scripts/release-candidate.sh",
83
83
  "!scripts/release-gate.sh",
84
84
  "!scripts/release-smoke.sh",
85
+ "!scripts/verify-release-version.sh",
85
86
  "!scripts/sign-macos-release.sh",
86
87
  "!scripts/verify-release-artifacts.sh",
87
88
  "!scripts/__pycache__/",
Binary file
Binary file
Binary file
Binary file
@@ -223,6 +223,7 @@ READY_FILE="\$SERVER_DIR/ready"
223
223
  DESTINATION_ID_FILE="\$STATE_DIR/destination.id"
224
224
  BUILD_READY_FILE="\$STATE_DIR/build-for-testing.ready"
225
225
  LOG_FILE="\$STATE_DIR/xcodebuild.log"
226
+ DERIVED_DATA_PATH_VALUE="$DERIVED_DATA_PATH"
226
227
  STDIN_FILE="\$(mktemp)"
227
228
  trap 'rm -f "\$STDIN_FILE"' EXIT
228
229
 
@@ -332,6 +333,31 @@ destination_spec() {
332
333
  printf 'platform=%s,id=%s' "\$platform_name" "\$destination_id"
333
334
  }
334
335
 
336
+ clean_zmr_derived_data() {
337
+ if [[ -z "\$DERIVED_DATA_PATH_VALUE" ]]; then
338
+ return 0
339
+ fi
340
+
341
+ local derived_data_abs
342
+ if [[ "\$DERIVED_DATA_PATH_VALUE" == /* ]]; then
343
+ derived_data_abs="\$DERIVED_DATA_PATH_VALUE"
344
+ else
345
+ derived_data_abs="$APP_ROOT/\$DERIVED_DATA_PATH_VALUE"
346
+ fi
347
+ while [[ "\$derived_data_abs" == */ && "\$derived_data_abs" != "/" ]]; do
348
+ derived_data_abs="\${derived_data_abs%/}"
349
+ done
350
+
351
+ case "\$derived_data_abs" in
352
+ "$APP_ROOT/ZMRDerivedData"|"$APP_ROOT"/*/ZMRDerivedData)
353
+ rm -rf "\$derived_data_abs"
354
+ ;;
355
+ *)
356
+ echo "warning: refusing to delete non-ZMR derived data path: \$DERIVED_DATA_PATH_VALUE" >&2
357
+ ;;
358
+ esac
359
+ }
360
+
335
361
  is_server_running() {
336
362
  if [[ ! -f "\$PID_FILE" ]]; then
337
363
  return 1
@@ -399,6 +425,7 @@ build_for_testing() {
399
425
  local destination_id build_log
400
426
  destination_id="\$(destination_spec)"
401
427
  build_log="\$STATE_DIR/xcodebuild.build.log"
428
+ clean_zmr_derived_data
402
429
 
403
430
  run_xcodebuild_with_timeout "iOS shim build-for-testing" "\${ZMR_IOS_SHIM_BUILD_TIMEOUT_SECONDS:-5400}" "\$build_log" \\
404
431
  xcodebuild build-for-testing \\
@@ -19,7 +19,10 @@ Current status:
19
19
  selector commands through `test-without-building`, exchanging per-command
20
20
  files under `.zmr/ios-shim-state/`. Set `ZMR_IOS_SHIM_FORCE_REBUILD=1` to
21
21
  refresh the cached test bundle, or `ZMR_IOS_SHIM_ONESHOT=1` to force the
22
- slower one-command XCTest fallback for debugging.
22
+ slower one-command XCTest fallback for debugging. When configured with a
23
+ ZMR-owned derived data path ending in `ZMRDerivedData`, the command removes
24
+ that directory before each `build-for-testing` refresh and refuses to delete
25
+ arbitrary shared DerivedData paths.
23
26
  - The iOS adapter still uses `xcrun simctl` for simulator install, launch,
24
27
  terminate, open link, screenshots, and logs. It uses `xcrun devicectl` for
25
28
  physical-device lifecycle where Apple exposes a supported local command, and
package/src/version.zig CHANGED
@@ -1,4 +1,4 @@
1
- pub const runner_version = "0.2.14";
1
+ pub const runner_version = "0.2.15";
2
2
  pub const protocol_version = "2026-04-28";
3
3
  pub const protocol_min_compatible_version = "2026-04-28";
4
4
  pub const protocol_stability = "dev-preview";