fix(device): hard-fail device_screenshot on explicit-platform raw failure (closes #136 sub-issue 1)#174
Merged
Merged
Conversation
codex-pair (ask-llm plugin) is opt-in via a .codex-pair-context.md marker in the project root. The marker enables the PostToolUse hook; its content is the per-file project context the reviewer uses. Marker is per-developer (own model/threshold preferences, own rules) so it does not belong in shared history. Same for the log, cache, and pause sentinel directories — all developer-local. See https://github.com/Lykhoyda/ask-llm marketplace plugin for details. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…raw path fails Closes #136 (sub-issue 1 — re-evidence on #60). PR #142's "graceful degradation" — on raw screenshot failure, fall through to runAgentDevice — was the regression vector. agent-device's --platform routing is exactly the broken path PR #142 was created to avoid. The fallback re- introduced the bug whenever the raw path tripped on transient state (the user's session: OOM-unstable emulator → adb devices reports it as 'offline' → parseAdbDevicesEmu skips it → resolver returns null → fallback fires → iOS screen captured for a platform=android request). New behavior: when platform: is passed explicitly and the raw path fails, return a structured failResult with code=SCREENSHOT_FAILED and meta.reason=('no-device' | 'capture-failed') instead of trying agent-device. The error message names the underlying CLI (xcrun simctl / adb) and the sub-cause so the caller knows what to fix (boot a device vs retry a transient state). Implicit-platform calls (platformExplicit=false) still route through runAgentDevice — backward parity preserved by the existing 'platformExplicit=false → uses runAgentDevice (backward parity)' test. Changes: - src/tools/device-screenshot-raw.ts: tryRawScreenshot now returns a discriminated union {ok:true,path} | {ok:false,reason} so callers can distinguish resolver miss from capturer failure. - src/tools/device-list.ts: captureAndResizeScreenshot returns failResult on explicit-platform raw failure; imports failResult. - test/unit/gh-136-screenshot-raw-platform.test.js: inverted the existing 'falls through' test to assert hard-fail; added a second hard-fail test for the capture-failed surface; tightened the three pre-existing resolver/capturer null-checks to deepEqual on the new union shape. Verified: 1465/1465 cdp-bridge unit tests passing (+1 net new test). Also included: scripts/cdp-bridge/dist/tools/device-permission.js — dist was stale relative to source (PR #169's second escapeRegex site fix never made it to dist). Rebuilt locally; no source change. NOTE: codex-pair flagged an unrelated pre-existing issue in this file: deriveScreenshotPath throws PathTraversalScreenshotError on a '..' path, but no catch wraps the handler, so a bad path becomes an uncaught tool exception. Filed as #136-followup, NOT addressed here. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the first of three sub-issues in #136 —
device_screenshot platform: \"android\"silently capturing the iOS screen when both simulators are booted. PR #142 (the prior fix) shipped a raw-command path that bypasses agent-device's broken--platformrouting, but kept a "graceful fallback" that fell through to that same broken path on any raw failure. The fallback was the regression vector.Root cause
In the user-reported session, the Android emulator was in an OOM-unstable state.
adb devicesreported it asoffline,parseAdbDevicesEmu's^(emulator-\\d+)\\s+device\\bregex skipped it, the resolver returnednull, the raw path returnednull, and the fallback torunAgentDeviceFnfired — which then routed via agent-device's--platformflag (the exact broken path #142 was meant to avoid), capturing the iOS screen.The comment in
device-list.tsdefended this as graceful degradation preserving single-device behavior. That reasoning was backwards: single-device users don't passplatform:explicitly (it's inferred from CDP target), so they never hit this branch. Only the multi-device case setsplatformExplicit=true, and for that case silent fallback defeats the entire purpose of the arg.Fix
When
platformExplicit && raw fails, return a structured failure with the actionable reason instead of falling through.device-screenshot-raw.ts:tryRawScreenshotnow returns a discriminated union{ok:true,path}|{ok:false,reason: 'no-device' | 'capture-failed'}so callers can distinguish a resolver miss (no device booted / offline) from a capturer failure (transient adb/simctl error).device-list.ts:captureAndResizeScreenshotreturnsfailResult(...)withcode=SCREENSHOT_FAILEDandmeta={platform, reason}on explicit-platform failure. Error message names the CLI (xcrun simctl/adb) and the sub-cause so callers can act.platformExplicit=false) still route throughrunAgentDeviceunchanged — backward parity preserved by the existing'platformExplicit=false → uses runAgentDevice (backward parity)'test.Test plan
'falls through to runAgentDevice'test to assert hard-fail (the test had been encoding the regression behavior)capture-failedsurface (distinct fromno-device)tryRawScreenshottests fromassert.equal(result, null)toassert.deepEqual(result, {ok:false, reason:...})on the new union shapeOut of scope
cdp_statusdev-client picker auto-tap) — needs an Expo Dev Client test-app to reproduce. Tracked in Multi-device routing + dev-client picker hangs (combined session feedback) #136.launchAppdev-launcher handler) — same prereq.deriveScreenshotPaththrowsPathTraversalScreenshotErrorfor..paths, but no catch wraps the handler — a badpathbecomes an uncaught tool exception instead of a structuredfailResult. Filed as a follow-up, not addressed in this PR.Refs
🤖 Generated with Claude Code