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.
- package/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +14 -0
- package/.cursor-plugin/marketplace.json +16 -0
- package/.github/copilot-instructions.md +7 -0
- package/.github/workflows/ci.yml +21 -0
- package/AGENTS.md +21 -10
- package/README.md +69 -4
- 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/README.md +9 -3
- package/agent-plugin/assets/mobile-debug-tools.svg +28 -0
- package/agent-plugin/mcp.json +1 -1
- package/agent-plugin/plugin.json +1 -1
- package/agent-plugin/skills/mobile-debug-mcp/SKILL.md +113 -1
- package/dist/debugger/android.js +78 -0
- package/dist/debugger/guard.js +30 -0
- package/dist/debugger/sessions.js +262 -0
- package/dist/debugger/worker.js +165 -0
- package/dist/interact/android.js +10 -4
- package/dist/interact/conditional-action.js +68 -0
- package/dist/interact/index.js +70 -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 +25 -4
- package/dist/observe/evidence.js +280 -0
- package/dist/observe/index.js +33 -6
- package/dist/observe/ios.js +25 -6
- package/dist/observe/readiness-service.js +124 -0
- package/dist/observe/readiness.js +88 -0
- package/dist/observe/recording.js +165 -0
- package/dist/observe/snapshot-metadata.js +10 -2
- package/dist/observe/system-trace-android.js +135 -0
- package/dist/observe/system-trace.js +285 -0
- package/dist/server/common.js +9 -0
- package/dist/server/condition-schema.js +32 -0
- package/dist/server/debugger-handlers.js +38 -0
- package/dist/server/debugger-schema.js +42 -0
- package/dist/server/journey-schema.js +4 -2
- package/dist/server/readiness-schema.js +12 -0
- package/dist/server/system-trace-schema.js +17 -0
- package/dist/server/tool-definitions.js +51 -0
- package/dist/server/tool-handlers.js +294 -105
- package/dist/server-core.js +3 -3
- package/dist/server.js +34 -0
- package/dist/system/index.js +5 -4
- package/dist/utils/exec.js +2 -0
- package/dist/utils/ios/utils.js +4 -4
- package/dist/utils/java.js +14 -12
- package/dist/utils/operation-budget.js +26 -2
- package/dist/utils/resolve-device.js +9 -0
- package/docs/CHANGELOG.md +15 -0
- 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/023-android-system-traces/device-evidence.json +38 -0
- package/docs/specs/023-android-system-traces/emulator-recheck.json +38 -0
- package/docs/specs/023-android-system-traces/intent.md +40 -0
- package/docs/specs/023-android-system-traces/plan.md +117 -0
- package/docs/specs/023-android-system-traces/spec.md +120 -0
- package/docs/specs/024-android-heap-dump-capture/intent.md +39 -0
- package/docs/specs/025-android-runtime-debugger/intent.md +40 -0
- package/docs/specs/025-android-runtime-debugger/plan.md +104 -0
- package/docs/specs/025-android-runtime-debugger/spec.md +182 -0
- package/docs/specs/process.md +213 -0
- package/docs/tools/TOOLS.md +5 -0
- package/docs/tools/debugger.md +104 -0
- package/docs/tools/evidence.md +82 -0
- package/docs/tools/interact.md +125 -2
- package/docs/tools/observe.md +70 -0
- package/docs/tools/system-traces.md +126 -0
- package/package.json +1 -1
- package/scripts/agent-plugin.mjs +68 -8
- package/src/debugger/android.ts +65 -0
- package/src/debugger/guard.ts +26 -0
- package/src/debugger/sessions.ts +168 -0
- package/src/debugger/worker.ts +122 -0
- package/src/interact/android.ts +10 -4
- package/src/interact/conditional-action.ts +75 -0
- package/src/interact/index.ts +57 -28
- 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 +33 -6
- package/src/observe/evidence.ts +262 -0
- package/src/observe/index.ts +32 -8
- package/src/observe/ios.ts +32 -8
- package/src/observe/readiness-service.ts +94 -0
- package/src/observe/readiness.ts +84 -0
- package/src/observe/snapshot-metadata.ts +12 -1
- package/src/observe/system-trace-android.ts +133 -0
- package/src/observe/system-trace.ts +218 -0
- package/src/server/common.ts +7 -0
- package/src/server/condition-schema.ts +32 -0
- package/src/server/debugger-handlers.ts +29 -0
- package/src/server/debugger-schema.ts +37 -0
- package/src/server/journey-schema.ts +4 -2
- package/src/server/readiness-schema.ts +12 -0
- package/src/server/system-trace-schema.ts +17 -0
- package/src/server/tool-definitions.ts +49 -0
- package/src/server/tool-handlers.ts +182 -21
- package/src/server-core.ts +3 -3
- package/src/server.ts +24 -0
- package/src/system/index.ts +7 -5
- package/src/types.ts +30 -1
- package/src/utils/exec.ts +2 -0
- package/src/utils/ios/utils.ts +4 -4
- package/src/utils/java.ts +13 -12
- package/src/utils/operation-budget.ts +24 -2
- package/src/utils/resolve-device.ts +10 -0
- package/test/device/automated/debugger/runtime.android.test.ts +203 -0
- package/test/device/automated/observe/correlated-evidence.smoke.ts +70 -0
- package/test/device/automated/observe/system-trace.smoke.ts +104 -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/debugger-app/MainActivity.java +46 -0
- package/test/fixtures/debugger-app/NoLocals.java +7 -0
- package/test/fixtures/debugger-app/README.md +13 -0
- package/test/fixtures/debugger-app/build.ts +29 -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/helpers/perfetto.ts +59 -0
- package/test/unit/debugger/guards.test.ts +46 -0
- package/test/unit/debugger/native.test.ts +33 -0
- package/test/unit/debugger/sessions.test.ts +83 -0
- package/test/unit/observe/android_tree_acquisition.test.ts +2 -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_tree_acquisition.test.ts +2 -0
- package/test/unit/observe/readiness.test.ts +41 -0
- package/test/unit/observe/readiness_native.test.ts +68 -0
- package/test/unit/observe/system-trace-android.test.ts +52 -0
- package/test/unit/observe/system-trace.test.ts +166 -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/system-trace.test.ts +64 -0
- package/test/unit/server/tap_selector.test.ts +2 -0
- package/worker/DebuggerWorker.java +160 -0
- package/skills/mobile-debug-tools-usage/SKILL.md +0 -96
- /package/{skills → .agents/skills}/README.md +0 -0
- /package/{skills → .agents/skills}/mcp-builder/SKILL.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/SKILL.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/SKILL.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,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.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Spec 021: app readiness signals
|
|
2
|
+
|
|
3
|
+
Date: 2026-10-29. Status: approved — implementation authorised.
|
|
4
|
+
Source: [issue #65](https://github.com/clivejefferies/mobile-debug-tools/issues/65).
|
|
5
|
+
[Intent](intent.md) approved by the originator’s “yes” in this conversation.
|
|
6
|
+
|
|
7
|
+
## Behaviour and scope
|
|
8
|
+
|
|
9
|
+
Report readiness of the observed foreground device UI on Android/iOS. This is
|
|
10
|
+
not proof of a particular app’s identity, backend completion, or safe input.
|
|
11
|
+
Reuse snapshot signatures, loading observations, device resolution, and budgets.
|
|
12
|
+
Do not infer readiness from screenshots or input-dispatch success.
|
|
13
|
+
|
|
14
|
+
Initial supported states are `loading`, `transitioning`, `settled`, and `unknown`.
|
|
15
|
+
Reserve `unresponsive` and `debugger_paused`, but report their capabilities as
|
|
16
|
+
unsupported on both platforms: there is currently no authoritative process-health
|
|
17
|
+
or debugger-session provider. A timeout must never manufacture either state.
|
|
18
|
+
Adding those providers requires a subsequent spec decision; this feature does
|
|
19
|
+
not implement a debugger, recovery, restart, or performance profiling.
|
|
20
|
+
|
|
21
|
+
## Public contracts
|
|
22
|
+
|
|
23
|
+
Add tools `get_app_readiness` and `wait_for_app_ready`, requiring `platform`
|
|
24
|
+
(`android`/`ios`) and optionally accepting `deviceId`. Resolve and pin one concrete
|
|
25
|
+
device per call. Both accept strict integer controls:
|
|
26
|
+
|
|
27
|
+
| Control | Default | Range |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `timeoutMs` | get: 15000; wait: 30000 | 100–60000 |
|
|
30
|
+
| `stabilityMs` | 500 | 100–10000 |
|
|
31
|
+
| `pollIntervalMs` (wait only) | 500 | 100–1000 |
|
|
32
|
+
|
|
33
|
+
Reject invalid/unknown arguments as `INVALID_ARGUMENT` before device work.
|
|
34
|
+
`get_app_readiness` takes at most two fresh samples, separating their acquisitions
|
|
35
|
+
by at least `stabilityMs`. A loading first sample can return immediately. The
|
|
36
|
+
wait tool samples until settled or its deadline. Reads are sequential; never
|
|
37
|
+
start a background poller or overlap hierarchy dumps within a call.
|
|
38
|
+
|
|
39
|
+
Both return `{ success, device, readiness, elapsed_ms, failure_code? }`.
|
|
40
|
+
For get, success means a supported state was established; `unknown` is unsuccessful.
|
|
41
|
+
For wait, only `settled` is successful. Unsuccessful codes are
|
|
42
|
+
`READINESS_UNAVAILABLE` (get), `READINESS_TIMEOUT` (wait deadline), and
|
|
43
|
+
`DEVICE_UNAVAILABLE` (resolution/disconnection prevents continuation).
|
|
44
|
+
|
|
45
|
+
`readiness` contains:
|
|
46
|
+
|
|
47
|
+
- `state`, `scope: device_ui`, and `basis` (`observed`, `heuristic`, or `insufficient`).
|
|
48
|
+
- `captured_at_ms` (latest successful sample, or null), `sample_count`,
|
|
49
|
+
`stable_for_ms`, and nullable `snapshot_session_id`/`snapshot_revision`.
|
|
50
|
+
- `evidence`: bounded records of signal code, source, capture time, and snapshot
|
|
51
|
+
revision; maximum eight, newest retained. No full text, logs, screenshots, or trees.
|
|
52
|
+
- `limitations`: applicable codes from `INSUFFICIENT_SAMPLES`, `OBSERVATION_FAILED`,
|
|
53
|
+
`OBSERVATION_INVALID`, `DEADLINE_EXCEEDED`, `NORMALIZED_UI_ONLY`,
|
|
54
|
+
`FOREGROUND_APP_UNVERIFIED`, `UNRESPONSIVE_SIGNAL_UNSUPPORTED`, and
|
|
55
|
+
`DEBUGGER_SIGNAL_UNSUPPORTED`.
|
|
56
|
+
- `capabilities`: true for loading/transition/stability observation; false for
|
|
57
|
+
unresponsive/debugger-paused detection in this version.
|
|
58
|
+
|
|
59
|
+
Add the same `readiness` summary to debug/compact `get_ui_tree` and nested tree
|
|
60
|
+
observations without additional reads. Single-sample summaries can only report
|
|
61
|
+
loading or unknown; separate calls never silently share a stability window.
|
|
62
|
+
Preserve existing fields, compact error evidence, and delta behaviour.
|
|
63
|
+
|
|
64
|
+
Add opt-in `requireReady: boolean` (default false) to `wait_for_ui` and
|
|
65
|
+
`wait_for_ui_change`. When enabled, success requires the existing predicate and
|
|
66
|
+
settled readiness on the same current samples. Reuse those waits’ samples and
|
|
67
|
+
single existing deadline; do not add another wait budget or unrelated tree reads.
|
|
68
|
+
Return `readiness` alongside the existing envelope. On expiry retain the existing
|
|
69
|
+
timeout/failure shape and include readiness limitations. Default calls retain
|
|
70
|
+
existing predicate semantics; no new journey assertion type is introduced.
|
|
71
|
+
|
|
72
|
+
## Classification rules
|
|
73
|
+
|
|
74
|
+
| Evidence | State / basis |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| Visible native `android.widget.ProgressBar`, `ActivityIndicator`, or `ProgressIndicator` | loading / observed |
|
|
77
|
+
| Visible label exactly “Loading” or “Please wait”, ignoring case, whitespace, and trailing full stops/ellipsis | loading / heuristic |
|
|
78
|
+
| Two valid consecutive signatures differ | transitioning / observed |
|
|
79
|
+
| At least two valid identical signatures spanning `stabilityMs`, without loading evidence | settled / observed |
|
|
80
|
+
| Missing, failed, invalid, or insufficient evidence | unknown / insufficient |
|
|
81
|
+
|
|
82
|
+
Loading takes precedence over stability. Do not reuse the broad existing
|
|
83
|
+
`loading_state` keyword heuristic as proof: a button named “Progress” is not an
|
|
84
|
+
active loader. Keep that legacy field unchanged for compatibility.
|
|
85
|
+
|
|
86
|
+
Use full normalized snapshot signatures, including state and bounds, not the
|
|
87
|
+
coarser screen fingerprint or truncated delta. A change resets stability;
|
|
88
|
+
failed reads break consecutive-sample continuity. Validate device/session,
|
|
89
|
+
revision, a finite capture timestamp from this acquisition, nonempty elements,
|
|
90
|
+
and positive finite resolution. Never accept retained metadata as a fresh read.
|
|
91
|
+
Track elapsed/stability intervals with a monotonic clock; expose wall-clock
|
|
92
|
+
capture timestamps only as evidence. Results describe their capture window,
|
|
93
|
+
not a guarantee that the UI stays settled afterward.
|
|
94
|
+
|
|
95
|
+
## Failure, lifecycle, and privacy
|
|
96
|
+
|
|
97
|
+
Bound acquisition, retries, and delays by the remaining operation budget and any
|
|
98
|
+
outer deadline. No late sample may produce success. Terminate/await owned timed-out
|
|
99
|
+
subprocesses; perform no follow-up snapshot after expiry. Waits can retry transient
|
|
100
|
+
observation failures within budget, with continuity reset; disconnect is terminal.
|
|
101
|
+
Retain the last supported state on wait timeout only as timestamped evidence,
|
|
102
|
+
with `DEADLINE_EXCEEDED`; if the latest read failed, current state is unknown.
|
|
103
|
+
There is no cancellation argument. Existing transport cancellation does not
|
|
104
|
+
currently abort platform reads; those reads remain bounded by their deadlines.
|
|
105
|
+
Do not claim immediate cancellation support or create persistent sessions.
|
|
106
|
+
Do not reset network windows, dispatch input, or retain cross-request histories.
|
|
107
|
+
Do not log UI payloads or input text. No permissions, dependencies, migrations,
|
|
108
|
+
or automatic package release are required.
|
|
109
|
+
|
|
110
|
+
## Acceptance and verification
|
|
111
|
+
|
|
112
|
+
- Loading outranks identical trees; changing trees transition; consecutive stable
|
|
113
|
+
valid trees settle. Text-only loading is explicitly heuristic.
|
|
114
|
+
- A frozen-looking screenshot, stable tree, slow dump, timeout, or missing loader
|
|
115
|
+
cannot establish responsiveness, debugger pause, or app/backend completion.
|
|
116
|
+
- Error/empty/stale-session/wrong-device samples never settle; failures reset
|
|
117
|
+
continuity. Deadlines prevent late success and background work.
|
|
118
|
+
- New summaries preserve compact/debug evidence and existing defaults; opt-in
|
|
119
|
+
waits require both their predicate and readiness.
|
|
120
|
+
- Every state has evidence and limitations; unsupported capabilities are explicit.
|
|
121
|
+
|
|
122
|
+
Use deterministic native-adapter/clock fixtures for transitions, failures,
|
|
123
|
+
timeouts, isolation, and schema parity. Run `npm run verify`, `npm run test:device`,
|
|
124
|
+
and updated-server Android/iOS journeys for stable content, a controlled loader
|
|
125
|
+
transition, bounded waiting, and opt-in predicate waits. Record simulator/build,
|
|
126
|
+
results, and unsupported coverage in the plan after approval.
|
|
127
|
+
|
|
128
|
+
## Investigation and approval
|
|
129
|
+
|
|
130
|
+
Existing owners: `src/observe/snapshot-metadata.ts`, `src/observe/index.ts`,
|
|
131
|
+
`src/interact/index.ts`, server schemas/handlers, shared types and operation budgets.
|
|
132
|
+
The legacy idle wait can compare null fingerprints; the new classifier must not
|
|
133
|
+
copy that inference. No debugger/readiness module currently exists.
|
|
134
|
+
|
|
135
|
+
Read-only reconnaissance used the connected server (exact build not reported;
|
|
136
|
+
local checkout 0.32.4). Android 16 `emulator-5554`: two fresh 15-element trees,
|
|
137
|
+
same revision 9, no loading hint, reads about 4.4/3.0 seconds. iOS 26.4 iPhone 17
|
|
138
|
+
Pro `F9243BAF-62A8-4990-8A99-17F527F56FDA`: two fresh 14-element trees, revision
|
|
139
|
+
10, no loading hint, reads about 3.4/2.0 seconds. These establish available
|
|
140
|
+
observation routes, not feature passes; no inputs or settings were changed.
|
|
141
|
+
|
|
142
|
+
Approved by the originator’s “yep” on 2026-10-29 in direct response to this
|
|
143
|
+
spec and first-version scope, including unsupported unresponsive/debugger-paused
|
|
144
|
+
detection and opt-in existing-wait integration.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"verified_on": "2026-10-30",
|
|
3
|
+
"source_base": "3701f3987940a8abc76ed893b575ed370e3fb673",
|
|
4
|
+
"source_state": "working tree with correlated evidence changes",
|
|
5
|
+
"node_version": "23.11.0",
|
|
6
|
+
"build_sha256": {
|
|
7
|
+
"dist/server/tool-handlers.js": "4ae4aef2467518216e3b987112aadb91e5ff560679782fe08aefdb389209cc50",
|
|
8
|
+
"dist/observe/evidence.js": "bd7c6ac046ad4adde08117dcb66f68682e0036a83308c0c84f6d5ce141f8a386"
|
|
9
|
+
},
|
|
10
|
+
"journeys": [
|
|
11
|
+
{
|
|
12
|
+
"status": "PASS",
|
|
13
|
+
"server": {
|
|
14
|
+
"name": "mobile-debug-mcp",
|
|
15
|
+
"version": "0.32.4"
|
|
16
|
+
},
|
|
17
|
+
"platform": "android",
|
|
18
|
+
"device": "emulator-5554",
|
|
19
|
+
"osVersion": "16",
|
|
20
|
+
"startingApp": "com.android.settings",
|
|
21
|
+
"actionId": "5fdfe5a0-cfd3-4182-a12b-c440d855cec5",
|
|
22
|
+
"phases": {
|
|
23
|
+
"dispatchStarted": 1793350325789,
|
|
24
|
+
"dispatchCompleted": 1793350326584,
|
|
25
|
+
"screenshotStarted": 1793350327002,
|
|
26
|
+
"screenshotCompleted": 1793350327334
|
|
27
|
+
},
|
|
28
|
+
"screenshot": "captured",
|
|
29
|
+
"logs": "captured",
|
|
30
|
+
"uiTree": "captured",
|
|
31
|
+
"disabledComponents": "omitted",
|
|
32
|
+
"foreignReference": "unavailable",
|
|
33
|
+
"cleanup": "No settings, app data or streams changed; Settings remains foreground. No content artifacts saved."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"status": "PASS",
|
|
37
|
+
"server": {
|
|
38
|
+
"name": "mobile-debug-mcp",
|
|
39
|
+
"version": "0.32.4"
|
|
40
|
+
},
|
|
41
|
+
"platform": "ios",
|
|
42
|
+
"device": "F9243BAF-62A8-4990-8A99-17F527F56FDA",
|
|
43
|
+
"osVersion": "iOS 26.4",
|
|
44
|
+
"startingApp": "com.apple.Preferences",
|
|
45
|
+
"actionId": "99a4cdc4-8be1-4194-acab-3d08e7bd4a41",
|
|
46
|
+
"phases": {
|
|
47
|
+
"dispatchStarted": 1793350335131,
|
|
48
|
+
"dispatchCompleted": 1793350335486,
|
|
49
|
+
"screenshotStarted": 1793350335806,
|
|
50
|
+
"screenshotCompleted": 1793350336212
|
|
51
|
+
},
|
|
52
|
+
"screenshot": "captured",
|
|
53
|
+
"logs": "captured",
|
|
54
|
+
"uiTree": "captured",
|
|
55
|
+
"disabledComponents": "omitted",
|
|
56
|
+
"foreignReference": "unavailable",
|
|
57
|
+
"cleanup": "No settings, app data or streams changed; Settings remains foreground. No content artifacts saved."
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|