Skip to content

fix(droid-control): preserve text during native typing - #50

Merged
factory-ain3sh merged 1 commit into
masterfrom
ain3sh/native-type-0625
Sep 15, 2026
Merged

factory-ain3sh merged 1 commit into
masterfrom
ain3sh/native-type-0625

Conversation

@factory-ain3sh

Copy link
Copy Markdown
Contributor

Description

What

Preserves literal text sent through tctl type on native Linux Wayland. Real Ghostty byte captures reproduce the reported dropped : and Q→Tab substitution on the baseline and pass on this patch. The fix costs one process per character: a warm 52-character prompt measured 403 ms instead of 223 ms.

Why

wtype allocates sequential evdev keycodes for distinct characters. In Reply with exactly: QA ready. Do not call any tools., the first unintended assignment is:

Requested text wtype keycode Ghostty physical-key handling Incoming bytes
: 14 Backspace with printable text omitted
Q 15 Tab 0x09

Swapping the 14th and 15th distinct characters moves the corruption with their keycodes. A direct raw-stdin probe reproduces it without Droid or the diagnostic wrapper.

How

Routes literal typing and single-character presses through fresh single-character wtype keymaps. -- preserves dash-prefixed text. Chords keep their existing explicit modifier releases; tuistory keeps bulk typing. No clipboard path, custom keyboard encoder, dependency, or pacing delay is added.

Repro Recipe

With Ghostty, cage, and wtype installed, run from the repository root:

TCTL="$PWD/plugins/droid-control/bin/tctl"
S="native-proof-$(date +%s)-$$"
"$TCTL" launch "printf 'READY\n'; cat" -s "$S" --backend ghostty
"$TCTL" -s "$S" wait READY --timeout 15000
"$TCTL" -s "$S" type 'Reply with exactly: QA ready. Do not call any tools.'
"$TCTL" -s "$S" press enter
"$TCTL" -s "$S" snapshot
"$TCTL" -s "$S" close

Expect the exact prompt. The baseline drops the colon and substitutes Tab for Q.

Related Issue

Discovered during CLI-1964 QA. This independently fixes the plugin input driver; it does not change CLI startup or close that work.

Reviewer Guide

Review depth: Standard.

  1. plugins/droid-control/bin/tctl: one literal-input owner replaces the bulk call and duplicate character-press branch.
  2. tests/test_tctl.py: deterministic process-boundary contract and opt-in real Ghostty bytes.
  3. Linux mechanics document and workflow path filter: explain the tradeoff and run the existing test job when tctl changes.

Risk & Impact

  • Native typing is slower. Five warm samples per implementation, same isolated Ghostty session and 52-character prompt: baseline median 223 ms, patch median 403 ms. These are local typing measurements, not application latency or cross-machine estimates. Re-measure by timing the recipe's type command against each implementation after warmup.
  • Real-emulator coverage is Ghostty 1.3.1-arch2.2 with wtype 0.4-2.2 on Linux, plain mode and Kitty disambiguation flag 1. Other emulators, full Kitty reporting flags, non-UTF-8 locales, and arbitrary custom keybindings were not verified.
  • No installed plugin cache, desktop clipboard, authentication fixture, or CLI source was modified.

Verification

Behavior verified @ 7d58d672a6e083a457eed3229a687177b918d3d3:

Control Result
Bulk → single-character → bulk, same real Ghostty boundary corrupt → exact → corrupt
New native regression against baseline, plain and Kitty modes both fail on the original corrupted prompt
Patched suite all 7 tests pass
Deterministic contract tests against baseline 5 fail; unchanged tuistory routing passes

Coverage includes punctuation/capitals, accented/CJK/emoji/combining text, consecutive calls, Ctrl/Shift chords followed by unmodified text, whitespace, literal dashes, warmup, and stopping on injector failure.

Sentinel test: TctlGhosttyTest.test_native_bytes_across_text_calls_and_chords checks real incoming bytes, not screen reconstruction.

Standard validators: scoped Python unittest suite, Bash syntax, and staged whitespace checks pass. CI runs deterministic boundary tests; native tests require TCTL_NATIVE_E2E=1. All test-created terminal sessions were closed.

Bulk wtype assigns distinct characters consecutive evdev keycodes. Ghostty
interprets positions 14 and 15 as Backspace and Tab, dropping punctuation
and replacing letters before input reaches the application.

Reuse single-character native keymaps for literal typing and character
presses. Keep chord modifier release and tuistory bulk input unchanged.
Add deterministic boundary tests and an opt-in real Ghostty byte probe;
the original wrapper fails the reported prompt in plain and Kitty modes.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@factory-ain3sh
factory-ain3sh merged commit d362dc1 into master Sep 15, 2026
3 checks passed
@factory-ain3sh
factory-ain3sh deleted the ain3sh/native-type-0625 branch September 15, 2026 19:03
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.

2 participants