15 KiB
Linux support completion plan
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.
Goal
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:
- delivers and changes fixture-owned state under every required side-effect oracle;
- returns the declared structured refusal before dispatch; or
- is recorded as an environment gap with a named owner and representative test lane.
A successful driver response is never delivery evidence.
Execution status
| 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. |
Current baseline
| 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.
Target environment matrix
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.
Phase 0: Reconcile the truth
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
- Mark stale sections of
libs/cua-driver/docs/test-harness-convergence-plan.mdas superseded by current evidence. - Rewrite
nix/cua-driver/tests/README.md; it currently names deleted fixtures. - Remove the empty
nix/cua-driver/tests/wayland/directory if it is still empty. - Add the experimental nested-compositor row to
libs/cua-driver/docs/action-support.mdand link it from the public platform support page. - Replace misleading "EIS compositor" wording. The current route is a private Unix socket protocol, not libei/EIS.
Acceptance
- No documentation points to a deleted fixture or workflow.
- Every public capability has current evidence or an explicit experimental gap.
Phase 1: Make results identify the real route
The result schema currently says only Wayland and can make nested injection
look like a standard virtual-pointer or AT-SPI route.
Work
- Add
LinuxCuaCompositorInjecttoDriverRouteincua-driver-testkit/src/e2e.rs. - Add environment fields for
compositorand availableinput_backends. - Detect values such as
openbox-x11,sway,gnome-mutter,kwin, andcua-compositor-nestedin the environment preflight. - Update
cua-e2e-reportand the Markdown summary to display the compositor and input route without changing test-family or action naming. - Keep backward compatibility with existing result artifacts.
Acceptance
- E1 and E2 retain identical cell outcomes.
- Every delivered cell identifies both its display server and actual route.
- E2 and E3 artifacts cannot be mistaken for each other.
Phase 2: Audit and harden nested injection
This is a proof gate, not an assumption. The existing path has two correctness risks:
app_id_for_windowappears 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.- The compositor silently ignores unsupported Unicode and keys and has no scroll or hotkey verb. The driver must never return success for those cases.
Work
- Add protocol handshake/version and acknowledgements.
- Resolve targets through stable app/window identity, including duplicate app IDs.
- Pre-validate all text and key shapes before sending.
- Return exact
background_unavailablerefusals for unsupported text, key, hotkey, scroll, or drag shapes until their protocol verb exists. - Add focused unit tests with a mock Unix socket for encoding, acknowledgements, validation, target ambiguity, and error mapping.
- Verify or correct the stale
LIBEI_SOCKETcomment inwayland/libei.rs.
Acceptance
- No unsupported command can be silently dropped.
- No route depends on connection-local Wayland object IDs as stable identities.
- A compositor protocol mismatch fails preflight once, before the matrix starts.
Phase 3: Restore the compositor as a typed environment
Do not restore the old Nix/Python fixture suite. Package only the environment, then run the existing Rust catalogs inside it.
Work
- Export
cua-compositoras a flake package and build check. - Add a
cua-driver-inject-e2edevelopment shell containing the compositor and existing Rust harness dependencies. - Add
scripts/ci/linux/run-rust-e2e-inject.shto start the nested session, perform capability preflight, and delegate to the canonicalscripts/ci/linux/run-rust-e2e.shcommand. - Add an independent
CuaCompositorobserver in the testkit. Read-only control queries must report focus and z-order directly; the driver response cannot be the observer. - Reuse the GTK3 and shared
CaseSpeccatalogs. Key expectations by detected environment capability, not by weakening an oracle. - Add a repo-local parallel-drag journal action because the old test asserted only command completion and therefore supplied insufficient evidence.
- Record every cell with before/after screenshots, trajectory data, and video.
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-compositoris a CI check.- The hosted experimental lane completes the Step A matrix three consecutive times on one source SHA with owned videos and no orphan evidence.
- The two useful historical claims, focus-free typing and simultaneous drags, are superseded by stronger typed fixture evidence.
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.
Phase 4: Complete the injection protocol
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
- Background left/right/double click, Unicode typing, named keys, hotkeys, scroll, single drag, and parallel drag all have typed delivery evidence.
- Remaining refusals describe genuinely unsupported public shapes.
Phase 5: Close stock Sway gaps
Work independently of the custom-compositor lane.
Work
- Repair Electron foreground AX/PX hotkey delivery in the driver.
- Repair renderer-to-compositor PX origin reconstruction.
- Keep Tauri/WebKitGTK environment-limited on hosted pixman Sway; do not use fixture changes or renderer-disabling flags as representative proof.
Acceptance
- Existing failing cells become delivered through external fixture state, or remain named gaps with exact diagnostics and a linked issue.
Phase 6: Representative desktop coverage
GNOME and WebKitGTK
- Run the unchanged shared Electron catalog on the existing GNOME VM.
- Add portal MP4 capture with the same ownership and nonblank checks as Sway.
- Check
/dev/drifirst and run the Tauri/WebKitGTK catalog there when a real render node is available. - If it is unavailable, provision a dedicated DRM/EGL VM rather than adapting the fixture to software-renderer behavior.
KDE
- Implement a target-addressable KWin activation adapter using supported KWin scripting or D-Bus interfaces.
- Validate activation through an independent observer before enabling portal input.
- Use Plasma 6; do not accept the broken Plasma 5.27 cloud image as evidence.
Real Xorg
- Add an optional maintainer lane for MPX/uinput and parallel pointer behavior.
- Do not describe Xvfb refusals as proof that real-Xorg delivery is impossible.
Phase 7: Release validation policy
- Keep E1 X11 and E2 Sway dispatch-only or maintainer-triggered, but require green run links on the exact release SHA in the release checklist.
- Keep E3-E7 optional until each environment has an owner and three stable runs.
- Store results, summary, screenshots, trajectories, logs, and videos per lane.
- Fail preflight once for a missing session capability; do not turn all cells into skips.
Suggested PR sequence
- Documentation reconciliation.
- Result route and environment metadata.
- Nested-injection refusal hardening and mock-socket tests.
- Sway hotkey repair and PX geometry repair as independent changes.
- Compositor package, build check, protocol query support, and stable identity.
- Testkit observer, typed Step A matrix, runner, and experimental workflow.
- Protocol completion and expectation flips.
- GNOME shared/video and representative Tauri renderer evidence.
- KWin adapter and Plasma 6 evidence.
- Release checklist gating.
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.
Anti-overfitting rules
- No Python behavioral runner, second matrix file, terminal/GIF fixture, or old Nix test restoration.
- No command-return-only oracle.
- No AT-SPI delivery described as raw PX injection.
- No raw-background claim without physical occlusion and focus/z-order guards.
- No fixture expectation change made solely to accommodate driver behavior.
- No compositor proliferation without a unique contract and named owner.
- No software-renderer workaround accepted as representative WebKitGTK evidence.
Definition of done
- E1 remains 108/108 on the final source SHA.
- E2 has no silent no-effect outcome; hotkey and PX geometry are delivered or recorded as precise gaps.
- E3 has the complete typed raw-injection matrix and truthful route metadata, or the dormant subsystem and its claim are removed.
- GNOME has shared Electron, portal video, and representative WebKitGTK evidence.
- KDE has verified target-addressable activation on Plasma 6 or remains clearly experimental with that adapter as the named blocker.
- Every supported or refused action is represented by a typed Rust row and external evidence across the environments where its contract differs.
- Public and contributor documentation link current evidence and never conflate standard Wayland with compositor-owned raw injection.