adb-ready 0.5.1 → 0.7.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.
@@ -26,7 +26,7 @@ it. ADB Ready does not open an MCP network listener.
26
26
 
27
27
  ## Connect an agent
28
28
 
29
- Preview the exact project change first, then apply it:
29
+ Preview the exact project changes first, then apply them:
30
30
 
31
31
  ```bash
32
32
  adb-ready agent setup codex --dry-run
@@ -35,7 +35,28 @@ adb-ready agent setup codex
35
35
 
36
36
  Replace `codex` with `claude-code`, `cursor`, or `vscode`. Existing unrelated
37
37
  configuration is preserved. An existing `adb-ready` entry with different
38
- settings is reported as a conflict and is never replaced automatically.
38
+ settings is reported as a conflict and is never replaced automatically. The
39
+ same setup installs ADB Ready's version-matched Agent Skill at
40
+ `.agents/skills/adb-ready/SKILL.md`, or `.claude/skills/adb-ready/SKILL.md` for
41
+ Claude Code. A custom skill is never overwritten; generated older versions are
42
+ updated atomically with the MCP configuration.
43
+
44
+ Narrow tool discovery when an agent needs only one workflow:
45
+
46
+ ```bash
47
+ adb-ready agent setup codex --mcp-profile debug
48
+ ```
49
+
50
+ | Profile | Intended work |
51
+ | --- | --- |
52
+ | `full` | Complete backward-compatible tool surface; the default |
53
+ | `session` | Target readiness, app lifecycle, and durable development sessions |
54
+ | `ui` | Target readiness, app lifecycle, semantic UI automation, and screenshots |
55
+ | `debug` | Target readiness, app lifecycle, current/saved evidence, and failure diagnosis |
56
+
57
+ Profiles reduce accidental tool selection and client context size; they are not
58
+ an authorization boundary. Start a server directly with `adb-ready mcp
59
+ --mcp-profile ui` when configuration is managed outside `agent setup`.
39
60
 
40
61
  ### Codex
41
62
 
@@ -49,15 +70,16 @@ default_tools_approval_mode = "writes"
49
70
  ```
50
71
 
51
72
  Codex CLI, the Codex IDE extension, and the ChatGPT desktop app share Codex MCP
52
- configuration. See the [official Codex MCP guide](https://learn.chatgpt.com/docs/extend/mcp).
73
+ configuration. Codex loads project `.codex/config.toml` only after the project
74
+ is trusted; ADB Ready never changes trust on the user's behalf. See the
75
+ [official Codex configuration guide](https://developers.openai.com/codex/config-basic).
53
76
 
54
77
  ### Claude Code
55
78
 
56
- Run this from the project root:
79
+ Run the project setup from the project root:
57
80
 
58
81
  ```bash
59
- claude mcp add --scope project adb-ready -- node ./node_modules/adb-ready/dist/cli.js mcp
60
- claude mcp get adb-ready
82
+ adb-ready agent setup claude-code
61
83
  ```
62
84
 
63
85
  Claude Code stores project-scoped servers in `.mcp.json` and asks users to
@@ -126,16 +148,17 @@ starts in the Android project root.
126
148
 
127
149
  Ask the agent to follow this sequence:
128
150
 
129
- 1. Call `doctor` when host or ADB health is unknown.
130
- 2. Call `ensure_ready`; provide an exact device serial, configured alias, or
151
+ 1. Call `get_capabilities` and stay within the active profile.
152
+ 2. Call `doctor` when host or ADB health is unknown.
153
+ 3. Call `ensure_ready`; provide an exact device serial, configured alias, or
131
154
  transport ID when more than one ready target exists.
132
- 3. Start the configured stack with `start_dev_session` when it is not already
155
+ 4. Start the configured stack with `start_dev_session` when it is not already
133
156
  running; retain its task handle and poll `get_dev_session` without holding a
134
157
  tool call open.
135
- 4. Resolve the project app with `resolve_app`.
136
- 5. Use `inspect_app` or `inspect_ui` for bounded current evidence.
137
- 6. Perform one typed action, then inspect again instead of assuming success.
138
- 7. Use `get_session_problems` or `compile_debug_context` for an existing
158
+ 5. Resolve the project app with `resolve_app`.
159
+ 6. Use `inspect_app` or `inspect_ui` for bounded current evidence.
160
+ 7. Perform one typed action, then inspect again instead of assuming success.
161
+ 8. Use `get_session_problems` or `compile_debug_context` for an existing
139
162
  development session.
140
163
 
141
164
  The first successful `ensure_ready` binds one target to that MCP connection and
@@ -156,17 +179,18 @@ UI Automation service per target.
156
179
 
157
180
  | Capability | MCP tools |
158
181
  | --- | --- |
159
- | Host and target readiness | `doctor`, `list_targets`, `ensure_ready` |
182
+ | Discovery and readiness | `get_capabilities`, `doctor`, `list_targets`, `ensure_ready` |
160
183
  | Durable development lifecycle | `start_dev_session`, `get_dev_session`, `stop_dev_session` |
161
184
  | App identity and lifecycle | `resolve_app`, `install_app`, `launch_app`, `restart_app`, `open_url` |
162
- | Current evidence | `inspect_app`, `inspect_ui`, `capture_screenshot` |
163
- | Safe UI queries and actions | `audit_ui`, `get_ui`, `find_ui`, `assert_ui`, `compare_ui`, `tap_ui`, `long_press_ui`, `scroll_ui`, `swipe_ui`, `fill_ui`, `clear_ui`, `type_text_ui`, `press_key_ui`, `wait_for_ui` |
185
+ | Current evidence | `inspect_app`, `inspect_failures`, `inspect_ui`, `capture_screenshot` |
186
+ | Safe UI queries and actions | `audit_ui`, `get_ui`, `find_ui`, `assert_ui`, `compare_ui`, `tap_ui`, `long_press_ui`, `scroll_ui`, `swipe_ui`, `fill_ui`, `clear_ui`, `type_text_ui`, `press_key_ui`, `wait_for_ui`, `inspect_keyboard`, `dismiss_keyboard`, `inspect_permission_dialog`, `respond_to_permission_dialog` |
164
187
  | Saved diagnostics | `list_sessions`, `get_session_problems`, `compile_debug_context` |
165
188
 
166
189
  MCP resources keep larger read-only context outside tool calls:
167
190
 
168
191
  | Resource | Content |
169
192
  | --- | --- |
193
+ | `adb-ready://capabilities` | active profile, profile tool membership, and long-running task compatibility |
170
194
  | `adb-ready://targets` | current target inventory and this connection's bound target |
171
195
  | `adb-ready://sessions` | bounded saved-session manifests |
172
196
  | `adb-ready://sessions/{sessionId}` | one session manifest |
@@ -176,13 +200,33 @@ MCP resources keep larger read-only context outside tool calls:
176
200
  Every tool advertises an output schema and returns the same versioned result
177
201
  envelope used by CLI JSON output. Screenshot capture additionally returns MCP
178
202
  `image` content so a vision-capable agent can inspect the pixels directly; the
179
- verified project-local PNG remains the evidence source of record.
203
+ verified project-local PNG remains the evidence source of record. Agent
204
+ screenshots default to a 1024×1024 maximum and 4 MiB base64 content budget, with
205
+ explicit crop/dimension controls and a deliberate `fullResolution` opt-in.
206
+ Standard MCP annotations describe read-only, destructive, idempotent, and
207
+ open-world behavior. The namespaced `dev.adbready/tool` metadata adds profile,
208
+ category, target-binding, mutation, and sensitive-data signals. Annotations are
209
+ advisory for clients; ADB Ready still enforces the operation at runtime.
180
210
 
181
211
  The npm package also ships `schema/agent-tools-v1.json`, generated from the
182
212
  server's real `tools/list` response during every build. Integrations can inspect
183
213
  version-matched input and output schemas plus safety annotations without
184
214
  starting ADB.
185
215
 
216
+ The package also ships `skills/adb-ready/SKILL.md`. It is generated from the
217
+ same real `tools/list` contract during the build, so its workflow cannot name a
218
+ tool missing from that package version. The skill teaches task order and safety;
219
+ the MCP server remains the controlled execution surface.
220
+
221
+ ## Long-running task compatibility
222
+
223
+ ADB Ready does not advertise the MCP Tasks extension yet. Its durable
224
+ `start_dev_session`, `get_dev_session`, and `stop_dev_session` handles remain
225
+ available across supported clients and reconnects. `get_capabilities` reports
226
+ this compatibility path explicitly. Native Tasks will be advertised only after
227
+ capability negotiation and end-to-end interoperability are proven in at least
228
+ two supported clients; unnegotiated clients never receive a Tasks claim.
229
+
186
230
  `list_sessions` is project-scoped by default and supports status, preset,
187
231
  recency, and result-count filters. When more matches remain, pass its opaque
188
232
  `nextCursor` back as `cursor`; an expired cursor fails explicitly instead of
@@ -197,6 +241,9 @@ silently restarting the list.
197
241
  - UI hierarchy and app inspection are marked sensitive and remain bounded.
198
242
  - UI references are checked against a fresh hierarchy digest before mutation;
199
243
  stale references are rejected.
244
+ - Secret UI text is referenced by local environment-variable name rather than
245
+ sent in MCP arguments; Unicode input is capability-checked before mutation
246
+ and restores the prior Android IME.
200
247
  - Data clearing and uninstall are intentionally absent from the agent surface.
201
248
  - Tool annotations help clients request approval, but ADB Ready enforces its
202
249
  own target, path, and destructive-action rules.
@@ -123,6 +123,7 @@ Capture a PNG directly from the selected target:
123
123
  ```bash
124
124
  adb-ready capture screenshot
125
125
  adb-ready capture screenshot --out artifacts/login.png
126
+ adb-ready capture screenshot --crop 120,300,840,900 --max-width 640
126
127
  ```
127
128
 
128
129
  Capture a bounded MP4 recording:
@@ -146,7 +147,10 @@ instead of publishing unusable evidence; create visible activity and retry.
146
147
 
147
148
  Some multi-display Android builds, including foldables, emit a short textual
148
149
  warning before the screenshot bytes. ADB Ready removes only a bounded text
149
- preamble and still requires a valid PNG signature before publishing the file.
150
+ preamble and still decodes the complete PNG, verifies its chunks and checksums,
151
+ and validates its dimensions before publishing the file. `--crop` uses source
152
+ pixels in `X,Y,WIDTH,HEIGHT` order. `--max-width` and `--max-height` preserve
153
+ aspect ratio and never upscale.
150
154
 
151
155
  The result contains:
152
156
 
@@ -154,12 +158,21 @@ The result contains:
154
158
  - media type and byte size;
155
159
  - measured duration and video frame count for recordings;
156
160
  - SHA-256 digest;
161
+ - capture timestamp, source/output dimensions, effective crop, PNG encoding,
162
+ and whether crop or resize intentionally truncated the original frame;
157
163
  - selected target and capture-command provenance.
158
164
 
159
165
  Binary data is stored as a file rather than embedded into JSON or AI context.
160
166
  Screenshots and recordings can contain private information; ADB Ready does not
161
167
  upload or implicitly attach them to diagnostic context.
162
168
 
169
+ MCP screenshot results are different by design: `capture_screenshot` returns
170
+ image content to the requesting client, so it defaults to a proportional
171
+ 1024×1024 maximum and a 4 MiB base64 content budget. Agents may provide a
172
+ smaller `crop`, `maxWidth`, or `maxHeight`. `fullResolution: true` is an
173
+ explicit escape hatch for clients that can safely accept the complete image;
174
+ it cannot be combined with dimension limits.
175
+
163
176
  ## Target selection and automation
164
177
 
165
178
  All commands accept the standard target selectors:
@@ -158,7 +158,7 @@ leaving a development server open:
158
158
 
159
159
  ```bash
160
160
  adb-ready run --preset expo --run-timeout 10m -- \
161
- maestro '--device={target.serial}' test .maestro/smoke.yaml
161
+ maestro test .maestro/smoke.yaml
162
162
  ```
163
163
 
164
164
  ADB Ready selects and exclusively leases one target, prepares the configured
@@ -168,9 +168,23 @@ retries a failed product assertion as if it were an infrastructure failure.
168
168
 
169
169
  The literal `{target.serial}` inside a verification argument is replaced only
170
170
  after ADB Ready selects and leases the target. The child also receives the
171
- same value as `ANDROID_SERIAL` and `ADB_READY_TARGET_SERIAL`. This keeps tools
172
- such as Maestro pinned explicitly without invoking a shell; tools that already
173
- honor `ANDROID_SERIAL`, including common Gradle/ADB workflows, need no placeholder.
171
+ same value as `ANDROID_SERIAL` and `ADB_READY_TARGET_SERIAL`, plus a private
172
+ `ADB_READY_VERIFIER_OUTPUT_DIR` for native artifacts. Tools that already honor
173
+ `ANDROID_SERIAL`, including common Gradle/ADB workflows, need no placeholder.
174
+
175
+ For a direct `maestro test` command, ADB Ready uses Maestro's supported global
176
+ `--device` option to bind the leased serial. Unless the command already names
177
+ them, it also requests Maestro JUnit, test-output, and debug-output files. An
178
+ explicit Maestro device that differs from the leased target fails before
179
+ Maestro starts; ADB Ready never silently retargets it.
180
+
181
+ Android CLI does not expose a single `journey run` command or a stable Journey
182
+ report format. Journeys are evaluated by an agent using the installed Android
183
+ CLI skill. ADB Ready therefore does not invent such a command. It keeps the
184
+ agent workflow target-bound through MCP, and when a finite verifier invokes
185
+ the official `android layout` or `android screen capture` primitives directly,
186
+ ADB Ready supplies their supported `--device` and `--output` options. The
187
+ Journey XML remains owned by Android CLI and the agent.
174
188
 
175
189
  Every executed run prints the path to a project-local evidence directory under
176
190
  `.adb-ready/artifacts/`. Its manifest references the structured result,
@@ -184,6 +198,20 @@ Preview the complete project plan before a device is allocated:
184
198
  adb-ready run --preset expo --dry-run --json -- npm run test:e2e
185
199
  ```
186
200
 
201
+ ### Fan out over an explicit pool
202
+
203
+ Named project pools repeat the same bounded verifier without weakening the
204
+ single-target run contract:
205
+
206
+ ```bash
207
+ adb-ready run --pool smoke --max-concurrency 2 -- npm run test:e2e
208
+ ```
209
+
210
+ Each member keeps its own target lease, session, result envelope, evidence, and
211
+ cleanup. Required failures fail the aggregate; optional failures remain
212
+ visible. Queueing is bounded and host-local, never an inferred distributed
213
+ lock. See [Target pools and fan-out](./target-pools.md).
214
+
187
215
  ### Start an existing AVD and deploy the intended build
188
216
 
189
217
  For a complete local or CI-owned emulator job, name one existing AVD and either
@@ -196,7 +224,7 @@ adb-ready run \
196
224
  --artifact android/app/build/outputs/apk/debug/app-debug.apk \
197
225
  --package com.example.app \
198
226
  --run-timeout 10m \
199
- -- maestro '--device={target.serial}' test .maestro/smoke.yaml
227
+ -- maestro test .maestro/smoke.yaml
200
228
 
201
229
  adb-ready run --avd Pixel_9_API_36 --deploy --variant debug -- \
202
230
  ./gradlew connectedDebugAndroidTest
@@ -225,8 +253,18 @@ line-oriented `artifact_*`, `deployment_*`, and `emulator_*` fields; `--json`
225
253
  and `--ndjson` retain the complete structured automation data.
226
254
 
227
255
  The evidence manifest also includes the verifier's bounded, redacted native
228
- stdout and stderr. Preparation failures still publish the same result,
229
- problems, JUnit, and evidence contract after owned-resource cleanup.
256
+ stdout and stderr. Supported native JUnit, screenshots, videos, logs, JSON,
257
+ and HTML reports are copied under `verifier-native/`, checksummed, marked
258
+ sensitive, and bounded to 100 files, 20 MiB per file, and 50 MiB total.
259
+ Symlinks and unsupported file types are never followed. Text artifacts are
260
+ redacted; binary screenshots and video remain local sensitive evidence.
261
+ Preparation failures still publish the same result, problems, JUnit, and
262
+ evidence contract after owned-resource cleanup.
263
+
264
+ Structured results classify the verifier independently as `passed`,
265
+ `assertion-failed`, `tool-failed`, `target-failed`, `timed-out`, `cancelled`,
266
+ or `unavailable`. This classification uses process and session state, not
267
+ fragile parsing of human log messages.
230
268
 
231
269
  ## CI example
232
270
 
@@ -241,3 +279,78 @@ problems, JUnit, and evidence contract after owned-resource cleanup.
241
279
  USB passthrough, emulators, remote ADB servers, and device labs remain the CI
242
280
  environment's responsibility. ADB Ready reports the observed boundary instead
243
281
  of simulating a connected device.
282
+
283
+ ### GitHub-hosted emulator
284
+
285
+ The repository continuously runs a complete finite ADB Ready job on a real
286
+ headless Linux emulator. The runner layer creates and owns the AVD; ADB Ready
287
+ binds to its exact serial, verifies boot and unlock state, executes the bounded
288
+ check, writes normalized NDJSON and evidence, and leaves emulator teardown to
289
+ the owning layer.
290
+
291
+ Use the maintained, commit-pinned workflow and fixture as the starting point:
292
+
293
+ - [Android emulator workflow](../.github/workflows/android-emulator.yml)
294
+ - [CI emulator fixture](../examples/ci-emulator/)
295
+
296
+ The evidence upload uses `if: always()` so a product assertion or verifier
297
+ failure retains the same manifest, result, JUnit, native files, and GitHub step
298
+ summary as a successful run. A failure before ADB Ready starts cannot produce
299
+ an ADB Ready bundle and is reported by the provisioning step instead.
300
+
301
+ ### Self-hosted physical target
302
+
303
+ Use the maintained [physical-device recipe](../examples/ci-physical/) when a
304
+ dedicated self-hosted runner owns one pre-authorized Android phone or tablet.
305
+ The workflow is manual-only, routes through an explicit `android-device`
306
+ runner label, queues competing GitHub jobs, and still relies on ADB Ready's
307
+ host-local target lease as the final ownership boundary.
308
+
309
+ The configured serial must match one `adb devices -l` row exactly. Missing,
310
+ `unauthorized`, `offline`, replaced, and concurrently owned targets fail before
311
+ the verifier can mutate another device. ADB Ready does not restart the shared
312
+ ADB server, approve Android's RSA dialog, or claim a distributed lock across
313
+ runner machines.
314
+
315
+ Do not expose a physical self-hosted runner to untrusted pull requests. Keep
316
+ the runner and its custom label scoped to trusted repositories or runner
317
+ groups, and keep the phone unlocked only when the project's assertions require
318
+ it. The example verifier is read-only and intentionally captures no screenshot
319
+ from a personal device.
320
+
321
+ ### Gradle-owned virtual devices
322
+
323
+ When the Android build declares Gradle Managed Devices, keep lifecycle
324
+ ownership with Gradle instead of treating its transient emulator as an ADB
325
+ Ready target:
326
+
327
+ ```bash
328
+ adb-ready test gradle
329
+ adb-ready test gradle :app:pixel2api35DebugAndroidTest --dry-run
330
+ adb-ready test gradle :app:pixel2api35DebugAndroidTest --software-rendering
331
+ ```
332
+
333
+ ADB Ready preflights the exact task and normalizes fresh JUnit, HTML, shard,
334
+ and Gradle failure evidence without replacing the build's device definitions
335
+ or test DSL. See [Gradle Managed Devices](./gradle-managed-devices.md) for task
336
+ groups, sharding, outcomes, and the ownership boundary.
337
+
338
+ ### Firebase-owned remote devices
339
+
340
+ Use Firebase Test Lab when the provider should own remote physical or virtual
341
+ targets while ADB Ready owns the deterministic invocation, outcome language,
342
+ and bounded evidence handoff:
343
+
344
+ ```bash
345
+ adb-ready test firebase devices --project my-project
346
+ adb-ready test firebase instrumentation \
347
+ --project my-project \
348
+ --app app-debug.apk \
349
+ --test-apk app-debug-androidTest.apk \
350
+ --test-device model=Pixel2.arm,version=35 \
351
+ --dry-run --json
352
+ ```
353
+
354
+ See [Firebase Test Lab](./firebase-test-lab.md) for live catalog policy,
355
+ provider-owned lifecycle, result buckets, normalized outcomes, and explicit
356
+ remote cancellation.
@@ -58,6 +58,7 @@ existing file unless `--force` is explicit. Use `--dry-run` first.
58
58
  | `timeoutMs` | Default bounded operation timeout |
59
59
  | `output` | Interactive, animation, color, and Unicode preferences |
60
60
  | `targets.aliases` | Friendly name to exact ADB serial mapping |
61
+ | `targets.pools` | Named explicit target sets with bounded concurrency and queue policy |
61
62
  | `dev` | Development-session configuration |
62
63
  | `profiles` | Named, optionally inherited overrides |
63
64
 
@@ -81,6 +82,11 @@ existing file unless `--force` is explicit. Use `--dry-run` first.
81
82
  Unknown keys and invalid nested values fail validation rather than being
82
83
  silently ignored.
83
84
 
85
+ Target pools accept typed `adb`, `avd`, `remote-adb`, or `firebase` members.
86
+ Every pool requires an explicit `maxConcurrency`; member IDs and resource
87
+ identities must be unique. See [Target pools and fan-out](./target-pools.md) for
88
+ the complete schema, execution semantics, and trust boundaries.
89
+
84
90
  `init` detects and writes `packageManager` only for Expo, React Native, and
85
91
  Capacitor presets, where it selects the project CLI. Flutter and native Gradle
86
92
  use their own launchers, so an unrelated package-manager executable available
@@ -0,0 +1,126 @@
1
+ # Firebase Test Lab
2
+
3
+ Use this workflow when Firebase should own the remote Android targets. ADB
4
+ Ready validates exact dimensions against the live catalog, starts one bounded
5
+ instrumentation or Robo matrix through `gcloud`, normalizes its outcome, and
6
+ keeps local evidence. It does not create projects, manage billing, provision
7
+ credentials, or replace Firebase's test formats.
8
+
9
+ ## Prerequisites
10
+
11
+ Install the current [Google Cloud CLI](https://cloud.google.com/sdk/docs/install),
12
+ authenticate it in the surrounding developer or CI environment, and enable
13
+ Firebase Test Lab for the selected Google Cloud project. Credentials remain in
14
+ Google's credential store; ADB Ready never writes access tokens into config,
15
+ output, or evidence.
16
+
17
+ Inspect the authenticated live catalog before choosing a matrix:
18
+
19
+ ```bash
20
+ adb-ready test firebase devices --project my-project
21
+ adb-ready test firebase devices --project my-project --json
22
+ ```
23
+
24
+ Catalog output joins each model to its supported Android versions and reports
25
+ form, form factor, capacity, and provider tags. A requested unavailable,
26
+ inaccessible, deprecated, reduced-stability, zero-capacity, or low-capacity
27
+ dimension fails before artifact upload. Deprecated, reduced-stability, and
28
+ low-capacity choices require their matching explicit `--allow-*` policy.
29
+
30
+ ## Instrumentation
31
+
32
+ Preview the exact direct `gcloud` invocation first:
33
+
34
+ ```bash
35
+ adb-ready test firebase instrumentation \
36
+ --project my-project \
37
+ --app app/build/outputs/apk/debug/app-debug.apk \
38
+ --test-apk app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
39
+ --test-device model=Pixel2.arm,version=35,locale=en,orientation=portrait \
40
+ --dry-run --json
41
+ ```
42
+
43
+ Remove `--dry-run` to create the matrix. Repeat `--test-device` for additional
44
+ explicit dimensions. Local paths and private `gs://bucket/object` artifact
45
+ references are supported; signed URLs are rejected so credentials cannot be
46
+ retained accidentally.
47
+
48
+ ## Robo
49
+
50
+ Robo needs the app artifact but no instrumentation APK:
51
+
52
+ ```bash
53
+ adb-ready test firebase robo \
54
+ --project my-project \
55
+ --app app-debug.apk \
56
+ --test-device model=Pixel2.arm,version=35
57
+ ```
58
+
59
+ `--test-timeout 10m` controls the provider-side timeout for each execution.
60
+ `--run-timeout 30m` bounds only ADB Ready's local observation.
61
+
62
+ ## Results and evidence
63
+
64
+ For deterministic CI retention, use a bucket your job can read and write:
65
+
66
+ ```bash
67
+ adb-ready test firebase instrumentation \
68
+ --project my-project \
69
+ --app app-debug.apk \
70
+ --test-apk app-debug-androidTest.apk \
71
+ --test-device model=Pixel2.arm,version=35 \
72
+ --results-bucket gs://my-test-results \
73
+ --results-dir ci/$GITHUB_RUN_ID
74
+ ```
75
+
76
+ ADB Ready always retains redacted `gcloud` output and the structured result
77
+ under `.adb-ready/artifacts/firebase-<run-id>/`. With an explicit result bucket,
78
+ it also lists only that exact prefix and downloads a bounded allowlist of native
79
+ JUnit, HTML, JSON, log, screenshot, video, coverage, and protobuf artifacts:
80
+
81
+ - at most 100 provider files;
82
+ - at most 20 MiB per file;
83
+ - at most 50 MiB total;
84
+ - exact post-download size verification; and
85
+ - a local `provider-files.json` source manifest.
86
+
87
+ The structured result reports `complete`, `unavailable`, `remote-running`, or
88
+ `not-configured` evidence collection. Failure to copy requested provider
89
+ evidence is a warning alongside the original test outcome; it never rewrites a
90
+ test assertion into a false pass or a different provider outcome.
91
+
92
+ Without an explicit bucket, the Firebase console URL remains the provider
93
+ result reference and only local normalized evidence is copied.
94
+
95
+ ## Outcomes and cancellation
96
+
97
+ ADB Ready preserves the documented `gcloud firebase test android run` result
98
+ classes instead of flattening them:
99
+
100
+ - `passed`;
101
+ - `flaky` — provider roll-up succeeded but at least one test was flaky;
102
+ - `assertion-failed`;
103
+ - `inconclusive`;
104
+ - `unsupported`;
105
+ - `cancelled`;
106
+ - `infrastructure-failed`;
107
+ - `auth-failed`; and
108
+ - local `observation-timed-out` or `observation-stopped`.
109
+
110
+ Stopping or timing out local observation intentionally leaves an identified
111
+ remote matrix running. Cancel exactly one matrix with a separate explicit
112
+ action:
113
+
114
+ ```bash
115
+ adb-ready test firebase cancel MATRIX_ID --project my-project --dry-run
116
+ adb-ready test firebase cancel MATRIX_ID --project my-project
117
+ ```
118
+
119
+ Cancellation obtains a short-lived access token from the authenticated
120
+ `gcloud` process in memory and calls the official Testing API. The token is not
121
+ stored or printed.
122
+
123
+ See Firebase's current [Android CLI guide](https://firebase.google.com/docs/test-lab/android/command-line),
124
+ [available-device guidance](https://firebase.google.com/docs/test-lab/android/available-testing-devices),
125
+ and [result documentation](https://firebase.google.com/docs/test-lab/android/analyzing-results)
126
+ for provider setup, billing, quotas, and native result semantics.
@@ -0,0 +1,105 @@
1
+ # Gradle Managed Devices
2
+
3
+ Use this workflow when the Android build already declares virtual devices or
4
+ device groups in Gradle. Gradle remains the lifecycle owner: it provisions the
5
+ emulator, installs the app and tests, applies sharding, runs instrumentation,
6
+ and tears the target down. ADB Ready does not select, lease, or send commands to
7
+ that transient emulator.
8
+
9
+ ## Discover declared tasks
10
+
11
+ Run discovery from the Gradle project root:
12
+
13
+ ```bash
14
+ adb-ready test gradle
15
+ ```
16
+
17
+ ADB Ready invokes the checked-in `gradlew` or `android/gradlew` Wrapper and
18
+ lists only task descriptions that identify a managed device or device group.
19
+ If the Wrapper lives elsewhere, pass it explicitly:
20
+
21
+ ```bash
22
+ adb-ready test gradle --gradle tools/gradlew
23
+ ```
24
+
25
+ Task names belong to the project. Typical Android Gradle Plugin names resemble
26
+ `pixel2api35DebugAndroidTest` and
27
+ `phoneAndTabletGroupDebugAndroidTest`; ADB Ready does not invent a device,
28
+ variant, or group name.
29
+
30
+ ## Preview and run one task
31
+
32
+ Preview the exact direct process invocation without starting Gradle:
33
+
34
+ ```bash
35
+ adb-ready test gradle :app:pixel2api35DebugAndroidTest --dry-run --json
36
+ ```
37
+
38
+ Then execute the same bounded task:
39
+
40
+ ```bash
41
+ adb-ready test gradle :app:pixel2api35DebugAndroidTest --run-timeout 20m
42
+ ```
43
+
44
+ Before execution, ADB Ready asks Gradle to resolve that exact task. A missing
45
+ task fails before test infrastructure starts. The task is passed directly to
46
+ the Wrapper without a shell.
47
+
48
+ ## Groups, sharding, and server rendering
49
+
50
+ A declared group task runs through the same boundary:
51
+
52
+ ```bash
53
+ adb-ready test gradle :app:phoneAndTabletGroupDebugAndroidTest
54
+ ```
55
+
56
+ Request Gradle's managed-device sharding property when the build and Android
57
+ Gradle Plugin support it:
58
+
59
+ ```bash
60
+ adb-ready test gradle :app:pixel2api35DebugAndroidTest --shards 4
61
+ ```
62
+
63
+ On a headless host, opt into Gradle's documented SwiftShader property:
64
+
65
+ ```bash
66
+ adb-ready test gradle :app:pixel2api35DebugAndroidTest --software-rendering
67
+ ```
68
+
69
+ ADB Ready passes these properties for the current invocation. It never edits
70
+ Gradle files, creates SDK components, or changes a project's declared devices.
71
+
72
+ ## Results and evidence
73
+
74
+ After execution, ADB Ready reads native files from the Android Gradle Plugin
75
+ managed-device result and report directories. It retains bounded XML,
76
+ HTML, JSON, logs, text, screenshots, and video under:
77
+
78
+ ```text
79
+ .adb-ready/artifacts/gradle-managed-<run-id>/
80
+ ```
81
+
82
+ Machine output keeps these outcomes distinct:
83
+
84
+ - `passed` — Gradle exited successfully and retained JUnit has no failures;
85
+ - `assertion-failed` — fresh JUnit reports a failed or errored test;
86
+ - `infrastructure-failed` — Gradle could not complete provisioning or execution
87
+ and no test assertion explains the failure;
88
+ - `timed-out` — the configured finite timeout expired;
89
+ - `cancelled` — the caller interrupted the task;
90
+ - `GRADLE_MANAGED_TASK_NOT_FOUND` — exact task preflight failed.
91
+
92
+ The original Gradle result and report trees remain the source artifacts. The
93
+ ADB Ready copy is bounded, redacted where textual, and convenient for CI upload
94
+ or an agent. Failed tasks can only classify assertions from files written by
95
+ that execution. A successful up-to-date Gradle task may retain the output that
96
+ Gradle validated from its cache, explicitly marked with `gradle-cache`
97
+ provenance.
98
+
99
+ ## Responsibility boundary
100
+
101
+ Use `adb-ready test gradle` for Gradle-owned virtual devices. Use
102
+ `adb-ready run` when ADB Ready must own one already visible ADB target, prepare
103
+ ports and a development service, run a finite verifier, and clean only its own
104
+ resources. Connected-device tasks therefore remain under `run`; they are not
105
+ misrepresented as Gradle Managed Devices.
@@ -62,6 +62,40 @@ and never promoted to application problems. A configured `app.android.package`
62
62
  or `dev --package APP_ID` scopes the stream directly; Expo sessions retarget it
63
63
  to the package that Android resolves for the verified launch URL.
64
64
 
65
+ ## Correlated crash, ANR, and native evidence
66
+
67
+ Inspect one installed application across Android's available failure sources:
68
+
69
+ ```bash
70
+ adb-ready inspect failures com.example.app
71
+ adb-ready inspect failures --package com.example.app --since 30m --max-records 25 --json
72
+ ```
73
+
74
+ This read-only workflow verifies the selected package and target, records the
75
+ UID and current PID when Android exposes them, uses the target's own clock and
76
+ UTC offset, then correlates a bounded recent window from:
77
+
78
+ - package-scoped `ApplicationExitInfo` exposed by ActivityManager;
79
+ - UID-scoped `main`, `system`, and `crash` logcat buffers;
80
+ - exact package/process matches in supported app crash, native crash, and ANR
81
+ DropBox tags.
82
+
83
+ Java/Kotlin exceptions, native crashes, React Native fatal errors, and ANRs are
84
+ classified separately. Stable numeric `ApplicationExitInfo` reason codes drive
85
+ classification; its human description remains evidence rather than a parsing
86
+ contract. Old exits outside `--since` and DropBox blocks for other packages are
87
+ excluded. Matching evidence from different Android sources is returned as one
88
+ incident with `corroboratedBy`, rather than inflating one crash into multiple
89
+ findings. The default window is 15 minutes and default result limit is 20;
90
+ accepted bounds are one second to seven days and 1 to 100 incidents.
91
+
92
+ Android versions and OEM builds do not expose every source equally. The result
93
+ contains an availability, record count, truncation state, and limitation for
94
+ each source. Missing history or a DropBox permission denial does not become a
95
+ false application failure, and unrelated system/process failures are never
96
+ promoted into the selected app's findings. Returned evidence is sensitive and
97
+ redacted before retention or rendering.
98
+
65
99
  ## Session history
66
100
 
67
101
  Development sessions incrementally persist redacted NDJSON and an atomic