Skip to content

Bound clipboard sync retries and unify Windows subprocess flags - #31

Merged
ami3go merged 2 commits into
mainfrom
fix/clipboard-sync-and-windows-flags
Aug 20, 2026
Merged

Bound clipboard sync retries and unify Windows subprocess flags#31
ami3go merged 2 commits into
mainfrom
fix/clipboard-sync-and-windows-flags

Conversation

@ami3go

@ami3go ami3go commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Fixes from a review of the v0.11.6/v0.11.7 range. Five fixes, and one reported finding that was wrong — detailed at the bottom.

Clipboard

Unbounded re-send on a missing ACK. synchronizePcClipboard records #lastSentClipboard only on ACK, which is deliberate — test_failed_pc_clipboard_sync_remains_retryable wants a failed sync retried. But pollPcClipboard runs every 1800ms and skips only text recorded as sent, and the ACK wait is 1500ms. So an unacknowledged sync re-sent the same text every tick, forever, for as long as it stayed on the PC clipboard — an unbounded stream of control messages with a status line flapping over whatever the user was reading.

Retries are now capped at three attempts per text, after which the text is recorded as sent and the status says so. Retryability is preserved; the loop is not.

~875 round trips for a large paste. pasteText always followed the clipboard sync with a full textInjectionMessages() injection, which chunks at 300 UTF-8 bytes and awaits each chunk. At the 256 KiB clipboard limit that is roughly 875 sequential control messages. Injection is now capped at 8 KiB; above that the clipboard is still synchronized and the status tells the user to paste on the device. pasteTypedText rejects oversized input with the same explanation, since typing is all it does.

The ADR's reasoning is untouched — an ACK genuinely does not prove KEYCODE_PASTE inserted anything, so deterministic injection stays the default. This only stops the pathological size.

Connect wiped the fallback text box. resetClipboardSessionState cleared clipboardText.value on every connect, discarding text the user had typed while disconnected. The two cached device-clipboard fields are still cleared — leaking those across devices is the bug that method exists to fix — but the textarea is user input, not cached state.

Windows subprocess flags

The firewall call still flashed a console. network_access.apply() runs PowerShell New-NetFirewallRule on Windows with no creation flags, so a window appeared every time LAN access was applied or removed. Same defect class the new adb helper was added to fix, and the most visible instance of it — a full PowerShell window during an interactive settings change.

Four expressions of one decision, three of them different. adb/client.py fell back to the real constant, desktop/controller.py and desktop/support.py fell back to 0 (no flag at all), and network_access.py had nothing. All four now use droid_web_display/process_utils.subprocess_creation_kwargs. adb/client._subprocess_creation_kwargs is kept as a thin delegate because the Windows regression test monkeypatches it by name.

Retracted finding

I reported that copyAndroidClipboard had lost the transient user activation needed for navigator.clipboard.writeText by moving the write into the device-message loop, and that Copy would fail on Firefox.

That is wrong. bindAndroidCopyWriteThrough in main.ts already performs the write inside the originating gesture — it polls the DOM for the reply and falls back to document.execCommand("copy"). I had not read main.ts when I filed the finding. No change was made there, and I reverted the refactor I had started.

Verification

No node/npm or pytest in the authoring environment. dist/assets/controller.js was hand-applied and the manifest regenerated; verify_static_client passes over all 92 files.

Executed directly and passing: all 8 clipboard contract tests, both clipboard-copy server-mode tests, the toolbar layout tests, and the Windows creation-kwargs test. Not run: the fixture-based subprocess tests, and anything needing a browser. The retry cap and the injection threshold have not been exercised against a real device.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XfT5Z3Z24bjC8qtqERGpMQ

ami3go and others added 2 commits August 20, 2026 10:35
Review findings on the v0.11.6/v0.11.7 range. Five fixes; one reported
finding was wrong and is explained below.

transfers/clipboard:

- synchronizePcClipboard recorded #lastSentClipboard only on ACK, which
  is deliberate (test_failed_pc_clipboard_sync_remains_retryable wants a
  failed sync retried). But pollPcClipboard runs every 1800ms and skips
  only text recorded as sent, and the ACK wait is 1500ms, so an
  unacknowledged sync re-sent the same text every tick for as long as it
  stayed on the PC clipboard -- an unbounded stream of control messages
  with a status line flapping over it. Retries are now capped at three
  attempts per text, after which the text is recorded as sent and the
  status says so. Retryability is preserved; the loop is not.

- pasteText always followed the clipboard sync with a full
  textInjectionMessages() injection. That chunks at 300 UTF-8 bytes and
  awaits each chunk, so a paste at the 256 KiB limit was roughly 875
  sequential round trips. Injection is now capped at 8 KiB; above that
  the clipboard is still synchronized and the status tells the user to
  paste on the device. pasteTypedText rejects oversized input with the
  same explanation, since typing is all it does.

- resetClipboardSessionState cleared the visible fallback text box on
  every connect, discarding text the user had typed while disconnected.
  The two cached device-clipboard fields are still cleared; the textarea
  is user input, not cached state.

Windows subprocess flags:

- network_access.apply() runs PowerShell New-NetFirewallRule on Windows
  with no creation flags, so a console window appeared every time LAN
  access was applied or removed. This is the same defect the new adb
  helper was added to fix, and the most visible instance of it.

- That helper was the fourth expression of the same decision, and the
  three existing ones disagreed: adb/client.py fell back to the real
  constant, desktop/controller.py and desktop/support.py fell back to 0
  (no flag), and network_access.py had nothing. All four now use
  droid_web_display/process_utils.subprocess_creation_kwargs.
  adb/client._subprocess_creation_kwargs is kept as a thin delegate
  because the Windows regression test monkeypatches it by name.

Retracted finding: I reported that copyAndroidClipboard had lost the
transient user activation needed for navigator.clipboard.writeText by
moving the write into the device-message loop. That is wrong.
bindAndroidCopyWriteThrough in main.ts already performs the write inside
the originating gesture, polling the DOM for the reply and falling back
to document.execCommand("copy"). I had not read main.ts when I filed it.
No change was made there.

Tests: the session-state contract asserted the textarea is cleared; it
now asserts the opposite, with the reason. Added coverage for the retry
bound and the injection cap, against both src and dist.

No node/npm or pytest here. dist/assets/controller.js was hand-applied
and the manifest regenerated; verify_static_client passes over all 92
files. All 8 clipboard contract tests, both clipboard-copy server-mode
tests, the toolbar layout tests and the Windows creation-kwargs test
were executed directly and pass. The fixture-based subprocess tests and
anything needing a browser were not run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfT5Z3Z24bjC8qtqERGpMQ
CI rebuilds dist/ with tsc, which splits a single-statement `if` across
two lines. The hand-applied dist used a one-liner, so the new
oversized-text test failed against a real build even though it passed
against the hand-applied file.

dist now uses the compiler's form, and the assertion matches either
style so a future rebuild cannot fail on whitespace alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfT5Z3Z24bjC8qtqERGpMQ
@ami3go
ami3go merged commit 5a6a0ee into main Aug 20, 2026
3 checks passed
@ami3go
ami3go deleted the fix/clipboard-sync-and-windows-flags branch August 20, 2026 08:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant