agent-desktop/docs/solutions/logic-errors
Lahfir 4f7bad337c
feat: windows input synthesis (sub-phase 2.8) (#123)
* docs: add sub-phase 2.8 input synthesis plan

* docs: harden the 2.8 input synthesis plan after adversarial review

* docs: give every 2.8 deferral a receiving owner in phases.md

* feat: measure windows input synthesis gaps (area 20)

Probe the unmeasured SendInput and token facts 2.8 depends on so coordinate, abort-drag, double-click, integrity, and cost branches are settled before the adapter InputOps land.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: implement windows mouse_event via SendInput

Give bare-coordinate hover/click/wheel a real physical path with primary-vs-virtual-desktop normalization, FFI held-edge guards, and a release-safe modifier hold around multi-click.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: implement windows drag with origin release guard

Port the macOS abort-state contract onto SendInput so an interrupted drag posts corrective release at the origin and reports delivered_unverified instead of claiming a clean failure.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: implement windows keyboard synthesis and blocked combos

Ship chord and UTF-16 type_text primitives with release guards, macOS key-name parity, and a Windows-specific dangerous-combo list so press/type can land without stuck keys or silent session-kill shortcuts.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: detect uipi elevation before physical input

Compare caller and target integrity levels so a Medium-into-High SendInput attempt fails closed as PERM_DENIED instead of reporting a silent no-op as success.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: wire windows physical execute_action legs

Replace the 2.7 not-supported stubs for type, press, multi-click, and right-click with SendInput synthesis gated by focus, elevation, and headed policy.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: pin windows physical input envelope parity

Lock AdapterError and ActionResult wire shapes for the physical legs against the macOS disposition contract, and cite the A20-6 hot-path cost baseline already captured in area 20.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: dogfood windows input synthesis and harden delivery

Prove the physical path on Notepad and ScratchForms under the corpus safety envelope, and fix the dogfood-found root-window foreground gate plus SendInput absolute normalization and drag pickup timing.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: align windows input synthesis docs with shipped evidence

Restate phases, concepts, skill wording, and ledger rows so 2.8 reads as what shipped, including UIPI detection closure and the type command divergence handed to 2.15.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: harden windows input abort guards after review

Strip plan decision ids from crates so CI phase-reference scan passes, arm a click release guard around each down/up, and re-verify keyboard focus at every type_text chunk boundary.

Co-authored-by: Cursor <cursoragent@cursor.com>

* refactor: give the mouse input constructors and release-guard state one home

button_input existed verbatim in four files and move_input in three, so the
corrective batch an abort depends on could drift from the normal post. The
click and drag guards also carried the same armed-and-counted state machine
and the same delivery report, differing only in their recovery hint.

Both now have a single home: mouse_send.rs owns the SendInput surface -
the MOUSEEVENTF_* flags with their compile-time assertions against
windows-sys, plus the two input constructors - and release_state.rs owns
the guard state and the delivery report it produces. Each guard keeps its
own Drop, because only the guard knows whether the corrective post is a
button-up in place or a move back to the origin first.

* fix: resolve the host window before refusing a physical click

target_window_is_foreground read the leaf element's NativeWindowHandle,
which UIA reports as 0 - success, not failure - for every element that is
not itself a window. That is the normal shape for WPF, WinUI, UWP and
Chromium content, so double-click, triple-click and right-click were
refused before injection on most modern desktop UI. The handle is now
resolved the way the rest of the crate resolves it, by climbing to the
first ancestor that owns one.

Also from the same review pass:
- mouse-wheel dropped the caller's modifiers, so the documented
  --modifiers shift horizontal-scroll chord silently degraded to a plain
  vertical scroll while still reporting success.
- The drag preflight reserved one pickup delay where the sequence spends
  two, admitting drags that then aborted mid-gesture with the button
  down. Both delays now come from one constant.
- The skill docs carried plan decision ids and sub-phase numbers, and
  they are include_str!d into the binary, so agent-desktop skills served
  them to agents. The phase-reference scan now covers skills/ for exactly
  that reason and caught a second instance in SKILL.md.

* fix: close the remaining input-synthesis review findings

- The click sequence counted delivered events per click and let the
  inter-click gap fail through a bare ?, so a --count 3 that aborted late
  reported two events, or none at all with an unknown disposition.
  Accounting now spans the sequence while cleanup stays per click. Wheel
  chunks report the same way: a partial scroll says how much landed.
- mouse_modifier opened a second SendInput keyboard path with its own
  fake sink, so the one-seam invariant keyboard_send documents was
  already false. It now posts through that seam and reuses its VK map.
- type_text's focus gate and per-chunk re-verify were exercised through a
  cfg(test) clone that omitted the policy gate, the preflight and the real
  verifier, so deleting either guard left the suite green. The gate is one
  production function now, with the verifier injected, and a test drives
  it with a verifier that fails mid-sequence.
- Two drag tests asserted a std-library identity and re-typed the
  production step arithmetic. They drive synthesize_drag and read the
  posted event stream instead. Inverting the first showed the zero-dwell
  early return was dead - the loop guard already covered it - so it is
  gone.
- elevation's module doc blamed unavailable token manufacture for the
  unproven cross-boundary effect; the captures record manufacture working
  with no higher-integrity target to aim at. keyboard's docs claimed its
  primitives were unreachable from execute_action, which calls both.

* docs: capture the input-synthesis learnings and correct what the run overstated

Two new solutions docs and one extended:
- An enforcement gate must cover everything the binary embeds. The
  phase-reference scan read crates/ and src/ for .rs files while the skill
  markdown it never looked at was include_str!d into the binary, so plan
  ids were being served to agents by the skills command. Widening the
  scope immediately found a ninth instance nobody had reported.
- A zero success value is not the answer you asked for. UIA returns
  NativeWindowHandle 0 as a success for any element that is not a window,
  and the crate already had the climb that treats it as keep-looking; the
  physical-click gate re-derived a naive version and refused delivery on
  WPF, WinUI and Chromium.
- The cannot-fail taxonomy gains the cfg(test) re-implementation shape,
  and a prevention note to invert one production site per run: two
  mutations at once made a test fail on the other one's arithmetic and
  hid the failure that mattered.

The dogfood report claimed the multi-click path more broadly than it
measured it. Both multi-click judgements ran against HWND-bearing targets,
which is precisely where the defect was invisible, so J4 now states its
scope and the residual records that a non-HWND judgement is still owed.

* docs: judge multi-click on a non-HWND target and close the residual

The original run judged multi-click only against WinForms and Notepad,
both of which own a window handle - the one shape where the foreground
gate's leaf-handle read could not fail. J10 runs the same WPF button with
the pre-fix and fixed binaries back to back: the first reports
ACTION_FAILED and not_delivered with the app's click counter untouched,
the second lands and the counter advances to action:2, read back
independently of the envelope.

Two correct refusals fell out of the same run and are recorded with it:
the occlusion gate naming the terminal window that actually covered the
fixture, and the private-file guard rejecting a store an elevated run had
left owned by the Administrators group rather than the user.

* fix: block modifier supersets of a dangerous shortcut

Exact canonical-string matching let alt+shift+tab through to SendInput.
That is the reverse task switcher and takes the foreground exactly as the
alt+tab it extends, which is the harm the list exists to prevent. A test
asserted the old behaviour as correct, reasoning that an extra modifier
makes a shortcut unrelated; shift does not neutralize alt+f4 and does not
neutralize alt+tab, so the premise was wrong and the test is gone.

Matching is now key equality plus modifier superset. It over-blocks a few
combinations nobody presses, and that trade is deliberate: a wrongly
blocked combo costs one --force, a wrongly allowed one moves input to
another window mid-run.

macOS answers the same problem by enumerating variants (it lists cmd+q and
cmd+shift+q separately), so the two adapters now decide danger by
different rules. Recorded as a settlement item for the hardening gate
rather than changed here, since the macOS crate is the GA line.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 05:29:03 -07:00
..
a-zero-success-value-is-not-the-answer-you-asked-for.md feat: windows input synthesis (sub-phase 2.8) (#123) 2026-08-08 05:29:03 -07:00
emit-state-on-a-positive-claim-never-on-a-default.md feat: windows vocabulary — roles, states, native_id and name evidence (#115) 2026-08-01 14:25:02 -07:00
progressive-snapshot-review-contract-2026-04-16.md fix: return observed trees and stop demanding renderer activation from shallow walks (#117) 2026-08-02 01:02:50 -04:00
tri-state-evidence-collapses-under-negation.md feat: windows resolution and live locator (sub-phase 2.5) (#120) 2026-08-06 16:14:04 -07:00