packages/cua-driver/docs/linux-support-completion-plan.md
Status: Historical implementation plan. Current outcomes live in
action-support.md; remaining work lives in the
public platform roadmap.
Scope: Linux driver behavior, canonical Rust E2E evidence, and release validation
Principle: Standard Wayland and the optional compositor-owned injection environment are separate products with separate claims.
Close the remaining Linux gaps without restoring the deleted Python, shell, GIF, or real-app fixture suites. Reuse the repo-local Rust harness catalogs and their external state, focus, z-order, cursor, input-leak, screenshot, trajectory, and video oracles.
The work is complete when each supported action either:
A successful driver response is never delivery evidence.
| Phase | Result |
|---|---|
| Truth and route metadata | Complete. Current docs distinguish X11, stock compositor routes, portal/libei, and the nested private protocol. Typed results record compositor and input backend. |
| Nested protocol hardening | Complete. The protocol has a version handshake, acknowledgements, stable PID targeting, focus/z-order queries, geometry queries, strict key/text validation, and explicit errors for missing resources. |
| Nested typed environment | Complete as an experimental lane. Nix packages the compositor, the canonical Rust catalogs run inside it, and every lane retains typed evidence and video. Promotion still depends on a complete accepted run. |
| Protocol action coverage | Implemented for left/right/double click, printable ASCII text, named keys, hotkeys, scroll, and single/parallel drag. Unicode text and a canonical parallel-drag behavior row remain unproven. |
| Stock Sway repairs | Complete for the declared hosted catalog. Exact run 29213349962 passed all 108 Electron, Tauri, GTK3, capture, and desktop-scope outcomes: 71 deliveries and 37 exact refusals. |
| Representative desktops | Partial. GNOME native GTK behavior is accepted; Plasma 6 reached session, AT-SPI, and portal preflight only. Shared GNOME renderers, portal video, real Xorg MPX, and DRM/EGL WebKitGTK remain named gaps. |
| Release policy | Complete. Linux unit/source/distribution gates run automatically; X11, Sway, and nested GUI matrices are exact-SHA maintainer dispatches with row-level artifacts. |
| Environment | Current evidence | Main gaps |
|---|---|---|
| X11/Openbox on Xvfb | 108/108 declared outcomes: 71 deliveries and 37 exact refusals | Real-Xorg MPX/uinput and parallel pointer behavior are not proven by Xvfb |
| Sway/wlroots, native Wayland | 108/108 declared outcomes: 71 deliveries and 37 exact refusals across Electron, Tauri, GTK3, capture, and desktop scope | Other wlroots compositors remain unproven; stock Wayland still cannot provide arbitrary focus-free raw input outside declared target-addressable routes |
| GNOME 46/Mutter | GTK3 31/31 using WinRects, AT-SPI, and portal/libei | Shared Electron/Tauri matrix and portal MP4 evidence |
| KDE/KWin | Plasma 6 session startup, GTK AT-SPI discovery, and portal interfaces | Target-addressable activation adapter and an accepted behavioral lane |
Nested cua-compositor | Packaged experimental environment, route metadata, observer, native GTK3 31/31, capture/scope 5/5, and Electron shared 26/36 before focused repairs | Stable renderer click/scroll coverage, a complete accepted shared matrix, Unicode text, and a canonical parallel-drag row |
Standard Wayland cannot generally route raw pointer or keyboard events to an
arbitrary occluded, unfocused surface. The nested cua-compositor is different:
it owns the compositor and can route directly to a client surface through its
private control socket. Evidence from that environment must never be presented
as a stock Sway, GNOME, KDE, or general Wayland capability.
Keep the matrix small. An environment earns a lane only when it proves a contract that another lane cannot.
| ID | Environment | Purpose | Ownership |
|---|---|---|---|
| E1 | X11/Openbox on Xvfb | XSendEvent, XTest, AT-SPI, capture, scope, complete shared/native catalogs | Hosted GitHub runner; canonical |
| E2 | Sway on native Wayland | wlroots virtual pointer, screencopy, foreign toplevel, and standard-Wayland refusals | Hosted GitHub runner; canonical |
| E3 | Nested cua-compositor | Raw focus-free per-surface input and multi-pointer behavior | Hosted GitHub runner; experimental until proven |
| E4 | GNOME/Mutter real session | WinRects, portal/libei, shared renderer apps, portal recording | Maintainer VM; representative |
| E5 | Real Xorg session | MPX/uinput behavior unavailable under Xvfb | Maintainer VM; optional |
| E6 | KDE Plasma 6/KWin | KWin activation and portal behavior | Maintainer VM; experimental |
| E7 | Real DRM/EGL renderer session | WebKitGTK/Tauri accessibility tree and native renderer geometry | Prefer E4; use a separate VM only if needed |
Labwc, Hyprland, and other wlroots compositors remain expected-compatible but unproven unless a user report demonstrates a meaningful divergence. XWayland is not a separate lane.
The phase sections below preserve the implementation and acceptance criteria used for this work. Current outcomes are summarized above; unchecked evidence gaps remain follow-up work rather than implied support.
Update stale internal references before changing behavior.
Work
packages/cua-driver/docs/test-harness-convergence-plan.md
as superseded by current evidence.nix/cua-driver/tests/README.md; it currently names deleted fixtures.nix/cua-driver/tests/wayland/ directory if it is still empty.packages/cua-driver/docs/action-support.md and link it from the public platform
support page.Acceptance
The result schema currently says only Wayland and can make nested injection
look like a standard virtual-pointer or AT-SPI route.
Work
LinuxCuaCompositorInject to DriverRoute in
cua-driver-testkit/src/e2e.rs.compositor and available input_backends.openbox-x11, sway, gnome-mutter, kwin, and
cua-compositor-nested in the environment preflight.cua-e2e-report and the Markdown summary to display the compositor and
input route without changing test-family or action naming.Acceptance
This is a proof gate, not an assumption. The existing path has two correctness risks:
app_id_for_window appears to compare a toplevel object ID obtained from one
Wayland connection with objects from a new connection. Protocol IDs are
connection-scoped, so target resolution must be tested and likely replaced
with stable identity resolution.Work
background_unavailable refusals for unsupported text, key,
hotkey, scroll, or drag shapes until their protocol verb exists.LIBEI_SOCKET comment in wayland/libei.rs.Acceptance
Do not restore the old Nix/Python fixture suite. Package only the environment, then run the existing Rust catalogs inside it.
Work
cua-compositor as a flake package and build check.cua-driver-inject-e2e development shell containing the compositor and
existing Rust harness dependencies.scripts/ci/linux/run-rust-e2e-inject.sh to start the nested session,
perform capability preflight, and delegate to the canonical
scripts/ci/linux/run-rust-e2e.sh command.CuaCompositor observer in the testkit. Read-only control
queries must report focus and z-order directly; the driver response cannot be
the observer.CaseSpec catalogs. Key expectations by detected
environment capability, not by weakening an oracle.Minimum Step A matrix
| Background PX action | Initial contract |
|---|---|
| Left, right, and double click | Deliver |
| ASCII type text | Deliver |
| Supported named key | Deliver |
| Single drag | Deliver after socket routing exists; otherwise exact refusal |
| Two simultaneous drags | Deliver with fixture state for both paths |
| Scroll | Exact refusal until an axis verb exists |
| Hotkey | Exact refusal until a chord verb exists |
| Unsupported Unicode or key | Exact refusal before socket dispatch |
All delivery rows require fixture state, unchanged sentinel focus and z-order, and no leaked input. Cursor preservation is not an oracle when the compositor route intentionally has no physical cursor.
Acceptance
nix build .#cua-compositor is a CI check.Deletion gate
If the compositor cannot build, target stable identities, or support truthful focus/z-order observation after two focused implementation cycles, remove the entire dormant subsystem and its public capability claim in one PR. Do not leave advertised but untested production branches indefinitely.
After Step A is green, add protocol verbs for axis/scroll, modifier chords, Unicode text, broader named keys, and single drag. Flip a test from refusal to delivery only when the unchanged fixture and side-effect oracles pass.
Acceptance
Work independently of the custom-compositor lane.
Work
Acceptance
/dev/dri first and run the Tauri/WebKitGTK catalog there when a real
render node is available.Each PR must leave existing canonical lanes green. A new environment may fail only in a clearly labeled experimental job and may not change the public contract of E1 or E2.