mobile-debug-mcp 0.32.2 → 0.33.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.
- package/.agents/plugins/marketplace.json +20 -0
- package/{skills → .agents/skills}/mcp-builder/SKILL.md +1 -2
- package/{skills → .agents/skills}/spec-review/SKILL.md +1 -1
- package/{skills → .agents/skills}/test-authoring/SKILL.md +1 -1
- package/.claude-plugin/marketplace.json +14 -0
- package/.cursor-plugin/marketplace.json +16 -0
- package/.github/copilot-instructions.md +7 -0
- package/AGENTS.md +21 -10
- package/README.md +67 -5
- package/agent-plugin/.claude-plugin/plugin.json +20 -0
- package/agent-plugin/.codex-plugin/plugin.json +34 -0
- package/agent-plugin/.cursor-plugin/plugin.json +23 -0
- package/agent-plugin/.mcp.json +14 -0
- package/agent-plugin/LICENSE +201 -0
- package/agent-plugin/README.md +29 -0
- package/agent-plugin/assets/mobile-debug-tools.svg +28 -0
- package/agent-plugin/mcp.json +11 -0
- package/agent-plugin/plugin.json +29 -0
- package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +124 -0
- package/dist/interact/android.js +10 -4
- package/dist/interact/conditional-action.js +68 -0
- package/dist/interact/index.js +54 -14
- package/dist/interact/ios.js +14 -8
- package/dist/manage/android.js +4 -3
- package/dist/manage/ios.js +3 -2
- package/dist/observe/android.js +27 -22
- package/dist/observe/evidence.js +273 -0
- package/dist/observe/index.js +33 -6
- package/dist/observe/ios.js +25 -6
- package/dist/observe/readiness-service.js +115 -0
- package/dist/observe/readiness.js +81 -0
- package/dist/observe/recording.js +165 -0
- package/dist/observe/snapshot-metadata.js +10 -2
- package/dist/server/common.js +9 -0
- package/dist/server/condition-schema.js +32 -0
- package/dist/server/journey-schema.js +3 -2
- package/dist/server/readiness-schema.js +12 -0
- package/dist/server/tool-definitions.js +46 -0
- package/dist/server/tool-handlers.js +99 -17
- package/dist/server-core.js +1 -1
- package/dist/utils/ios/utils.js +4 -4
- package/dist/utils/operation-budget.js +24 -2
- package/dist/utils/resolve-device.js +7 -0
- package/docs/CHANGELOG.md +16 -2
- package/docs/benchmarks/ui-tree-performance/README.md +4 -3
- package/docs/specs/020-conditional-actions/intent.md +62 -0
- package/docs/specs/020-conditional-actions/plan.md +214 -0
- package/docs/specs/020-conditional-actions/spec.md +301 -0
- package/docs/specs/021-app-readiness-signals/intent.md +40 -0
- package/docs/specs/021-app-readiness-signals/plan.md +102 -0
- package/docs/specs/021-app-readiness-signals/spec.md +144 -0
- package/docs/specs/022-correlated-evidence/device-evidence.json +60 -0
- package/docs/specs/022-correlated-evidence/intent.md +37 -0
- package/docs/specs/022-correlated-evidence/modul8-evidence.json +1062 -0
- package/docs/specs/022-correlated-evidence/modul8-selector-fix-evidence.json +336 -0
- package/docs/specs/022-correlated-evidence/plan.md +171 -0
- package/docs/specs/022-correlated-evidence/spec.md +123 -0
- package/docs/specs/process.md +213 -0
- package/docs/tools/TOOLS.md +3 -0
- package/docs/tools/evidence.md +79 -0
- package/docs/tools/interact.md +125 -2
- package/docs/tools/observe.md +64 -2
- package/package.json +4 -2
- package/scripts/agent-plugin.mjs +98 -0
- package/src/interact/android.ts +10 -4
- package/src/interact/conditional-action.ts +75 -0
- package/src/interact/index.ts +42 -14
- package/src/interact/ios.ts +14 -8
- package/src/manage/android.ts +4 -3
- package/src/manage/ios.ts +3 -2
- package/src/observe/android.ts +35 -21
- package/src/observe/evidence.ts +255 -0
- package/src/observe/index.ts +32 -8
- package/src/observe/ios.ts +32 -8
- package/src/observe/readiness-service.ts +89 -0
- package/src/observe/readiness.ts +78 -0
- package/src/observe/snapshot-metadata.ts +12 -1
- package/src/server/common.ts +7 -0
- package/src/server/condition-schema.ts +32 -0
- package/src/server/journey-schema.ts +3 -2
- package/src/server/readiness-schema.ts +12 -0
- package/src/server/tool-definitions.ts +44 -0
- package/src/server/tool-handlers.ts +93 -17
- package/src/server-core.ts +1 -1
- package/src/types.ts +30 -1
- package/src/utils/ios/utils.ts +4 -4
- package/src/utils/operation-budget.ts +22 -2
- package/src/utils/resolve-device.ts +8 -0
- package/test/device/automated/observe/correlated-evidence.smoke.ts +70 -0
- package/test/device/manual/interact/conditional_actions.manual.ts +95 -0
- package/test/device/manual/interact/modul8_selector_tap.manual.ts +63 -0
- package/test/device/manual/interact/readiness.manual.ts +99 -0
- package/test/fixtures/readiness-app/MainActivity.java +23 -0
- package/test/fixtures/readiness-app/README.md +12 -0
- package/test/fixtures/readiness-app/build.ts +37 -0
- package/test/fixtures/readiness-app/main.m +27 -0
- package/test/unit/observe/android_tree_acquisition.test.ts +6 -0
- package/test/unit/observe/capture_debug_snapshot.test.ts +42 -32
- package/test/unit/observe/compact_tree_error.test.ts +24 -0
- package/test/unit/observe/conditional_snapshots.test.ts +33 -0
- package/test/unit/observe/correlated-evidence.test.ts +128 -0
- package/test/unit/observe/ios-getlogs.test.ts +3 -3
- package/test/unit/observe/ios_tree_acquisition.test.ts +2 -0
- package/test/unit/observe/readiness.test.ts +39 -0
- package/test/unit/observe/readiness_native.test.ts +68 -0
- package/test/unit/server/conditional_actions.test.ts +299 -0
- package/test/unit/server/conditional_schema.test.ts +59 -0
- package/test/unit/server/correlated-evidence.test.ts +194 -0
- package/test/unit/server/readiness.test.ts +170 -0
- package/test/unit/server/selector_tap_target.test.ts +79 -0
- package/test/unit/server/tap_selector.test.ts +2 -0
- package/skills/mobile-debug-tools-usage/SKILL.md +0 -74
- package/src/observe/android-tree-bridge.ts +0 -178
- package/test/device/automated/ui-tree-performance/bridge-probe.ts +0 -197
- package/test/fixtures/ui-tree-bridge/AndroidManifest.xml +0 -6
- package/test/fixtures/ui-tree-bridge/build.ts +0 -34
- package/test/fixtures/ui-tree-bridge/src/TreeInstrumentation.java +0 -126
- package/test/unit/observe/android_tree_bridge_cleanup.test.ts +0 -95
- /package/{skills → .agents/skills}/README.md +0 -0
- /package/{skills → .agents/skills}/mcp-builder/references/build-flags.md +0 -0
- /package/{skills → .agents/skills}/mcp-builder/references/diagnostics-schema.md +0 -0
- /package/{skills → .agents/skills}/mcp-builder/references/toolchain-details.md +0 -0
- /package/{skills → .agents/skills}/spec-review/references/spec-review-checklist.md +0 -0
- /package/{skills → .agents/skills}/spec-review/references/spec-review-template.md +0 -0
- /package/{skills → .agents/skills}/test-authoring/references/repo-test-layout.md +0 -0
- /package/{skills → .agents/skills}/test-authoring/references/test-authoring-workflow.md +0 -0
- /package/{skills → .agents/skills}/test-authoring/references/test-quality-checklist.md +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Plan: conditional actions
|
|
2
|
+
|
|
3
|
+
Date: 2026-10-29. Status: complete — implementation and required verification passed.
|
|
4
|
+
Intent: [intent.md](intent.md). Specification: [spec.md](spec.md).
|
|
5
|
+
Both approved by the originator's separate “yes” replies in this conversation;
|
|
6
|
+
the latest approval authorises implementation of spec 020.
|
|
7
|
+
|
|
8
|
+
## Implementation sequence
|
|
9
|
+
|
|
10
|
+
- [x] Inspect action wrapping, native input, observation/history, budgets, tests.
|
|
11
|
+
- [x] Add session metadata and retained successful-baseline lookup without changing
|
|
12
|
+
existing revision semantics or growing history.
|
|
13
|
+
- [x] Add strict condition schema, supported-tool validation, and journey tap wiring.
|
|
14
|
+
- [x] Add a request-scoped asynchronous pre-dispatch guard, pin the device, and
|
|
15
|
+
isolate the observation budget from the action budget (respecting outer budgets).
|
|
16
|
+
- [x] Integrate at Android/iOS input spawn boundaries; retain failure evidence
|
|
17
|
+
through adapter errors; defer guarded network-window reset until dispatch.
|
|
18
|
+
- [x] Preserve condition evidence and non-delivery semantics in compact/debug
|
|
19
|
+
responses, including journey stop and not-checked cases.
|
|
20
|
+
- [x] Add deterministic unit/adapter regression coverage and device journeys.
|
|
21
|
+
- [x] Update public docs/changelog and packaged agent usage guidance; sync/check plugin.
|
|
22
|
+
- [x] Run focused checks, maintainer verification, device suite, and focused device journeys.
|
|
23
|
+
- [x] Review changed code/contracts/callers and record results and limits.
|
|
24
|
+
|
|
25
|
+
## Expected affected files
|
|
26
|
+
|
|
27
|
+
- `src/observe/snapshot-metadata.ts`, `src/observe/index.ts`, `src/types.ts`.
|
|
28
|
+
- New shared condition schema/guard modules under `src/` as responsibility requires.
|
|
29
|
+
- `src/utils/operation-budget.ts`, Android/iOS input adapters.
|
|
30
|
+
- `src/server/tool-definitions.ts`, `tool-handlers.ts`, `journey-schema.ts`, `common.ts`.
|
|
31
|
+
- Unit tests under `test/unit/server/`, `test/unit/observe/`, and adapter fixtures.
|
|
32
|
+
- Focused reusable device helper under `test/device/manual/` if explicit simulator
|
|
33
|
+
selection/state setup makes it unsuitable for automatic generic suite execution.
|
|
34
|
+
- `docs/tools/interact.md`, `observe.md`, `docs/CHANGELOG.md`, `README.md`, and
|
|
35
|
+
`agent-plugin/skills/mobile-debug-mcp/SKILL.md`.
|
|
36
|
+
|
|
37
|
+
## Verification mapping
|
|
38
|
+
|
|
39
|
+
| Spec criteria | Verification |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| 1, 5, 8 | Real Android/iOS adapter calls using fake binaries; all supported actions, verification modes and response modes; unguarded compatibility tests |
|
|
42
|
+
| 2, 3 | Mutable hierarchy fixture changes text/bounds/enabled/state and changes during target resolution/adapter preparation; assert zero native input and exact final tree |
|
|
43
|
+
| 4 | Session, history expiry/eviction, device mismatch, error/malformed observation, timeout including late completion and child cleanup |
|
|
44
|
+
| 6 | Journey with earlier passed step, blocked conditional tap, later not-run step; both response modes |
|
|
45
|
+
| 7 | Runtime invalid-input matrix and advertised schemas; unsupported tools/steps rejected before any side effects |
|
|
46
|
+
| 9 | Overlapping guarded/unguarded calls and network-window reset spy; condition budget distinct from input budget; outer-budget expiry |
|
|
47
|
+
| 10 | Documentation and focused review against actual implementation |
|
|
48
|
+
|
|
49
|
+
Commands: focused test files via existing `tsx` runtime, then `npm run verify`,
|
|
50
|
+
`npm run test:device`, and focused device helper against built `dist/` modules.
|
|
51
|
+
Runtime: repository supports Node >=18; record actual Node/toolchain with results.
|
|
52
|
+
No new dependency, migration, helper APK, publication, or service is needed.
|
|
53
|
+
|
|
54
|
+
## Device routes and readiness
|
|
55
|
+
|
|
56
|
+
Available during spec reconnaissance: Android 16 `emulator-5554` and booted iOS
|
|
57
|
+
26.4 iPhone Air `F72108FF-C8D6-4C1E-980E-1AE5E5CCD27B`. The physical Pixel is
|
|
58
|
+
excluded from mutation checks. Tool access and fresh trees work on both simulators.
|
|
59
|
+
Before device runs, inspect current state and choose safe system controls; avoid
|
|
60
|
+
interacting with the open Modul8 subscription flow. Use an already-installed
|
|
61
|
+
system Settings app on Android/iOS where possible; no accounts or entitlements.
|
|
62
|
+
|
|
63
|
+
For each platform on the newly built server:
|
|
64
|
+
|
|
65
|
+
1. Open system Settings, observe its tree and session/revision, repeat to check a
|
|
66
|
+
stable baseline, then perform a guarded safe navigation tap and assert destination.
|
|
67
|
+
2. Observe the baseline, navigate to another Settings screen, then send a guarded
|
|
68
|
+
action with the old expectation; assert non-delivery and returned current tree.
|
|
69
|
+
Confirm the blocked action has no effect using a fresh state assertion.
|
|
70
|
+
3. Run a journey with an earlier successful wait/assert, an old-revision guarded
|
|
71
|
+
tap, and a later action. Confirm the tap fails, later step is not run, and state
|
|
72
|
+
stays unchanged. Repeat relevant compact/debug evidence checks.
|
|
73
|
+
|
|
74
|
+
Exact selectors depend on the current simulator OS tree and will be recorded
|
|
75
|
+
with results. Restore original foreground apps where practical; do not alter
|
|
76
|
+
settings, app data, or subscriptions. Owner of local prerequisites: current task.
|
|
77
|
+
Unavailable/timeout and last-moment preparation races use controlled fake binaries;
|
|
78
|
+
no forced device disconnect or privileged settings change is needed. This is
|
|
79
|
+
reported as simulated error-path evidence, separate from real-device passes.
|
|
80
|
+
|
|
81
|
+
## Results
|
|
82
|
+
|
|
83
|
+
Implementation is present. Node.js v23.11.0 was used locally (the supported
|
|
84
|
+
minimum is >=18; CI's Node 18 run has not been executed locally).
|
|
85
|
+
|
|
86
|
+
### Automated checks
|
|
87
|
+
|
|
88
|
+
- Initial `npm run verify`: PASS, including lint, build, unit tests, plugin check.
|
|
89
|
+
- Focused conditional action regression: PASS using production Android/iOS input
|
|
90
|
+
adapters and controlled fake native binaries. Coverage includes all supported
|
|
91
|
+
actions, each verification/response mode, late target-preparation changes,
|
|
92
|
+
mismatches, unavailable/malformed observations, session/history/device rejection,
|
|
93
|
+
request isolation, native input failure, and journey stop. Complete native
|
|
94
|
+
observation/input paths also pass with fake ADB/idb, including child-close
|
|
95
|
+
evidence for condition timeouts. This is simulated platform evidence.
|
|
96
|
+
- Focused schema parity and retention tests: PASS. Advertised JSON schemas are
|
|
97
|
+
exercised using the existing MCP SDK's validator, alongside runtime schemas.
|
|
98
|
+
All elements contribute to revision checks beyond the bounded delta history.
|
|
99
|
+
- `npm run sync:agent-plugin`: PASS; no generated manifest differences.
|
|
100
|
+
- Final `npm run verify`: PASS (lint, build, 59 unit-test files, plugin consistency).
|
|
101
|
+
Log: `/tmp/mobile-debug-conditional-verify-final.log`.
|
|
102
|
+
- Initial `npm run test:device`: FAIL at iOS UI-tree smoke (`Invalid UI tree resolution`) while the original simulator was booting.
|
|
103
|
+
Android screenshot/log/tree wrappers passed. iOS screenshot wrapper passed;
|
|
104
|
+
the iOS log wrapper also reported pass but its underlying log retrieval printed
|
|
105
|
+
`getpwuid_r` / unavailable log-store errors, so this is not evidence of usable
|
|
106
|
+
iOS logs. These wrappers use automatic device selection; focused tests below
|
|
107
|
+
pin their simulator explicitly. Full log: `/tmp/mobile-debug-conditional-device-suite.log`.
|
|
108
|
+
|
|
109
|
+
- Final `npm run test:device`: PASS after the replacement iPhone 17 Pro finished
|
|
110
|
+
booting. All six Android/iOS screenshot, log, and tree smoke wrappers passed,
|
|
111
|
+
without the earlier iOS log-store diagnostics. Log:
|
|
112
|
+
`/tmp/mobile-debug-conditional-device-suite-final.log`.
|
|
113
|
+
- Final documentation relative-link checks and `git diff --check`: PASS.
|
|
114
|
+
- Final lint after the device helper's selector/start-state correction: PASS.
|
|
115
|
+
|
|
116
|
+
### Focused Android emulator evidence
|
|
117
|
+
|
|
118
|
+
Command (against the built server over MCP stdio, no mocked tool handlers):
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
node --import tsx test/device/manual/interact/conditional_actions.manual.ts \
|
|
122
|
+
--platform android --id emulator-5554 \
|
|
123
|
+
--target-text 'Network & internet' --destination-text Internet \
|
|
124
|
+
--output /tmp/mobile-debug-conditional-android.json
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Device: Android 16 sdk_gphone64_arm64, `emulator-5554`.
|
|
128
|
+
Build: local `dist/server.js`, handshake `mobile-debug-mcp` 0.32.4, with
|
|
129
|
+
`expectedState` confirmed in its advertised tool schema. This is the working-tree
|
|
130
|
+
build, not proof of a published release.
|
|
131
|
+
Starting state: system Settings root, with Network & internet visible.
|
|
132
|
+
|
|
133
|
+
| Journey | Actions / expected outcome | Observed outcome | Result |
|
|
134
|
+
| --- | --- | --- | --- |
|
|
135
|
+
| Matching snapshot | Observe Settings; conditional coordinate tap Network & internet | `matched`, delivered once; fresh tree contains Internet | PASS |
|
|
136
|
+
| Stale snapshot, compact | On network page, attempt safe back-navigation tap with root expectation | `PRECONDITION_FAILED`, not delivered; compact tree contains Internet; fresh tree confirms network page remains | PASS |
|
|
137
|
+
| Stale snapshot, debug | Repeat guarded back navigation with root expectation | Same non-delivery and unchanged destination, full debug evidence | PASS |
|
|
138
|
+
| Journey stop, compact | Successful wait for Internet, conditional back tap with root expectation, later tap | Earlier wait passed, guarded step failed with fresh tree, later step `not_run`; destination remained | PASS |
|
|
139
|
+
| Journey stop, debug | Same sequence in debug mode | Same stop and preserved evidence | PASS |
|
|
140
|
+
|
|
141
|
+
Full structured evidence: `/tmp/mobile-debug-conditional-android.json`.
|
|
142
|
+
The five scenarios were rerun successfully against the final built server after
|
|
143
|
+
the compact-error and metadata-validity fixes and the helper selector correction.
|
|
144
|
+
The helper is reproducible and requires a deliberately prepared safe system
|
|
145
|
+
Settings screen. No setting or subscription was changed. The original Modul8
|
|
146
|
+
dev app was brought back to the foreground after checks.
|
|
147
|
+
|
|
148
|
+
### Focused iOS simulator evidence
|
|
149
|
+
|
|
150
|
+
The original iPhone Air simulator stalled during boot and was closed/rebooted by
|
|
151
|
+
the user. The replacement iPhone 17 Pro became responsive; read-only screenshot
|
|
152
|
+
and UI-tree checks confirmed Home and then Settings before running input checks.
|
|
153
|
+
No boot/restart/reset commands were issued by the agent.
|
|
154
|
+
|
|
155
|
+
Command:
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
node --import tsx test/device/manual/interact/conditional_actions.manual.ts \
|
|
159
|
+
--platform ios --id F9243BAF-62A8-4990-8A99-17F527F56FDA \
|
|
160
|
+
--target-text General --destination-text About \
|
|
161
|
+
--output /tmp/mobile-debug-conditional-ios.json
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Device: iOS 26.4 iPhone 17 Pro simulator,
|
|
165
|
+
`F9243BAF-62A8-4990-8A99-17F527F56FDA`.
|
|
166
|
+
Build: the same local `dist/server.js` 0.32.4 build and MCP stdio route as Android,
|
|
167
|
+
with condition support confirmed in tool discovery.
|
|
168
|
+
Starting state: Settings root with General visible, About absent.
|
|
169
|
+
|
|
170
|
+
| Journey | Actions / expected outcome | Observed outcome | Result |
|
|
171
|
+
| --- | --- | --- | --- |
|
|
172
|
+
| Matching snapshot | Observe Settings; conditional coordinate tap General | `matched`, delivered; fresh tree contains About | PASS |
|
|
173
|
+
| Stale snapshot, compact | Attempt safe back-navigation tap with Settings-root expectation | `PRECONDITION_FAILED`, not delivered; compact fresh tree contains About; new observation confirms General stays visible | PASS |
|
|
174
|
+
| Stale snapshot, debug | Same guarded back navigation | Same non-delivery and preserved full condition tree | PASS |
|
|
175
|
+
| Journey stop, compact | Successful wait for About, stale conditional tap on `resource_id: BackButton`, later back tap | Wait passed; condition failed; later step `not_run`; General remained visible | PASS |
|
|
176
|
+
| Journey stop, debug | Same sequence in debug mode | Same stop and fresh condition evidence | PASS |
|
|
177
|
+
|
|
178
|
+
Structured evidence: `/tmp/mobile-debug-conditional-ios.json`.
|
|
179
|
+
An initial helper attempt selected the application container sharing the Settings
|
|
180
|
+
label and correctly stopped with `ELEMENT_NOT_INTERACTABLE` / `not_checked`;
|
|
181
|
+
this was not counted as a conditional-journey pass. The helper now prefers the
|
|
182
|
+
unique native resource ID and requires the destination to be absent from its
|
|
183
|
+
starting screen. A subsequent incorrect-start attempt was also discarded.
|
|
184
|
+
The final five-pass run above starts at the confirmed root and uses BackButton.
|
|
185
|
+
|
|
186
|
+
Cleanup: only navigation was exercised. No setting, permission, account, or app
|
|
187
|
+
data was changed. Home is restored after the smoke-suite rerun.
|
|
188
|
+
|
|
189
|
+
### Focused quality review
|
|
190
|
+
|
|
191
|
+
Reviewed the shared condition schema and advertised definitions, action wrapper
|
|
192
|
+
and journey dispatch, Android/iOS native input boundaries, observation metadata
|
|
193
|
+
and history, budget ancestors, compact serialization, error/recovery mapping,
|
|
194
|
+
and associated tests/callers. The guard runs after adapter preparation, and
|
|
195
|
+
blocked conditions replace generic adapter failures without inventing an execute
|
|
196
|
+
trace or allowing recovery. Condition observation budgets pause only the current
|
|
197
|
+
action; enclosing deadlines still apply. Tests check no late input after timeout
|
|
198
|
+
and native observation child closure. No new framework or dependency was added.
|
|
199
|
+
|
|
200
|
+
Review findings fixed:
|
|
201
|
+
|
|
202
|
+
- Journey light-verification response overlay must preserve `not_requested` when
|
|
203
|
+
its conditional tap did not dispatch.
|
|
204
|
+
- Compact tree responses must retain observation errors instead of looking like
|
|
205
|
+
successful deltas carrying an old revision/session. Added a regression test.
|
|
206
|
+
- Repeated successful observations refresh retained revision timestamps, so a
|
|
207
|
+
recently observed stable screen does not yield an immediately unusable baseline.
|
|
208
|
+
|
|
209
|
+
Limits: live simulator checks cover conditional taps and journey stops; conditional
|
|
210
|
+
swipes and Android typing/back use production-adapter tests with fake native
|
|
211
|
+
tools. Physical devices were not used for mutation checks. UI normalization, the observation-to-OS-input race, and concurrent external
|
|
212
|
+
input remain explicit spec limits. No mutation testing was performed. No tests
|
|
213
|
+
or validation rules were weakened. Existing uncommitted process/skill relocation
|
|
214
|
+
work was preserved. No package was published and nothing was committed/pushed.
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# Spec 020: conditional actions
|
|
2
|
+
|
|
3
|
+
Date: 2026-10-29.
|
|
4
|
+
Status: approved — implementation authorised.
|
|
5
|
+
Intent: [intent.md](intent.md), approved in the current conversation by the
|
|
6
|
+
originator's “yes” in response to the captured intent.
|
|
7
|
+
|
|
8
|
+
## Outcome
|
|
9
|
+
|
|
10
|
+
A client can request a supported UI action only if the observed UI snapshot still
|
|
11
|
+
matches its expectation. The server obtains a new hierarchy immediately before
|
|
12
|
+
native input dispatch. A mismatch prevents input and returns the fresh hierarchy
|
|
13
|
+
so the client can decide what to do next.
|
|
14
|
+
|
|
15
|
+
This is a precondition check, not a guarantee that the UI remains unchanged until
|
|
16
|
+
the OS delivers the input or that the action achieves its intended outcome.
|
|
17
|
+
|
|
18
|
+
## Current code and observations
|
|
19
|
+
|
|
20
|
+
- `src/server/tool-handlers.ts` wraps action controls, delivery status, response
|
|
21
|
+
modes, and journeys. Existing assertions are separate from native input.
|
|
22
|
+
- `ToolsInteract.tapElementHandler` revalidates a handle but does not compare a
|
|
23
|
+
client-provided expected snapshot. Selector resolution can also precede input.
|
|
24
|
+
- `src/observe/snapshot-metadata.ts` computes a signature from the full element
|
|
25
|
+
list, screen, and resolution, and assigns revisions per resolved device.
|
|
26
|
+
Revisions remain stable for consecutive identical signatures. State, bounds,
|
|
27
|
+
identities, and actionability contribute; screen fingerprints omit some of
|
|
28
|
+
these and are unsuitable as the only condition.
|
|
29
|
+
- Revisions are process-local integers. Revision numbers alone cannot safely
|
|
30
|
+
distinguish server restarts. Error trees may carry a previous revision, so a
|
|
31
|
+
revision on an error response is not proof of a successful observation.
|
|
32
|
+
- Android and iOS input adapters mark dispatch immediately before spawning input.
|
|
33
|
+
The new guard must run there, after metadata/capability checks and resolution,
|
|
34
|
+
rather than only at entry to the MCP handler.
|
|
35
|
+
|
|
36
|
+
### Device reconnaissance (existing behaviour, not feature verification)
|
|
37
|
+
|
|
38
|
+
Read-only `list_devices` and `get_ui_tree` calls through the connected mobile-debug
|
|
39
|
+
MCP succeeded on 2026-10-29:
|
|
40
|
+
|
|
41
|
+
| Platform | Device | Observed state | Evidence |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| Android 16 | `emulator-5554`, sdk_gphone64_arm64 | Modul8 Subscription screen | 18 elements, revision 1, 1280 × 2856, tree call about 4.1 seconds |
|
|
44
|
+
| iOS 26.4 | `F72108FF-C8D6-4C1E-980E-1AE5E5CCD27B`, iPhone Air simulator | Home screen | 14 elements, revision 2, 420 × 912, tree call about 8.4 seconds |
|
|
45
|
+
|
|
46
|
+
These are observations from the connected server; its exact build was not exposed
|
|
47
|
+
by these calls. The local checkout declares version 0.32.4. No inputs, installs,
|
|
48
|
+
or settings changes were made. Verification must use a build from the changed
|
|
49
|
+
checkout; these reads do not establish conditional-action behaviour.
|
|
50
|
+
|
|
51
|
+
## Initial scope
|
|
52
|
+
|
|
53
|
+
| Tool | Platforms | Condition support |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| `tap` | Android, iOS | Optional expected snapshot |
|
|
56
|
+
| `tap_element` (selector or handle) | Android, iOS | Optional expected snapshot |
|
|
57
|
+
| `swipe` | Android, iOS | Optional expected snapshot |
|
|
58
|
+
| `type_text` | Android | Optional expected snapshot |
|
|
59
|
+
| `press_back` | Android | Optional expected snapshot |
|
|
60
|
+
| `run_journey` | Android, iOS | Optional condition on each `tap` step |
|
|
61
|
+
|
|
62
|
+
No platform gains an input capability it does not already support.
|
|
63
|
+
|
|
64
|
+
Out of scope: lifecycle/build/install/reset tools, `scroll_to_element`,
|
|
65
|
+
`adjust_control`, other multi-input operations, arbitrary predicate expressions,
|
|
66
|
+
per-element-only conditions, automatic retries/replanning, rollback, screenshot
|
|
67
|
+
comparison, and changing post-action verification semantics.
|
|
68
|
+
|
|
69
|
+
A supplied `expectedState` on any unsupported tool or journey step is rejected
|
|
70
|
+
as `INVALID_ARGUMENT` before any operation. It must never be silently ignored.
|
|
71
|
+
Conditions cannot be journey defaults or a condition on the journey as a whole.
|
|
72
|
+
|
|
73
|
+
## Input contract
|
|
74
|
+
|
|
75
|
+
Add this optional field to each supported tool and journey `tap` step:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
expectedState?: {
|
|
79
|
+
snapshotSessionId: string;
|
|
80
|
+
snapshotRevision: number;
|
|
81
|
+
timeoutMs?: number; // default 15000; integer, 100–60000 ms
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The object is strict. The session ID is nonempty; revision is a positive safe
|
|
86
|
+
integer. Invalid types, null, missing required members, extra members, and
|
|
87
|
+
out-of-range values return `INVALID_ARGUMENT` before observation or input.
|
|
88
|
+
Validate both the advertised JSON schema and actual runtime arguments.
|
|
89
|
+
|
|
90
|
+
Guarded standalone calls require explicit `platform` and concrete `deviceId`.
|
|
91
|
+
The ID must identify the same resolved device as the baseline; aliases such as
|
|
92
|
+
`booted` and `default` are not valid guarded targets. Journey taps inherit the
|
|
93
|
+
journey's once-resolved concrete platform/device. Unguarded calls retain their
|
|
94
|
+
existing device selection and argument behaviour.
|
|
95
|
+
|
|
96
|
+
### Obtaining an expectation
|
|
97
|
+
|
|
98
|
+
Successful UI-tree observations add `snapshot_session_id`, a random ID generated
|
|
99
|
+
once per server process. Keep existing revision and timestamp fields intact.
|
|
100
|
+
Carry the session ID through debug and compact `get_ui_tree` responses and nested
|
|
101
|
+
UI-tree evidence. This field does not imply that an error response is usable.
|
|
102
|
+
|
|
103
|
+
The client takes `snapshot_session_id` and `snapshot_revision` from a successful
|
|
104
|
+
`get_ui_tree` on the same device and supplies them as the expectation:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"platform": "android",
|
|
109
|
+
"deviceId": "emulator-5554",
|
|
110
|
+
"x": 100,
|
|
111
|
+
"y": 200,
|
|
112
|
+
"expectedState": {
|
|
113
|
+
"snapshotSessionId": "<session from get_ui_tree>",
|
|
114
|
+
"snapshotRevision": 12
|
|
115
|
+
},
|
|
116
|
+
"responseMode": "compact",
|
|
117
|
+
"verificationMode": "none"
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Require a retained successful baseline for this session, device, and revision.
|
|
122
|
+
Reuse the existing bounded snapshot history (up to eight revisions, ten-minute
|
|
123
|
+
retention); no new unbounded history. Expired, evicted, other-device, or
|
|
124
|
+
previous-session baselines cannot authorise input. Clients must observe again.
|
|
125
|
+
Check baseline availability before the fresh read and retain its identity for
|
|
126
|
+
that check, so the fresh read itself cannot evict the baseline being checked.
|
|
127
|
+
|
|
128
|
+
## Meaning of expected state
|
|
129
|
+
|
|
130
|
+
Match means the fresh successful observation's revision equals the retained
|
|
131
|
+
expected revision within the same server session and device. Use the existing
|
|
132
|
+
snapshot signature and revision rules; do not substitute screen fingerprints,
|
|
133
|
+
partial delta history, or target identity alone.
|
|
134
|
+
|
|
135
|
+
This checks the normalized accessibility snapshot, not every app or OS state.
|
|
136
|
+
The signature includes all elements even though retained delta records are
|
|
137
|
+
bounded. Existing normalization trims/lowercases string fields, includes state
|
|
138
|
+
and bounds, and does not prove the foreground package/activity (current tree
|
|
139
|
+
`screen` is empty on both adapters). Pixel-only changes, inaccessible controls,
|
|
140
|
+
backend state, and changes hidden by normalization are outside the guarantee.
|
|
141
|
+
An intervening observed change followed by a return to the old screen produces
|
|
142
|
+
a different revision and is conservatively rejected. An unobserved transient
|
|
143
|
+
change cannot be detected. Document these limits in tool usage guidance.
|
|
144
|
+
|
|
145
|
+
## Dispatch rules
|
|
146
|
+
|
|
147
|
+
1. Validate the entire condition, supported tool/step, and guarded targeting
|
|
148
|
+
before starting the operation. Resolve and pin the concrete device once.
|
|
149
|
+
2. Resolve the requested target and perform existing actionability/capability
|
|
150
|
+
checks. Existing target failures still prevent input normally; an unperformed
|
|
151
|
+
input does not imply its condition was checked.
|
|
152
|
+
3. Immediately before the input subprocess would spawn, validate the retained
|
|
153
|
+
baseline and obtain a new, uncached UI hierarchy on the pinned device. Do not
|
|
154
|
+
use a selector-resolution tree, previous assertion, compact delta, or an
|
|
155
|
+
earlier pre-action fingerprint as the final check.
|
|
156
|
+
4. Reject error/malformed trees, missing revision/session metadata, or a response
|
|
157
|
+
identifying another device. An old revision on an error tree never passes.
|
|
158
|
+
5. Compare the expectation once. No polling until it becomes true, automatic
|
|
159
|
+
rebasing, or retry of input. Existing internal acquisition retries remain
|
|
160
|
+
bounded by the condition timeout; they do not authorise a stale read.
|
|
161
|
+
6. On match, record the successful condition result, check the remaining action
|
|
162
|
+
budget, mark dispatch, and spawn native input. No further observation,
|
|
163
|
+
target resolution, metadata lookup, intentional delay, or asynchronous
|
|
164
|
+
capability probe may occur between the final check and input spawn.
|
|
165
|
+
7. On failure, preserve the structured condition result across adapter catches.
|
|
166
|
+
Do not mark dispatch or reset the network action window. Skip post-input
|
|
167
|
+
verification and recovery, and return immediately.
|
|
168
|
+
|
|
169
|
+
The condition observation has its own `timeoutMs` budget. Its elapsed time does
|
|
170
|
+
not consume the existing action-dispatch budget; target resolution and input
|
|
171
|
+
retain existing action controls. Check a nested budget's outer deadline too
|
|
172
|
+
where applicable. An expired condition read cannot permit later background input.
|
|
173
|
+
Condition checking is mandatory even with `verificationMode: "none"` or `"light"`.
|
|
174
|
+
|
|
175
|
+
The guard must be scoped to the request using existing async-context conventions;
|
|
176
|
+
it must not leak into another call or recursively guard its own observation.
|
|
177
|
+
There is no cross-client transaction or device lock in this initial version.
|
|
178
|
+
Concurrent actions, external input, and app updates may still change the UI
|
|
179
|
+
after observation; clients should sequence actions on a device. Do not claim
|
|
180
|
+
atomicity, exclusivity, or rollback.
|
|
181
|
+
|
|
182
|
+
## Response contract
|
|
183
|
+
|
|
184
|
+
Preserve existing action fields. Guarded calls add:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
condition: {
|
|
188
|
+
status: 'matched' | 'mismatch' | 'unavailable' | 'not_checked';
|
|
189
|
+
expected: { snapshot_session_id: string; snapshot_revision: number };
|
|
190
|
+
observed: GetUITreeResponse | null;
|
|
191
|
+
elapsed_ms: number;
|
|
192
|
+
reason?: string;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Stable `reason` values for unavailable checks: `BASELINE_UNAVAILABLE`,
|
|
197
|
+
`SESSION_MISMATCH`, `DEVICE_MISMATCH`, `OBSERVATION_FAILED`,
|
|
198
|
+
`OBSERVATION_INVALID`, and `CONDITION_TIMEOUT`. `not_checked` uses
|
|
199
|
+
`ACTION_NOT_REACHED` when target/capability failure precedes the guard.
|
|
200
|
+
`elapsed_ms` measures the condition check, or zero if it was not reached.
|
|
201
|
+
|
|
202
|
+
| Outcome | Action result | Delivery fields | Condition evidence |
|
|
203
|
+
| --- | --- | --- | --- |
|
|
204
|
+
| Match, then input | Existing action outcome | Existing delivery semantics | `matched`, fresh observed tree |
|
|
205
|
+
| State differs | `success: false`, `failure_code: PRECONDITION_FAILED`, `retryable: false` | `dispatch_started: false`, `delivery_status: not_delivered`, `lifecycle_state: failed` | `mismatch`, exact fresh observed tree |
|
|
206
|
+
| Check cannot be completed | `success: false`, `failure_code: PRECONDITION_UNAVAILABLE`, `retryable: false` | Same non-delivery fields | `unavailable`, reason; valid fresh tree if available, otherwise null |
|
|
207
|
+
| Target/capability failure before check | Existing failure code | No input dispatch | `not_checked`, observed null |
|
|
208
|
+
|
|
209
|
+
For blocked input, `verification.status` is `not_requested`; this is a failed
|
|
210
|
+
precondition, not a failed post-action assertion. If input fails after a match,
|
|
211
|
+
retain `matched` alongside the actual input failure; a matching condition is not
|
|
212
|
+
proof of delivery or outcome correctness. Add failure codes to shared types and
|
|
213
|
+
recovery handling without enabling automatic retry.
|
|
214
|
+
|
|
215
|
+
Debug mode includes full condition evidence. Compact mode always keeps condition
|
|
216
|
+
status, expected values, elapsed time, and reason. On mismatch/unavailable it also
|
|
217
|
+
keeps `observed`, including the full tree when available: this is a documented
|
|
218
|
+
exception to compact mode's usual omission of device IDs and tree contents.
|
|
219
|
+
On match, compact mode keeps only observed snapshot session/revision and capture
|
|
220
|
+
time instead of the full tree. `not_checked` keeps observed null.
|
|
221
|
+
|
|
222
|
+
The returned mismatch tree is the actual observation that blocked input; do not
|
|
223
|
+
replace it with a later snapshot. No automatic screenshot or log capture is
|
|
224
|
+
required. Apply existing response-data handling; UI trees can contain private
|
|
225
|
+
text. Do not log expectation payloads, input text, or full trees by default.
|
|
226
|
+
|
|
227
|
+
## Journey behaviour
|
|
228
|
+
|
|
229
|
+
A conditional tap uses the same guard as a standalone `tap_element`. Conditions
|
|
230
|
+
are evaluated at that step's dispatch boundary, after any earlier waits/asserts.
|
|
231
|
+
A mismatch or unavailable condition fails that step and sets
|
|
232
|
+
`stopped_at_step_id`; subsequent steps are `not_run`. Earlier completed steps
|
|
233
|
+
remain completed. Preserve condition evidence in both journey response modes,
|
|
234
|
+
regardless of `captureOnFailure`. Existing optional debug failure snapshots must
|
|
235
|
+
not overwrite the guard evidence. There is no rollback or implicit baseline
|
|
236
|
+
refresh from an earlier step.
|
|
237
|
+
|
|
238
|
+
## Acceptance criteria
|
|
239
|
+
|
|
240
|
+
1. Matching expectations dispatch exactly once on every supported tool/platform;
|
|
241
|
+
existing post-action controls still apply.
|
|
242
|
+
2. A changed text, bounds, enabled flag, or exposed element state blocks input
|
|
243
|
+
even when the target still exists; return the blocking observation.
|
|
244
|
+
3. Detect a change after selector resolution and during adapter preparation by
|
|
245
|
+
checking at the final dispatch boundary, not only at handler entry.
|
|
246
|
+
4. Old sessions, expired/evicted baselines, another-device baselines, failed
|
|
247
|
+
observations, malformed metadata, and condition timeout never dispatch input.
|
|
248
|
+
5. Conditions are enforced in none/light/full verification and compact/debug
|
|
249
|
+
response modes; mismatch evidence survives compact serialization.
|
|
250
|
+
6. A failed journey condition stops subsequent steps and preserves earlier results.
|
|
251
|
+
7. Unsupported/malformed conditions fail validation before side effects.
|
|
252
|
+
8. Unguarded calls preserve existing contracts and behaviour.
|
|
253
|
+
9. Concurrent requests do not share guard state; blocked input neither resets
|
|
254
|
+
the network window nor triggers retry/recovery. No late dispatch after timeout.
|
|
255
|
+
10. Documentation explains snapshot normalization, history/session limits,
|
|
256
|
+
additional observation latency, data exposure in mismatch responses, and the
|
|
257
|
+
observation-to-input race.
|
|
258
|
+
|
|
259
|
+
## Affected surfaces and verification expectations
|
|
260
|
+
|
|
261
|
+
Implementation is expected in tool definitions and runtime validation, action
|
|
262
|
+
wrapping/serialization and journey schema, snapshot metadata/history and types,
|
|
263
|
+
and the Android/iOS native input boundaries. Reuse existing async contexts,
|
|
264
|
+
budgets, and device resolution. Keep platform subprocess logic in its adapters.
|
|
265
|
+
Update interaction/observation docs, README usage, changelog, and packaged agent
|
|
266
|
+
skill guidance if it describes the new workflow; use the existing plugin sync
|
|
267
|
+
command when changing its source.
|
|
268
|
+
|
|
269
|
+
Use the repository's self-running `tsx` unit tests to prove dispatch prevention,
|
|
270
|
+
freshness ordering, error propagation, exact evidence, isolation, timeout, and
|
|
271
|
+
schema parity. Exercise real adapter paths with controlled subprocess fixtures;
|
|
272
|
+
a test that mocks away the guard boundary is insufficient.
|
|
273
|
+
|
|
274
|
+
Run `npm run verify` and `npm run test:device` after implementation, plus focused
|
|
275
|
+
Android and iOS simulator journeys using the newly built server. Use an isolated
|
|
276
|
+
test app or safe system controls, avoiding changes to the currently open Modul8
|
|
277
|
+
subscription flow. Verify an unchanged-state action, deliberate intervening
|
|
278
|
+
state change with non-delivery, and journey stop. Use controlled failures for
|
|
279
|
+
unavailable/timeout and race ordering; report simulated and device evidence
|
|
280
|
+
separately. Record exact test routes and cleanup in the implementation plan.
|
|
281
|
+
|
|
282
|
+
## Risks and decisions
|
|
283
|
+
|
|
284
|
+
- Whole-tree changes may conservatively block an action due to unrelated dynamic
|
|
285
|
+
UI; this initial version favours explicit rejection. Per-element predicates
|
|
286
|
+
can be specified later if needed.
|
|
287
|
+
- Hierarchy acquisition adds seconds to guarded input; the separate condition
|
|
288
|
+
budget is intentional and must not silently weaken freshness.
|
|
289
|
+
- Returning a fresh hierarchy in compact failures increases payload size and
|
|
290
|
+
exposes the same UI text as `get_ui_tree`; callers must handle it accordingly.
|
|
291
|
+
- Session metadata is an additive contract change. Older servers cannot accept
|
|
292
|
+
guarded calls; clients must obtain expectations from this supporting server.
|
|
293
|
+
Local tests are not proof of an installed or published version's behaviour.
|
|
294
|
+
- No data migration, new dependency, helper APK, external service, or automatic
|
|
295
|
+
release is required. Package publication remains a separate authorised action.
|
|
296
|
+
|
|
297
|
+
## Approval
|
|
298
|
+
|
|
299
|
+
Approved by the originator on 2026-10-29 in the current conversation: “yes”,
|
|
300
|
+
in direct response to the linked complete spec and the listed first-version
|
|
301
|
+
scope. Implementation is authorised within that scope.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Intent: app readiness signals
|
|
2
|
+
|
|
3
|
+
Originator: repository maintainer (current conversation).
|
|
4
|
+
Date: 2026-10-29. Status: approved.
|
|
5
|
+
Source: [GitHub issue #65: App readiness signals](https://github.com/clivejefferies/mobile-debug-tools/issues/65), roadmap priority 2 of 9.
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
Clients can mistake an observation captured during loading or a transition for
|
|
10
|
+
settled app content. They also need to distinguish an unresponsive app from an
|
|
11
|
+
app paused by a debugger, where evidence permits.
|
|
12
|
+
|
|
13
|
+
## Desired outcome
|
|
14
|
+
|
|
15
|
+
Android/iOS observations and bounded waits expose evidence-backed readiness:
|
|
16
|
+
loading, transitioning, settled, unresponsive, and debugger-paused. Results carry
|
|
17
|
+
the supporting observations and uncertainty so clients can decide whether to act,
|
|
18
|
+
wait, or investigate. A screenshot alone does not establish readiness.
|
|
19
|
+
|
|
20
|
+
## Scope and success criteria
|
|
21
|
+
|
|
22
|
+
- Build on existing observation, loading-state, snapshot, and wait contracts.
|
|
23
|
+
- Distinguish readiness states only when available evidence supports them;
|
|
24
|
+
otherwise report uncertainty rather than guess.
|
|
25
|
+
- Bound waits and distinguish timeout/observation failure from proven app
|
|
26
|
+
unresponsiveness.
|
|
27
|
+
- Coordinate with debugger sessions when available; do not invent a current
|
|
28
|
+
debugger integration.
|
|
29
|
+
- Preserve existing tool behaviour unless the approved spec explicitly changes it.
|
|
30
|
+
|
|
31
|
+
## Exclusions and questions
|
|
32
|
+
|
|
33
|
+
No runtime debugger implementation, automatic recovery/restart, or performance
|
|
34
|
+
profiling. The spec must define state evidence, sampling/stability rules,
|
|
35
|
+
unsupported signals, API placement, and how long observations remain current.
|
|
36
|
+
|
|
37
|
+
## Approval
|
|
38
|
+
|
|
39
|
+
Approved on 2026-10-29 by the originator’s “yes” in direct response to the
|
|
40
|
+
complete intent summary and linked artifact in this conversation.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Plan: app readiness signals
|
|
2
|
+
|
|
3
|
+
Date: 2026-10-29. Status: complete — implementation and required verification passed.
|
|
4
|
+
[Intent](intent.md) and [spec](spec.md) separately approved in this conversation;
|
|
5
|
+
“yep” authorises implementation of spec 021. Preserve existing uncommitted work.
|
|
6
|
+
|
|
7
|
+
## Implementation
|
|
8
|
+
|
|
9
|
+
- [x] Add a request-local classifier and typed, bounded evidence summaries in
|
|
10
|
+
`src/observe/readiness.ts` and shared types.
|
|
11
|
+
- [x] Add summaries to tree observations without extra reads; expose strict new
|
|
12
|
+
tool schemas/handlers using pinned devices and existing operation budgets.
|
|
13
|
+
- [x] Add opt-in readiness to existing UI/changed-UI waits, sharing their samples
|
|
14
|
+
and one deadline; retain default semantics.
|
|
15
|
+
- [x] Add deterministic classifier, schema, wait, native acquisition/error/timeout
|
|
16
|
+
tests in `test/unit/`; reuse existing self-running `tsx` conventions.
|
|
17
|
+
- [x] Update tool docs, README/changelog, and packaged agent usage guidance.
|
|
18
|
+
- [x] Verify, review the changed contracts, and record results below.
|
|
19
|
+
|
|
20
|
+
## Verification
|
|
21
|
+
|
|
22
|
+
| Criteria | Checks |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| State precedence and uncertainty | Explicit expected classifier outputs with controlled monotonic times; native/label loaders, changes, stable UI, empty/error/old-session/wrong-device samples |
|
|
25
|
+
| Bounded sampling and isolation | Production observation adapters with fake binaries, timeout/child-close evidence, outer budget, concurrent requests, no late success |
|
|
26
|
+
| Schemas and compatibility | SDK JSON-schema validation against runtime validation; compact/debug tree responses; default and opt-in waits; no extra reads |
|
|
27
|
+
| Real device behaviour | Updated server over MCP stdio on Android emulator and iOS simulator: stable UI, controlled loader → settled, bounded loading timeout, predicate present during loader, and changed-UI wait |
|
|
28
|
+
|
|
29
|
+
Commands: focused tests with the existing `tsx` runtime, `npm run verify`,
|
|
30
|
+
`npm run test:device`, and a reproducible focused device helper under
|
|
31
|
+
`test/device/manual/`. Native fixture apps provide a timed loader without
|
|
32
|
+
accounts, network services, settings changes, or inspection helper services.
|
|
33
|
+
Use the existing Android fixture build conventions and installed iOS SDK tools.
|
|
34
|
+
Known targets: Android `emulator-5554`; iOS
|
|
35
|
+
`F9243BAF-62A8-4990-8A99-17F527F56FDA` (reconfirm availability).
|
|
36
|
+
Fixture builds/access are local prerequisites owned by this task. Record exact
|
|
37
|
+
routes, artifacts, build identity and cleanup; restore original foreground apps.
|
|
38
|
+
Unsupported unresponsive/debugger pause signals must remain explicit, not tested
|
|
39
|
+
as implemented capabilities. No deployment or package publication is required.
|
|
40
|
+
|
|
41
|
+
## Results and review
|
|
42
|
+
|
|
43
|
+
Local build/version: working-tree `dist/server.js`, MCP handshake 0.32.4,
|
|
44
|
+
Node v23.11.0. This is local evidence, not a published package or Node 18 CI run.
|
|
45
|
+
|
|
46
|
+
| Check | Result / evidence |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| Classifier, schemas, opt-in waits | PASS: state precedence, monotonic stability, invalid/error continuity, no extra predicate reads, explicit unsupported states, timeout uncertainty |
|
|
49
|
+
| Production Android/iOS observation adapters with fake binaries | PASS: two-sample stability, native loaders, timeout child closure, and killing a hung iOS metadata probe; simulated error evidence |
|
|
50
|
+
| Maintainer checks | PASS: final `npm run verify` (lint, build, 62 unit-test files, plugin consistency); `/tmp/mobile-readiness-verify-complete.log` |
|
|
51
|
+
| Existing `npm run test:device` | PASS: all six Android/iOS screenshot, log, and tree smoke wrappers; `/tmp/mobile-readiness-device-suite.log` |
|
|
52
|
+
| Native fixture journeys through built MCP stdio | PASS: six on each simulator; evidence linked below |
|
|
53
|
+
|
|
54
|
+
Focused device commands (each builds, installs, and uninstalls its own offline
|
|
55
|
+
native fixture; temporary artifacts are removed):
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
node --import tsx test/device/manual/interact/readiness.manual.ts \
|
|
59
|
+
--platform android --id emulator-5554 --output /tmp/mobile-readiness-android.json
|
|
60
|
+
node --import tsx test/device/manual/interact/readiness.manual.ts \
|
|
61
|
+
--platform ios --id F9243BAF-62A8-4990-8A99-17F527F56FDA \
|
|
62
|
+
--output /tmp/mobile-readiness-ios.json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Android 16 sdk_gphone64_arm64 and iOS 26.4 iPhone 17 Pro were explicitly pinned.
|
|
66
|
+
Starting state: newly launched fixture, Ready visible, loader hidden. Show loader
|
|
67
|
+
starts a 12-second loader while Ready stays visible; completion displays Done.
|
|
68
|
+
|
|
69
|
+
| Scenario | Android | iOS | Observed proof |
|
|
70
|
+
| --- | --- | --- | --- |
|
|
71
|
+
| Stable content | PASS | PASS | Two fresh samples report settled |
|
|
72
|
+
| Controlled loader | PASS | PASS | Loading; Android native indicator, iOS accessible label marked heuristic |
|
|
73
|
+
| Bounded wait | PASS | PASS | 100 ms deadline rejects readiness; unknown plus deadline limitation, no invented unresponsive state |
|
|
74
|
+
| Predicate + readiness | PASS | PASS | Ready remains visible during loading; success only after settled, Done independently observed |
|
|
75
|
+
| Changed-UI wait | PASS | PASS | Loader completion changes hierarchy; opt-in wait returns success with settled evidence |
|
|
76
|
+
| Readiness wait success | PASS | PASS | Bounded wait establishes settled content |
|
|
77
|
+
|
|
78
|
+
All twelve device scenarios passed. Both fixture apps were uninstalled, build
|
|
79
|
+
folders removed, Android’s original foreground app restored, and iOS returned
|
|
80
|
+
to Home. No settings, permissions, accounts, or production app data were changed.
|
|
81
|
+
Structured evidence: `/tmp/mobile-readiness-android.json` and
|
|
82
|
+
`/tmp/mobile-readiness-ios.json`.
|
|
83
|
+
|
|
84
|
+
Review covered classification/normalization, schema parity, serialization,
|
|
85
|
+
request-local state, ancestor budgets, native acquisition and cleanup, existing
|
|
86
|
+
wait callers, conditional-action compatibility, and evidence privacy. Findings
|
|
87
|
+
fixed: discovery budget expiry must not look like a proven missing device;
|
|
88
|
+
iOS probes/metadata must respect the active budget; timeout envelopes must retain
|
|
89
|
+
an already-observed predicate instead of pretending its target was absent.
|
|
90
|
+
The Android helper uses the stable accessibility ID, avoiding button-case
|
|
91
|
+
variation. Initial fixture attempts exposed these issues and were not counted
|
|
92
|
+
as passes; the later runs above passed without weakening assertions.
|
|
93
|
+
|
|
94
|
+
Limits: unresponsive/debugger-paused detection remains unsupported. Stability
|
|
95
|
+
covers normalized sampled UI, not app identity, responsiveness, backend outcome,
|
|
96
|
+
or atomic input readiness. Transport cancellation still relies on deadlines.
|
|
97
|
+
No mutation testing or physical-device input checks were performed. The prior
|
|
98
|
+
conditional work was committed externally during this task and was preserved;
|
|
99
|
+
this task did not commit, push, or publish readiness changes.
|
|
100
|
+
|
|
101
|
+
Final source/diagnostic correction was covered by the completion verification run.
|
|
102
|
+
Documentation relative links and `git diff --check` also passed.
|