Summary
Surfaced while pre-vetting device-capture footage for a blog post. record --scope device --max-size 1080 produced a 216×480 video in one take on a Pixel 9 Pro XL API 37 emulator —
nowhere near the requested 1080px cap on the long edge. A second take on the same emulator
produced 486×1080, matching the cap.
Root cause: the Android recording path derives the on-device screenrecord --size once, from
whatever adb shell wm size currently reports, and only ever scales down. It never scales up
toward the requested cap, and it has no guard against a stale wm size override (as opposed to
the device's real physical size). The touch-overlay Swift compositor that burns the overlay into
the final video also does not independently enforce --max-size — it just re-encodes whatever
resolution screenrecord produced. So a stale/small wm size override silently caps the output
at whatever that override says, regardless of --max-size.
Repro
adb shell wm size <small-override, e.g. 240x536>
record start take.mp4 --scope device --max-size 1080
# ... interact ...
record stop
Expected: output video's long edge is scaled toward 1080 (up to the physical screen's aspect
ratio). Observed: output stays at (or near) the small override size — --max-size is not
independently enforced, it can only ever shrink an already-large size, never correct/override a
small one.
Root cause (read from source)
resolveAndroidRecordingSize in src/daemon/handlers/record-trace-android.ts:184-211 reads size
via adb shell wm size, preferring an override over the physical size:
const match =
sizeResult.stdout.match(/Override size:\s*(\d+)x(\d+)/) ??
sizeResult.stdout.match(/Physical size:\s*(\d+)x(\d+)/);
That size (override or physical) is passed into scaledSizeToMax
(src/daemon/handlers/record-trace-android.ts:213-223):
function scaledSizeToMax(size: AndroidRecordingSize & { maxSize: number }): AndroidRecordingSize {
const longest = Math.max(size.width, size.height);
if (longest <= size.maxSize) {
return { width: size.width, height: size.height };
}
const scale = size.maxSize / longest;
return {
width: scaledEvenDimension(size.width, scale),
height: scaledEvenDimension(size.height, scale),
};
}
This function only ever scales down (longest <= maxSize returns the input unchanged) — there
is no upscale path and no staleness check on the wm size override. If a prior test/tool left a
small wm size override in place, --max-size 1080 has no effect: the override is smaller than
1080, so scaledSizeToMax passes it through untouched, and screenrecord --size is invoked with
that small size (buildAndroidScreenrecordCommand, same file, ~line 229+). The touch-overlay
compositor (Swift, invoked from record_stop_overlay_export in
src/daemon/handlers/record-trace-finalize.ts) burns the overlay into whatever resolution
screenrecord already produced — it does not re-check or enforce --max-size against the actual
output.
Impact
--max-size is documented/expected to be an upper bound on output resolution, but in practice it
is only ever a ceiling on the detected device size at record-start time, not a guarantee about
the actual output resolution. Any drift between the real device resolution and whatever wm size
currently reports (a stale override, a multi-window/foldable state, etc.) silently changes the
output resolution with no warning.
Artifacts
Both takes from the pre-vet capture session,
~/.agent-device-bench/wave3/blog-videos/_style-sample/, same emulator, same --max-size 1080
flag:
style-sample-device.mp4 — v1 take, 216×480 actual output (verified via ffprobe).
style-sample-device-v2.mp4 — v2 take, 486×1080 actual output (verified via ffprobe).
style-sample-device.gesture-telemetry.json / style-sample-device-v2.gesture-telemetry.json
— telemetry referenceWidth/referenceHeight for both takes are 1344x2920 (the device's true
logical resolution) in both cases, confirming the video resolution — not the touch coordinate
reference frame — is what diverged between takes.
Source leads
src/daemon/handlers/record-trace-android.ts:
resolveAndroidRecordingSize (~line 184) — reads adb shell wm size, prefers Override size
over Physical size with no staleness check.
scaledSizeToMax (~line 213) — scale-down-only, no upscale/enforcement path.
- Touch-overlay Swift compositor invoked from
record_stop_overlay_export in
src/daemon/handlers/record-trace-finalize.ts — does not independently enforce --max-size
against the recorded video.
Summary
Surfaced while pre-vetting device-capture footage for a blog post.
record --scope device --max-size 1080produced a 216×480 video in one take on a Pixel 9 Pro XL API 37 emulator —nowhere near the requested 1080px cap on the long edge. A second take on the same emulator
produced 486×1080, matching the cap.
Root cause: the Android recording path derives the on-device
screenrecord --sizeonce, fromwhatever
adb shell wm sizecurrently reports, and only ever scales down. It never scales uptoward the requested cap, and it has no guard against a stale
wm sizeoverride (as opposed tothe device's real physical size). The touch-overlay Swift compositor that burns the overlay into
the final video also does not independently enforce
--max-size— it just re-encodes whateverresolution
screenrecordproduced. So a stale/smallwm sizeoverride silently caps the outputat whatever that override says, regardless of
--max-size.Repro
Expected: output video's long edge is scaled toward 1080 (up to the physical screen's aspect
ratio). Observed: output stays at (or near) the small override size —
--max-sizeis notindependently enforced, it can only ever shrink an already-large size, never correct/override a
small one.
Root cause (read from source)
resolveAndroidRecordingSizeinsrc/daemon/handlers/record-trace-android.ts:184-211reads sizevia
adb shell wm size, preferring an override over the physical size:That size (override or physical) is passed into
scaledSizeToMax(
src/daemon/handlers/record-trace-android.ts:213-223):This function only ever scales down (
longest <= maxSizereturns the input unchanged) — thereis no upscale path and no staleness check on the
wm sizeoverride. If a prior test/tool left asmall
wm sizeoverride in place,--max-size 1080has no effect: the override is smaller than1080, so
scaledSizeToMaxpasses it through untouched, andscreenrecord --sizeis invoked withthat small size (
buildAndroidScreenrecordCommand, same file, ~line 229+). The touch-overlaycompositor (Swift, invoked from
record_stop_overlay_exportinsrc/daemon/handlers/record-trace-finalize.ts) burns the overlay into whatever resolutionscreenrecordalready produced — it does not re-check or enforce--max-sizeagainst the actualoutput.
Impact
--max-sizeis documented/expected to be an upper bound on output resolution, but in practice itis only ever a ceiling on the detected device size at record-start time, not a guarantee about
the actual output resolution. Any drift between the real device resolution and whatever
wm sizecurrently reports (a stale override, a multi-window/foldable state, etc.) silently changes the
output resolution with no warning.
Artifacts
Both takes from the pre-vet capture session,
~/.agent-device-bench/wave3/blog-videos/_style-sample/, same emulator, same--max-size 1080flag:
style-sample-device.mp4— v1 take, 216×480 actual output (verified viaffprobe).style-sample-device-v2.mp4— v2 take, 486×1080 actual output (verified viaffprobe).style-sample-device.gesture-telemetry.json/style-sample-device-v2.gesture-telemetry.json— telemetry
referenceWidth/referenceHeightfor both takes are1344x2920(the device's truelogical resolution) in both cases, confirming the video resolution — not the touch coordinate
reference frame — is what diverged between takes.
Source leads
src/daemon/handlers/record-trace-android.ts:resolveAndroidRecordingSize(~line 184) — readsadb shell wm size, prefersOverride sizeover
Physical sizewith no staleness check.scaledSizeToMax(~line 213) — scale-down-only, no upscale/enforcement path.record_stop_overlay_exportinsrc/daemon/handlers/record-trace-finalize.ts— does not independently enforce--max-sizeagainst the recorded video.