mobile-debug-mcp 0.32.4 → 0.34.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 (170) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude-plugin/marketplace.json +14 -0
  3. package/.cursor-plugin/marketplace.json +16 -0
  4. package/.github/copilot-instructions.md +7 -0
  5. package/.github/workflows/ci.yml +21 -0
  6. package/AGENTS.md +21 -10
  7. package/README.md +69 -4
  8. package/agent-plugin/.claude-plugin/plugin.json +20 -0
  9. package/agent-plugin/.codex-plugin/plugin.json +34 -0
  10. package/agent-plugin/.cursor-plugin/plugin.json +23 -0
  11. package/agent-plugin/.mcp.json +14 -0
  12. package/agent-plugin/README.md +9 -3
  13. package/agent-plugin/assets/mobile-debug-tools.svg +28 -0
  14. package/agent-plugin/mcp.json +1 -1
  15. package/agent-plugin/plugin.json +1 -1
  16. package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +113 -1
  17. package/dist/debugger/android.js +78 -0
  18. package/dist/debugger/guard.js +30 -0
  19. package/dist/debugger/sessions.js +262 -0
  20. package/dist/debugger/worker.js +165 -0
  21. package/dist/interact/android.js +10 -4
  22. package/dist/interact/conditional-action.js +68 -0
  23. package/dist/interact/index.js +70 -14
  24. package/dist/interact/ios.js +14 -8
  25. package/dist/manage/android.js +4 -3
  26. package/dist/manage/ios.js +3 -2
  27. package/dist/observe/android.js +25 -4
  28. package/dist/observe/evidence.js +280 -0
  29. package/dist/observe/index.js +33 -6
  30. package/dist/observe/ios.js +25 -6
  31. package/dist/observe/readiness-service.js +124 -0
  32. package/dist/observe/readiness.js +88 -0
  33. package/dist/observe/recording.js +165 -0
  34. package/dist/observe/snapshot-metadata.js +10 -2
  35. package/dist/observe/system-trace-android.js +135 -0
  36. package/dist/observe/system-trace.js +285 -0
  37. package/dist/server/common.js +9 -0
  38. package/dist/server/condition-schema.js +32 -0
  39. package/dist/server/debugger-handlers.js +38 -0
  40. package/dist/server/debugger-schema.js +42 -0
  41. package/dist/server/journey-schema.js +4 -2
  42. package/dist/server/readiness-schema.js +12 -0
  43. package/dist/server/system-trace-schema.js +17 -0
  44. package/dist/server/tool-definitions.js +51 -0
  45. package/dist/server/tool-handlers.js +294 -105
  46. package/dist/server-core.js +3 -3
  47. package/dist/server.js +34 -0
  48. package/dist/system/index.js +5 -4
  49. package/dist/utils/exec.js +2 -0
  50. package/dist/utils/ios/utils.js +4 -4
  51. package/dist/utils/java.js +14 -12
  52. package/dist/utils/operation-budget.js +26 -2
  53. package/dist/utils/resolve-device.js +9 -0
  54. package/docs/CHANGELOG.md +15 -0
  55. package/docs/specs/020-conditional-actions/intent.md +62 -0
  56. package/docs/specs/020-conditional-actions/plan.md +214 -0
  57. package/docs/specs/020-conditional-actions/spec.md +301 -0
  58. package/docs/specs/021-app-readiness-signals/intent.md +40 -0
  59. package/docs/specs/021-app-readiness-signals/plan.md +102 -0
  60. package/docs/specs/021-app-readiness-signals/spec.md +144 -0
  61. package/docs/specs/022-correlated-evidence/device-evidence.json +60 -0
  62. package/docs/specs/022-correlated-evidence/intent.md +37 -0
  63. package/docs/specs/022-correlated-evidence/modul8-evidence.json +1062 -0
  64. package/docs/specs/022-correlated-evidence/modul8-selector-fix-evidence.json +336 -0
  65. package/docs/specs/022-correlated-evidence/plan.md +171 -0
  66. package/docs/specs/022-correlated-evidence/spec.md +123 -0
  67. package/docs/specs/023-android-system-traces/device-evidence.json +38 -0
  68. package/docs/specs/023-android-system-traces/emulator-recheck.json +38 -0
  69. package/docs/specs/023-android-system-traces/intent.md +40 -0
  70. package/docs/specs/023-android-system-traces/plan.md +117 -0
  71. package/docs/specs/023-android-system-traces/spec.md +120 -0
  72. package/docs/specs/024-android-heap-dump-capture/intent.md +39 -0
  73. package/docs/specs/025-android-runtime-debugger/intent.md +40 -0
  74. package/docs/specs/025-android-runtime-debugger/plan.md +104 -0
  75. package/docs/specs/025-android-runtime-debugger/spec.md +182 -0
  76. package/docs/specs/process.md +213 -0
  77. package/docs/tools/TOOLS.md +5 -0
  78. package/docs/tools/debugger.md +104 -0
  79. package/docs/tools/evidence.md +82 -0
  80. package/docs/tools/interact.md +125 -2
  81. package/docs/tools/observe.md +70 -0
  82. package/docs/tools/system-traces.md +126 -0
  83. package/package.json +1 -1
  84. package/scripts/agent-plugin.mjs +68 -8
  85. package/src/debugger/android.ts +65 -0
  86. package/src/debugger/guard.ts +26 -0
  87. package/src/debugger/sessions.ts +168 -0
  88. package/src/debugger/worker.ts +122 -0
  89. package/src/interact/android.ts +10 -4
  90. package/src/interact/conditional-action.ts +75 -0
  91. package/src/interact/index.ts +57 -28
  92. package/src/interact/ios.ts +14 -8
  93. package/src/manage/android.ts +4 -3
  94. package/src/manage/ios.ts +3 -2
  95. package/src/observe/android.ts +33 -6
  96. package/src/observe/evidence.ts +262 -0
  97. package/src/observe/index.ts +32 -8
  98. package/src/observe/ios.ts +32 -8
  99. package/src/observe/readiness-service.ts +94 -0
  100. package/src/observe/readiness.ts +84 -0
  101. package/src/observe/snapshot-metadata.ts +12 -1
  102. package/src/observe/system-trace-android.ts +133 -0
  103. package/src/observe/system-trace.ts +218 -0
  104. package/src/server/common.ts +7 -0
  105. package/src/server/condition-schema.ts +32 -0
  106. package/src/server/debugger-handlers.ts +29 -0
  107. package/src/server/debugger-schema.ts +37 -0
  108. package/src/server/journey-schema.ts +4 -2
  109. package/src/server/readiness-schema.ts +12 -0
  110. package/src/server/system-trace-schema.ts +17 -0
  111. package/src/server/tool-definitions.ts +49 -0
  112. package/src/server/tool-handlers.ts +182 -21
  113. package/src/server-core.ts +3 -3
  114. package/src/server.ts +24 -0
  115. package/src/system/index.ts +7 -5
  116. package/src/types.ts +30 -1
  117. package/src/utils/exec.ts +2 -0
  118. package/src/utils/ios/utils.ts +4 -4
  119. package/src/utils/java.ts +13 -12
  120. package/src/utils/operation-budget.ts +24 -2
  121. package/src/utils/resolve-device.ts +10 -0
  122. package/test/device/automated/debugger/runtime.android.test.ts +203 -0
  123. package/test/device/automated/observe/correlated-evidence.smoke.ts +70 -0
  124. package/test/device/automated/observe/system-trace.smoke.ts +104 -0
  125. package/test/device/manual/interact/conditional_actions.manual.ts +95 -0
  126. package/test/device/manual/interact/modul8_selector_tap.manual.ts +63 -0
  127. package/test/device/manual/interact/readiness.manual.ts +99 -0
  128. package/test/fixtures/debugger-app/MainActivity.java +46 -0
  129. package/test/fixtures/debugger-app/NoLocals.java +7 -0
  130. package/test/fixtures/debugger-app/README.md +13 -0
  131. package/test/fixtures/debugger-app/build.ts +29 -0
  132. package/test/fixtures/readiness-app/MainActivity.java +23 -0
  133. package/test/fixtures/readiness-app/README.md +12 -0
  134. package/test/fixtures/readiness-app/build.ts +37 -0
  135. package/test/fixtures/readiness-app/main.m +27 -0
  136. package/test/helpers/perfetto.ts +59 -0
  137. package/test/unit/debugger/guards.test.ts +46 -0
  138. package/test/unit/debugger/native.test.ts +33 -0
  139. package/test/unit/debugger/sessions.test.ts +83 -0
  140. package/test/unit/observe/android_tree_acquisition.test.ts +2 -0
  141. package/test/unit/observe/capture_debug_snapshot.test.ts +42 -32
  142. package/test/unit/observe/compact_tree_error.test.ts +24 -0
  143. package/test/unit/observe/conditional_snapshots.test.ts +33 -0
  144. package/test/unit/observe/correlated-evidence.test.ts +128 -0
  145. package/test/unit/observe/ios_tree_acquisition.test.ts +2 -0
  146. package/test/unit/observe/readiness.test.ts +41 -0
  147. package/test/unit/observe/readiness_native.test.ts +68 -0
  148. package/test/unit/observe/system-trace-android.test.ts +52 -0
  149. package/test/unit/observe/system-trace.test.ts +166 -0
  150. package/test/unit/server/conditional_actions.test.ts +299 -0
  151. package/test/unit/server/conditional_schema.test.ts +59 -0
  152. package/test/unit/server/correlated-evidence.test.ts +194 -0
  153. package/test/unit/server/readiness.test.ts +170 -0
  154. package/test/unit/server/selector_tap_target.test.ts +79 -0
  155. package/test/unit/server/system-trace.test.ts +64 -0
  156. package/test/unit/server/tap_selector.test.ts +2 -0
  157. package/worker/DebuggerWorker.java +160 -0
  158. package/skills/mobile-debug-tools-usage/SKILL.md +0 -96
  159. /package/{skills → .agents/skills}/README.md +0 -0
  160. /package/{skills → .agents/skills}/mcp-builder/SKILL.md +0 -0
  161. /package/{skills → .agents/skills}/mcp-builder/references/build-flags.md +0 -0
  162. /package/{skills → .agents/skills}/mcp-builder/references/diagnostics-schema.md +0 -0
  163. /package/{skills → .agents/skills}/mcp-builder/references/toolchain-details.md +0 -0
  164. /package/{skills → .agents/skills}/spec-review/SKILL.md +0 -0
  165. /package/{skills → .agents/skills}/spec-review/references/spec-review-checklist.md +0 -0
  166. /package/{skills → .agents/skills}/spec-review/references/spec-review-template.md +0 -0
  167. /package/{skills → .agents/skills}/test-authoring/SKILL.md +0 -0
  168. /package/{skills → .agents/skills}/test-authoring/references/repo-test-layout.md +0 -0
  169. /package/{skills → .agents/skills}/test-authoring/references/test-authoring-workflow.md +0 -0
  170. /package/{skills → .agents/skills}/test-authoring/references/test-quality-checklist.md +0 -0
@@ -0,0 +1,336 @@
1
+ {
2
+ "verified_on": "2026-10-30T09:14:34.967Z",
3
+ "device": {
4
+ "platform": "android",
5
+ "deviceId": "emulator-5554"
6
+ },
7
+ "package": "com.ideamechanics.modul8.dev",
8
+ "source": "rebuilt local MCP stdio server",
9
+ "build_sha256": "ab7f5916468b000b70f445de5a29406adfd141cea89e1298661562e68fbef61d",
10
+ "steps": [
11
+ {
12
+ "selector": {
13
+ "text": "FAVOURITES"
14
+ },
15
+ "waitForCondition": "default",
16
+ "status": "PASS",
17
+ "failure_code": null,
18
+ "evidence": {
19
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
20
+ "correlation_id": "01a328ba-91a9-4ac6-a768-b06701e71018",
21
+ "action_id": "01a328ba-91a9-4ac6-a768-b06701e71018",
22
+ "device": {
23
+ "platform": "android",
24
+ "id": "emulator-5554"
25
+ },
26
+ "association": "self",
27
+ "association_reason": null,
28
+ "operation_started_at_ms": 1793351680409,
29
+ "operation_completed_at_ms": 1793351683123,
30
+ "dispatch_started_at_ms": 1793351683067,
31
+ "dispatch_completed_at_ms": 1793351683123,
32
+ "verification_completed_at_ms": null,
33
+ "observations": [
34
+ {
35
+ "evidence_id": "1dc56459-d61e-4814-9612-2e3ad6c8782b",
36
+ "kind": "ui_tree",
37
+ "status": "captured",
38
+ "capture_started_at_ms": 1793351680628,
39
+ "capture_completed_at_ms": 1793351682798,
40
+ "source_captured_at_ms": 1793351682798,
41
+ "age_ms": 324.72998046875,
42
+ "freshness": "stale",
43
+ "freshness_reasons": [
44
+ "BEFORE_ACTION"
45
+ ],
46
+ "device": {
47
+ "platform": "android",
48
+ "id": "emulator-5554"
49
+ },
50
+ "snapshot_session_id": "9f2a6439-590e-46a5-9025-d6c828525250",
51
+ "snapshot_revision": 1,
52
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
53
+ "correlation_id": "01a328ba-91a9-4ac6-a768-b06701e71018",
54
+ "action_id": "01a328ba-91a9-4ac6-a768-b06701e71018",
55
+ "association": "internal"
56
+ }
57
+ ],
58
+ "trace_context": {
59
+ "supported": false,
60
+ "trace_id": null,
61
+ "markers": []
62
+ }
63
+ }
64
+ },
65
+ {
66
+ "selector": {
67
+ "text": "GENER8"
68
+ },
69
+ "waitForCondition": "default",
70
+ "status": "PASS",
71
+ "failure_code": null,
72
+ "evidence": {
73
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
74
+ "correlation_id": "61cdf8f3-3702-4342-ad25-aaa7146f4e85",
75
+ "action_id": "61cdf8f3-3702-4342-ad25-aaa7146f4e85",
76
+ "device": {
77
+ "platform": "android",
78
+ "id": "emulator-5554"
79
+ },
80
+ "association": "self",
81
+ "association_reason": null,
82
+ "operation_started_at_ms": 1793351688186,
83
+ "operation_completed_at_ms": 1793351691068,
84
+ "dispatch_started_at_ms": 1793351691021,
85
+ "dispatch_completed_at_ms": 1793351691068,
86
+ "verification_completed_at_ms": null,
87
+ "observations": [
88
+ {
89
+ "evidence_id": "74bda6d0-fdf1-40d3-92d6-31f61e49316b",
90
+ "kind": "ui_tree",
91
+ "status": "captured",
92
+ "capture_started_at_ms": 1793351688428,
93
+ "capture_completed_at_ms": 1793351690795,
94
+ "source_captured_at_ms": 1793351690795,
95
+ "age_ms": 273.0615234375,
96
+ "freshness": "stale",
97
+ "freshness_reasons": [
98
+ "BEFORE_ACTION"
99
+ ],
100
+ "device": {
101
+ "platform": "android",
102
+ "id": "emulator-5554"
103
+ },
104
+ "snapshot_session_id": "9f2a6439-590e-46a5-9025-d6c828525250",
105
+ "snapshot_revision": 2,
106
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
107
+ "correlation_id": "61cdf8f3-3702-4342-ad25-aaa7146f4e85",
108
+ "action_id": "61cdf8f3-3702-4342-ad25-aaa7146f4e85",
109
+ "association": "internal"
110
+ }
111
+ ],
112
+ "trace_context": {
113
+ "supported": false,
114
+ "trace_id": null,
115
+ "markers": []
116
+ }
117
+ }
118
+ },
119
+ {
120
+ "selector": {
121
+ "text": "SETTINGS"
122
+ },
123
+ "waitForCondition": "exists",
124
+ "status": "PASS",
125
+ "failure_code": null,
126
+ "resolved_target": {
127
+ "elementId": "el_9b12a59a90203e47349f1943",
128
+ "text": null,
129
+ "resource_id": null,
130
+ "accessibility_id": null,
131
+ "class": "android.view.View",
132
+ "bounds": [
133
+ 476,
134
+ 1408,
135
+ 708,
136
+ 1528
137
+ ],
138
+ "index": 12,
139
+ "state": {
140
+ "checked": false,
141
+ "selected": true,
142
+ "focused": false,
143
+ "enabled": true
144
+ }
145
+ },
146
+ "evidence": {
147
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
148
+ "correlation_id": "4f52aadc-2249-47f0-8149-781123a3e765",
149
+ "action_id": "4f52aadc-2249-47f0-8149-781123a3e765",
150
+ "device": {
151
+ "platform": "android",
152
+ "id": "emulator-5554"
153
+ },
154
+ "association": "self",
155
+ "association_reason": null,
156
+ "operation_started_at_ms": 1793351696337,
157
+ "operation_completed_at_ms": 1793351699045,
158
+ "dispatch_started_at_ms": 1793351698985,
159
+ "dispatch_completed_at_ms": 1793351699045,
160
+ "verification_completed_at_ms": null,
161
+ "observations": [
162
+ {
163
+ "evidence_id": "35f26191-7b0f-466e-90b0-1cf22f9b03ac",
164
+ "kind": "ui_tree",
165
+ "status": "captured",
166
+ "capture_started_at_ms": 1793351696577,
167
+ "capture_completed_at_ms": 1793351698724,
168
+ "source_captured_at_ms": 1793351698724,
169
+ "age_ms": 321.01416015625,
170
+ "freshness": "stale",
171
+ "freshness_reasons": [
172
+ "BEFORE_ACTION"
173
+ ],
174
+ "device": {
175
+ "platform": "android",
176
+ "id": "emulator-5554"
177
+ },
178
+ "snapshot_session_id": "9f2a6439-590e-46a5-9025-d6c828525250",
179
+ "snapshot_revision": 3,
180
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
181
+ "correlation_id": "4f52aadc-2249-47f0-8149-781123a3e765",
182
+ "action_id": "4f52aadc-2249-47f0-8149-781123a3e765",
183
+ "association": "internal"
184
+ }
185
+ ],
186
+ "trace_context": {
187
+ "supported": false,
188
+ "trace_id": null,
189
+ "markers": []
190
+ }
191
+ }
192
+ },
193
+ {
194
+ "selector": {
195
+ "text": "FAVOURITES"
196
+ },
197
+ "waitForCondition": "visible",
198
+ "status": "PASS",
199
+ "failure_code": null,
200
+ "resolved_target": {
201
+ "elementId": "el_25faaaa4e20f6ef0b85ab86b",
202
+ "text": null,
203
+ "resource_id": null,
204
+ "accessibility_id": null,
205
+ "class": "android.view.View",
206
+ "bounds": [
207
+ 12,
208
+ 1408,
209
+ 244,
210
+ 1528
211
+ ],
212
+ "index": 13,
213
+ "state": {
214
+ "checked": false,
215
+ "selected": true,
216
+ "focused": false,
217
+ "enabled": true
218
+ }
219
+ },
220
+ "evidence": {
221
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
222
+ "correlation_id": "fbc19ace-4779-4b92-9677-11852aa6d339",
223
+ "action_id": "fbc19ace-4779-4b92-9677-11852aa6d339",
224
+ "device": {
225
+ "platform": "android",
226
+ "id": "emulator-5554"
227
+ },
228
+ "association": "self",
229
+ "association_reason": null,
230
+ "operation_started_at_ms": 1793351704414,
231
+ "operation_completed_at_ms": 1793351707038,
232
+ "dispatch_started_at_ms": 1793351706987,
233
+ "dispatch_completed_at_ms": 1793351707038,
234
+ "verification_completed_at_ms": null,
235
+ "observations": [
236
+ {
237
+ "evidence_id": "489de2e9-21fe-405d-a680-3d56ece84aae",
238
+ "kind": "ui_tree",
239
+ "status": "captured",
240
+ "capture_started_at_ms": 1793351704599,
241
+ "capture_completed_at_ms": 1793351706754,
242
+ "source_captured_at_ms": 1793351706754,
243
+ "age_ms": 284.26416015625,
244
+ "freshness": "stale",
245
+ "freshness_reasons": [
246
+ "BEFORE_ACTION"
247
+ ],
248
+ "device": {
249
+ "platform": "android",
250
+ "id": "emulator-5554"
251
+ },
252
+ "snapshot_session_id": "9f2a6439-590e-46a5-9025-d6c828525250",
253
+ "snapshot_revision": 4,
254
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
255
+ "correlation_id": "fbc19ace-4779-4b92-9677-11852aa6d339",
256
+ "action_id": "fbc19ace-4779-4b92-9677-11852aa6d339",
257
+ "association": "internal"
258
+ }
259
+ ],
260
+ "trace_context": {
261
+ "supported": false,
262
+ "trace_id": null,
263
+ "markers": []
264
+ }
265
+ }
266
+ },
267
+ {
268
+ "selector": {
269
+ "text": "SETTINGS"
270
+ },
271
+ "waitForCondition": "default",
272
+ "status": "PASS",
273
+ "failure_code": null,
274
+ "evidence": {
275
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
276
+ "correlation_id": "c49cac9f-dca4-4a94-a4a4-f05119db855e",
277
+ "action_id": "c49cac9f-dca4-4a94-a4a4-f05119db855e",
278
+ "device": {
279
+ "platform": "android",
280
+ "id": "emulator-5554"
281
+ },
282
+ "association": "self",
283
+ "association_reason": null,
284
+ "operation_started_at_ms": 1793351712316,
285
+ "operation_completed_at_ms": 1793351715175,
286
+ "dispatch_started_at_ms": 1793351715131,
287
+ "dispatch_completed_at_ms": 1793351715175,
288
+ "verification_completed_at_ms": null,
289
+ "observations": [
290
+ {
291
+ "evidence_id": "f8c8f253-3e66-456b-94be-318fb94fc868",
292
+ "kind": "ui_tree",
293
+ "status": "captured",
294
+ "capture_started_at_ms": 1793351712552,
295
+ "capture_completed_at_ms": 1793351714774,
296
+ "source_captured_at_ms": 1793351714774,
297
+ "age_ms": 400.86767578125,
298
+ "freshness": "stale",
299
+ "freshness_reasons": [
300
+ "BEFORE_ACTION"
301
+ ],
302
+ "device": {
303
+ "platform": "android",
304
+ "id": "emulator-5554"
305
+ },
306
+ "snapshot_session_id": "9f2a6439-590e-46a5-9025-d6c828525250",
307
+ "snapshot_revision": 5,
308
+ "session_id": "35528fe5-ba92-4d62-8380-5d977d390b08",
309
+ "correlation_id": "c49cac9f-dca4-4a94-a4a4-f05119db855e",
310
+ "action_id": "c49cac9f-dca4-4a94-a4a4-f05119db855e",
311
+ "association": "internal"
312
+ }
313
+ ],
314
+ "trace_context": {
315
+ "supported": false,
316
+ "trace_id": null,
317
+ "markers": []
318
+ }
319
+ }
320
+ }
321
+ ],
322
+ "status": "PASS",
323
+ "cleanup": "PASS: restored Subscription; no settings, account data, favourites or sessions changed; no streams or screenshots created",
324
+ "server": {
325
+ "name": "mobile-debug-mcp",
326
+ "version": "0.32.4"
327
+ },
328
+ "starting_state": "Subscription",
329
+ "source_base": "3701f3987940a8abc76ed893b575ed370e3fb673",
330
+ "interaction_build_sha256": "79cb709c20fe8775571aee92413d01f28e9f8bc97da77476c08310cc555d7c2b",
331
+ "fixture_version_from_same_session": {
332
+ "versionName": "0.3.1-dev",
333
+ "versionCode": 14
334
+ },
335
+ "post_suite_cleanup": "PASS: relaunched Modul8 after the general device suite and confirmed Subscription via expect_element_visible"
336
+ }
@@ -0,0 +1,171 @@
1
+ # Plan: correlated evidence
2
+
3
+ Authorisation: [intent](intent.md), then [spec](spec.md), explicitly approved by
4
+ “yes” and “approve” respectively in this conversation on 2026-10-29.
5
+
6
+ ## Implementation
7
+
8
+ - [x] Add per-call evidence context, bounded action history, clocks/freshness/types.
9
+ - [x] Propagate action/journey IDs; instrument dispatch and verification phases.
10
+ - [x] Instrument platform observations, snapshot components, stream device ownership.
11
+ - [x] Register/validate actionRef; preserve compact/debug/image/log responses.
12
+ - [x] Add regression coverage; align tool docs, README, changelog.
13
+ - [x] Review contract, ownership, concurrency, and privacy; complete checks below.
14
+
15
+ Reuse async-local context, resolution, budget, and trace helpers. Node >=18
16
+ (package/CI requirement); use installed Node/npm/ADB/Xcode tooling. No install,
17
+ publish, commit, push, or device helper APK is required. Retain old response data.
18
+ Risks are missed nested dispatch/capture paths and incorrect stream targeting;
19
+ test adapter boundaries and serialization as well as the metadata helper.
20
+
21
+ ## Verification
22
+
23
+ | Criteria | Check |
24
+ | --- | --- |
25
+ | 1: matching identifiers | Handler tests: compact/debug actions, screenshot/log references, journey propagation; local-server Android/iOS journeys. |
26
+ | 2: accurate phases | Boundary tests: conditional rejection, no verification, failure/timeout, repeated input; real action before screenshot/log collection. |
27
+ | 3: freshness | Controlled clock/order tests for every specified status/reason and cached source time. |
28
+ | 4: isolation | Concurrent call/device tests; expired/evicted/foreign-session references; stream-owned device. |
29
+ | 5: compatibility/resources | Partial/disabled snapshot and response serialization tests; content-free bounded history. |
30
+ | 6: limitations | Assert unsupported trace context and device-log clock limitations on both providers. |
31
+
32
+ Commands: focused `npx tsx test/unit/observe/correlated-evidence.test.ts`,
33
+ `npm run verify`, `npm run test:device`. CI uses Node 18 for lint/build/unit;
34
+ Android integration is manual workflow dispatch; local verify additionally checks
35
+ the agent plugin. Device suite coverage must be reported, not assumed.
36
+
37
+ Device journey: updated local server (record package/build), Android emulator-5554
38
+ and booted iPhone 17 Pro simulator. Starting state: fixture/system Settings app;
39
+ launch it, collect referenced screenshot and bounded logs, verify IDs, dispatch
40
+ and capture intervals, image/log preservation and explicit clock limitations.
41
+ Use fresh device discovery. Exercise disabled/partial snapshot and invalid
42
+ reference without changing app data. Record pass/fail/blocked/unrun plus metadata
43
+ in this directory; avoid saving screenshot/log contents. Restore changed settings,
44
+ stop only owned streams, and remove temporary test data. No settings change is
45
+ needed. Android log healthcheck was degraded: targeted read is the next check;
46
+ if unavailable, record its concrete error and verify metadata failure behaviour.
47
+ iOS physical-device coverage is outside existing simulator support.
48
+
49
+ ## Results
50
+
51
+ Implementation and focused review completed on 2026-10-30. Review corrected
52
+ journey step completion/verification timestamps, explicit Android stream dispatch
53
+ targeting, stream cleanup ownership, and concurrent iOS screenshot filenames.
54
+ No outstanding findings in correlation implementation. Correlation history
55
+ contains no capture content; later Modul8 testing below records selector limits.
56
+
57
+ - Local runtime: Node 23.11.0 / npm 10.9.2; CI's Node 18 runtime was not run locally.
58
+ - Focused evidence helper and MCP handler tests: **pass**, including fake-native
59
+ Android/iOS dispatch, timeout/precondition failures, compact/debug serialization,
60
+ deferred journey verification, stream ownership, clocks, expiry, eviction,
61
+ concurrent devices, partial/omitted captures, and reference rejection.
62
+ - `npm run verify`: **pass** after review fixes (lint, build, all automated
63
+ unit tests, agent-plugin consistency).
64
+ - `npm run test:device`: **pass** on final code. Six existing screenshot/log/tree
65
+ smokes cover Android and iOS with automatic targeting (their output does not
66
+ identify each selected device); the new smoke covers rebuilt stdio-server
67
+ correlation on Android emulator-5554 (Android 16) and iPhone 17 Pro simulator
68
+ F9243BAF-62A8-4990-8A99-17F527F56FDA (iOS 26.4). This does not exercise physical
69
+ iOS or all interaction journeys.
70
+ - New real-device journeys: **pass** for Settings launch, referenced screenshot,
71
+ bounded logs, UI-tree acquisition, disabled snapshot components, pinned device,
72
+ and foreign-session rejection. Targeted Android logs succeeded despite the
73
+ earlier healthcheck warning. Phase times/IDs/build hashes are in
74
+ [device-evidence.json](device-evidence.json); no image/log content is retained.
75
+ - Cleanup: test-owned fake streams/files removed. No device settings or app data
76
+ changed; system Settings remains foreground on the emulator and simulator.
77
+ - Physical-device correlation, installed/published-package verification: **unrun**.
78
+ No release or deployment was requested.
79
+
80
+ Validation logs for this local run: `/tmp/mobile-debug-correlated-verify.log` and
81
+ `/tmp/mobile-debug-correlated-device.log`. The `tsx` CLI requires local IPC blocked
82
+ by the sandbox; required npm checks ran with approved escalation. Focused checks
83
+ also passed via `node --import tsx` without that socket.
84
+
85
+ ## Modul8 follow-up verification — 2026-10-30
86
+
87
+ User identified the installed Modul8 app as an additional fixture. Tested the
88
+ rebuilt local 0.32.4 server over MCP stdio on emulator-5554 (Android 16), using
89
+ `com.ideamechanics.modul8.dev` version 0.3.1-dev / code 14. Started on Subscription,
90
+ navigated to Favourites and Gener8, and restored Subscription, confirmed by the
91
+ final UI tree. No settings, account/subscription data, favourites, or sessions changed.
92
+
93
+ - **Pass:** screenshot-confirmed navigation, matching action/session/correlation
94
+ IDs across referenced screenshots, UI trees and app-scoped logs; distinct
95
+ capture/dispatch/verification times; pre-action stale and post-action fresh
96
+ records. Device-log freshness correctly remains unknown.
97
+ - **Pass:** an expectation matching the current screen allowed input. Reusing the
98
+ Favourites revision after navigating to Gener8 returned `PRECONDITION_FAILED`
99
+ with null dispatch timestamps. Failed selector calls also retained null
100
+ dispatch/verification times and could be referenced by subsequent captures.
101
+ - **Fail, existing selector limits:** default `tap_element` lookup exhausted its
102
+ 1,500ms observation budget (baseline 3701f39 has the same default). With a
103
+ 10,000ms lookup/15,000ms action budget, the Gener8 text leaf was found but
104
+ rejected as `ELEMENT_NOT_INTERACTABLE`; its parent is clickable. Guarded
105
+ screenshot-based coordinate navigation passed. These are not correlation
106
+ regressions and were not changed in this follow-up.
107
+ - Fresh screenshots can capture a crossfade: recent acquisition does not prove
108
+ settled UI. No settled-state or causal-log-emission claim was made.
109
+
110
+ [modul8-evidence.json](modul8-evidence.json) retains metadata and outcomes, excluding
111
+ screenshots, UI text, and log contents. Temporary screenshots/client were removed.
112
+ Only verification documents changed; link/JSON/whitespace checks passed. Existing
113
+ maintainer/device results above apply to the unchanged implementation.
114
+
115
+ ## Selector repair — 2026-10-30
116
+
117
+ User explicitly requested fixing the Modul8 selector failures. Lightweight bug-fix
118
+ path: restore existing clickable-ancestor targeting for selector taps after an
119
+ exists/visible wait; use the configured action budget for a single fresh lookup
120
+ (default 5 seconds); preserve `ACTION_TIMEOUT` on failed acquisition. Keep matching,
121
+ ambiguity/index rules, standalone wait results, hidden/disabled-node checks, and
122
+ conditional dispatch guards intact. Preserve unrelated correlated-evidence work.
123
+
124
+ - Implementation: `executeTapElement` lookup budgeting and internal tap-target
125
+ resolution in `waitForUIHandler`, reusing the existing ancestor helper.
126
+ - Regression checks: actual resolution/tap pipeline with a Modul8-shaped label
127
+ and clickable tab parent; default/exists/visible/clickable waits; explicit
128
+ timeout budget; timeout vs absence; hidden/disabled labels/parents; plain
129
+ labels; ambiguity; standalone wait behaviour. Focused checks **pass**.
130
+ - Device check: explicit manual fixture helper
131
+ `node --import tsx test/device/manual/interact/modul8_selector_tap.manual.ts`;
132
+ rebuilt server, emulator-5554, Modul8 dev. Verify default selector navigation
133
+ through Favourites/Gener8, exists/visible selector taps, and restored Subscription
134
+ using semantic assertions. **Pass**: all five selector taps and destination
135
+ assertions succeeded; no coordinate fallback was used. Metadata is in
136
+ [modul8-selector-fix-evidence.json](modul8-selector-fix-evidence.json).
137
+ - Final checks: `npm run verify` **pass** (65 unit files, lint/build/plugin check);
138
+ `npm run test:device` **pass** (7 smoke files, Android/iOS observation and
139
+ correlation coverage). Native selector repair verified on Android/Modul8;
140
+ equivalent iOS app selector navigation remains **unrun**.
141
+ - Focused review: matching/index/ambiguity and direct handle behaviour preserved;
142
+ tap-only ancestor resolution reuses the existing policy and acquisition;
143
+ hidden/disabled labels cannot route through an enabled parent. No outstanding
144
+ findings. Documentation links/JSON/whitespace checks passed.
145
+ - Cleanup: after the general device suite reopened system Settings, relaunched
146
+ Modul8 and semantically confirmed the original Subscription screen. No app data
147
+ or device settings changed; temporary cleanup client removed.
148
+ - Historical failures in `modul8-evidence.json` are retained as before-fix evidence;
149
+ this repair supersedes their unresolved-selector status. Validation logs:
150
+ `/tmp/mobile-debug-selector-fix-verify.log`, `/tmp/mobile-debug-selector-fix-device.log`.
151
+
152
+ ## Remaining readiness test flake — 2026-10-30
153
+
154
+ After push 0ae7300, [run 41034943919 passed](https://github.com/clivejefferies/mobile-debug-tools/actions/runs/41034943919)
155
+ while [run 41034949793 failed](https://github.com/clivejefferies/mobile-debug-tools/actions/runs/41034949793)
156
+ on the same commit. The failure was `server/readiness.test.ts:51`, expecting four
157
+ reads but observing five. Real timer rounding can leave the fourth sample just
158
+ short of the requested stability interval, legitimately requiring another read.
159
+
160
+ Test-only repair: the sequential fixture/poll timers now advance wall and
161
+ monotonic clocks by exactly their requested delay. Existing exact read counts,
162
+ late-read/deadline rejection, state transitions, predicate observations, and
163
+ forced second-read timeout assertions remain intact. Clock/timer hooks are
164
+ restored in `finally`. No production timing or readiness behaviour changed.
165
+
166
+ - Focused check: Node 23 pass; 25 separate Node 18.20.8 test-process runs pass.
167
+ - Full Node 18 maintainer verification with unavailable native defaults: **pass**
168
+ (65 unit files, lint, build, agent-plugin consistency). Changed whitespace and
169
+ local document links also pass; log: `/tmp/mobile-debug-readiness-clock-verify.log`.
170
+ - Native device suite not repeated for this test-only change; prior runtime
171
+ device results remain applicable. GitHub CI requires pushing this local repair.
@@ -0,0 +1,123 @@
1
+ # Specification: correlated evidence
2
+
3
+ Date: 2026-10-29. Status: approved.
4
+ Source: [issue #66](https://github.com/clivejefferies/mobile-debug-tools/issues/66).
5
+ [Intent](intent.md) approved by “yes”; this specification approved by the
6
+ maintainer’s “approve” on 2026-10-29 in this conversation.
7
+
8
+ ## Scope and association
9
+
10
+ Add metadata to `start_app`, `restart_app`, `tap`, `tap_element`, `swipe`,
11
+ `scroll_to_element`, `type_text`, `press_back`, `adjust_control`, their
12
+ `run_journey` execution, and internal observations. Observation coverage:
13
+ `capture_screenshot`, `get_ui_tree`, `get_screen_fingerprint`,
14
+ `capture_debug_snapshot`, `get_logs`, `read_log_stream`.
15
+
16
+ Allocate action IDs before execution; reuse them in results, traces, and nested
17
+ observations. Journey steps share one correlation ID but retain individual action
18
+ IDs. Standalone actions use their action ID as correlation ID. Independent
19
+ observations receive a new correlation ID without an associated action.
20
+
21
+ Observation tools accept optional `actionRef: { sessionId: string, actionId:
22
+ string }`; strings must contain 1–128 characters. Malformed references return
23
+ `INVALID_ARGUMENT` before collection. Associate only known same-session,
24
+ same-device actions; never infer “last action”. Unknown/expired references allow
25
+ independent collection, retaining the requested reference and association reason.
26
+ Restart changes session ID. Retain completed action records for 10 minutes,
27
+ maximum 1,000, evicting oldest first; active records belong to running calls.
28
+
29
+ ## Response contract
30
+
31
+ Add `evidence` to the first JSON text block in compact/debug responses; snapshots
32
+ place it alongside `raw`/`semantic`. Preserve existing fields, image blocks,
33
+ ordering, IDs, timestamps, and elapsed timings. Log metadata describes the
34
+ following log block.
35
+
36
+ | Field | Contract |
37
+ | --- | --- |
38
+ | `session_id`, `correlation_id`, `action_id` | Action ID nullable when unassociated. |
39
+ | `device` | Resolved platform/ID, nullable before resolution; app/PID only when resolved. UI scope is device-wide. |
40
+ | `association` | `self`, `explicit`, `internal`, `none`, `unavailable`. |
41
+ | `association_reason` | Null or `ACTION_UNAVAILABLE`, `SESSION_MISMATCH`, `DEVICE_MISMATCH`. Unavailable association has null action ID and independent correlation ID. |
42
+ | Phase timestamps | `operation_started_at_ms`, `operation_completed_at_ms`, `dispatch_started_at_ms`, `dispatch_completed_at_ms`, `verification_completed_at_ms`; unreached/uninstrumented phases null. |
43
+ | `observations` | Component records defined below. |
44
+ | `trace_context` | `{ supported: false, trace_id: null, markers: [] }`; future tracing can reuse these identifiers. |
45
+
46
+ Each component has unique `evidence_id`, `kind` (screenshot/ui_tree/fingerprint/
47
+ activity/logs), `status` (`captured`, `failed`, `omitted`),
48
+ `capture_started_at_ms`, `capture_completed_at_ms`, nullable
49
+ `source_captured_at_ms`/`age_ms`, `freshness`, and `freshness_reasons`.
50
+ Retain component errors and UI-tree snapshot session/revision references.
51
+ Snapshot components, including fallback logs, have individual acquisition intervals.
52
+
53
+ ## Timing and freshness
54
+
55
+ New epoch times use server UTC milliseconds; monotonic time governs ordering,
56
+ durations, and retention. Clock reversal yields `CLOCK_DISCONTINUITY`, unknown
57
+ freshness, and null age. Acquisition intervals bound server collection, not exact
58
+ device frame time. Instrument actual dispatch after conditional checks;
59
+ multi-input actions report first start/last completion. Preserve known times on
60
+ failure. Verification completion includes timeout/failure, is null when skipped,
61
+ and never overrides verification status.
62
+
63
+ Assess freshness at response assembly:
64
+
65
+ | Evidence | Freshness / reason |
66
+ | --- | --- |
67
+ | New successful visual acquisition, no intervening known dispatch on that device | `fresh`; no guarantee the app stayed unchanged. |
68
+ | Before associated action’s dispatch | `stale` / `BEFORE_ACTION`. |
69
+ | Overlapping dispatch | `unknown` / `OVERLAPPING_ACTION`. |
70
+ | Superseded by later covered dispatch | `stale` / `SUPERSEDED_BY_ACTION`. |
71
+ | Reused observation | Preserve source time; `unknown` / `REUSED_OBSERVATION` unless supersession proves stale. |
72
+ | Missing timing | `unknown` / `CAPTURE_TIME_UNKNOWN`. |
73
+ | Failed or omitted visual component | `unknown`. |
74
+ | Logs / omitted logs | `unknown` / `DEVICE_CLOCK_UNVERIFIED`; omitted logs `not_applicable`. |
75
+
76
+ Unassociated observations consider known concurrent dispatches. Age uses known
77
+ host source capture time, otherwise null; clients choose age limits. Conditional
78
+ safeguards remain unchanged. Preserve device log timestamps, declaring clock
79
+ `device_or_unknown`, nullable earliest/latest parseable entry times, and
80
+ `event_time_comparable: false`. Never infer emission freshness, causality, or
81
+ an action-window filter from collection time.
82
+
83
+ ## Targeting, resources, and compatibility
84
+
85
+ Pin snapshot components to one resolved device. References never redirect targets;
86
+ stream reads use their stream’s device. Unknown scope means unknown freshness
87
+ and no matching association. Per-call asynchronous context isolates concurrent
88
+ calls/devices/steps; device dispatch ordering must not serialise independent calls.
89
+ Snapshots remain non-atomic.
90
+
91
+ Preserve existing errors, partial results, timeouts, and cancellation support;
92
+ attach available metadata on failure. Add no retries, deadline extensions,
93
+ processes, cancellation API, artifact removal, or stream termination. Release
94
+ call context on completion/cancellation; existing owners clean up subprocesses
95
+ and streams. History stores only IDs, scope, timings, and ordering: no captured
96
+ content, arguments, credentials, persistence, or telemetry.
97
+
98
+ Android ADB and iOS simulators share this contract; physical iOS retains provider
99
+ limitations. Exclude trace capture, clock synchronisation, debugger, recovery,
100
+ heap dumps, and background recording.
101
+
102
+ ## Acceptance and verification
103
+
104
+ 1. Referenced screenshots/logs share action identifiers in compact/debug responses.
105
+ 2. Capture, dispatch, verification, skipped phases, precondition failures, timeouts,
106
+ and multi-input actions have accurate distinguishable timings.
107
+ 3. Freshness follows the table, preserving uncertainty and component outcomes.
108
+ 4. Concurrent steps/devices, expired/mismatched references, and server restarts
109
+ cannot falsely associate evidence.
110
+ 5. Partial/disabled captures preserve existing responses; history is bounded/content-free.
111
+ 6. Platform limitations and unsupported tracing are explicit.
112
+
113
+ Affected: server schemas/serialization, dispatch hooks, observation adapters,
114
+ shared types/context, journeys, tests, and tool/README/changelog docs. Risks:
115
+ missed dispatch paths, dropped metadata, retimestamped cache, wrong stream device.
116
+
117
+ Baseline (2026-10-29): checkout 0.32.4; connected build unverified. Discovery:
118
+ Android 16 emulator-5554, Pixel 9 Pro, iOS 26.4 iPhone 17 Pro simulator.
119
+ Android compact tree: revision 1, elapsed timing, no correlation/capture metadata.
120
+ Discovery/Android baseline passed; iOS capture/new behaviour unrun. Android logs
121
+ flagged unavailable by healthcheck; targeted read needed. Post-approval checks:
122
+ updated-server Android/iOS journeys, deterministic failure/concurrency coverage,
123
+ `npm run verify`, `npm run test:device`.
@@ -0,0 +1,38 @@
1
+ {
2
+ "status": "PASS",
3
+ "build_sha256": "f59ec5112751295517ce3fd6fafe78e06ca009f30ecfcc560c872bfbf1cf86ee",
4
+ "runtime": "v23.11.0",
5
+ "server": {
6
+ "name": "mobile-debug-mcp",
7
+ "version": "0.33.0"
8
+ },
9
+ "device": "emulator-5554",
10
+ "osVersion": "16",
11
+ "starting_state": "System Settings fixture",
12
+ "journey": "start Settings, assert baseline Settings-owned resource visible in UI tree; explicit trace association",
13
+ "trace_id": "7a0bdca9-f64f-4c4f-b204-5c9b7527c2c6",
14
+ "correlation_id": "6622a71c-e556-4e27-b228-1eb0328f0637",
15
+ "phases": {
16
+ "capture_started_at_ms": 1793359970395,
17
+ "capture_completed_at_ms": 1793359976096
18
+ },
19
+ "artifact_bytes": 870332,
20
+ "content": {
21
+ "packets": 227,
22
+ "ftrace_bundles": 210,
23
+ "events": 19833,
24
+ "compact_sched_bundles": 210,
25
+ "validation": "local protobuf structure, trace config duration and timestamped ftrace/compact scheduler content; not full Perfetto analysis"
26
+ },
27
+ "durationContent": {
28
+ "packets": 51,
29
+ "ftrace_bundles": 35,
30
+ "events": 4596,
31
+ "compact_sched_bundles": 35,
32
+ "validation": "local protobuf structure, trace config duration and timestamped ftrace/compact scheduler content; not full Perfetto analysis"
33
+ },
34
+ "cancel": "PASS",
35
+ "expired_journey": "PASS",
36
+ "active_shutdown": "PASS",
37
+ "cleanup": "Device capture files removed; test host artifacts removed in finally. No settings or app data changed. Settings remains foreground."
38
+ }