packages/cua-driver/docs/test-harness-convergence-plan.md
Historical plan. Use test-harnesses-guide.md, test-matrix.md, and
e2e-ci-reporting.md for the implemented contributor workflow.
The original direction remains sound:
This review changes five parts of the earlier plan:
matrix.yaml, Python collector, or shell-owned scenario list.The target is a smaller set of tests with named coverage reasons and strong external oracles. Test count is not a success metric.
#[should_panic] and do not count as green coverage.
They run in a named optional lane with a linked issue until fixed.The branch implements the target ownership and reporting model. Platform validation below replaces the older 60-cell proposal and records the remaining environment or backend gaps separately.
cross_platform_behavior_test.rs has one typed 40-cell catalog per shared
harness application: 80 shared cells across Electron and Tauri per platform.
It covers AX and PX with foreground and background delivery for click,
text, keyboard, scroll, and child-window actions; PX drag and AX editor-save
each cover both delivery modes.targeting, delivery, scope,
backend route, expected and observed behavior, independent test status,
required oracles, duration, and evidence.background_unavailable, background_occluded, and
background_uipi_blocked.background_occluded refusal because
temporarily raising the window would violate the desktop-side-effect contract.cua-driver-testkit.The plan reached its ownership and reporting target on 2026-07-12. Current
support details remain in action-support.md; these landing checkpoints record
completion without turning this historical plan into a live status page.
29211506624 passed 114/114 rows at 6d7f02e4.448d052f with the stable
installed-app TCC identity.29213365684 and Sway run 29213349962 each passed 108/108
rows at 72083bb4, with the same 71 deliveries and 37 exact refusals.cua-compositor lane remains experimental and reports
its typed failures rather than contributing stock-Wayland support claims.Every behavioral cell is declared in Rust with one structure shared by web and native harness tests:
struct CaseSpec {
id: &'static str,
platform: Platform,
display_server: DisplayServer,
harness: Harness,
action: Action,
targeting: Targeting,
delivery: Delivery,
scope: Scope,
expectation: ContractExpectation,
oracles: &'static [OracleKind],
route: DriverRoute,
}
enum ContractExpectation {
Deliver,
Refuse { allowed_codes: &'static [RefusalCode] },
}
RefusalCode is an enum, not a string-prefix check. Its initial Windows set is
BackgroundUnavailable, BackgroundOccluded, and BackgroundUipiBlocked;
Linux currently declares only BackgroundUnavailable. A cell lists the exact
codes allowed by its controlled setup.
Targeting uses Ax, Px, Page, or NotApplicable. Use targeting in the
schema instead of capture_mode; capture is a separate read contract.
DriverRoute names the implementation path that justifies coverage, such as
UIA Invoke, PostMessage, coordinate injection, CGEvent, AT-SPI action, libei,
or CDP. It is test metadata, not a request parameter.
The catalog is the machine-readable inventory. Contributor documentation and the coverage table are generated from it or checked against it. There is no second matrix file to keep in sync.
Use one schema version and these independent fields:
| Field | Meaning |
|---|---|
cell_id | Stable case id |
platform, display_server | OS and Win32/Quartz/X11/Wayland environment |
harness, toolkit | Electron, Tauri, WPF, WinUI3, WebView2, AppKit, SwiftUI, GTK3 |
action, targeting, delivery, scope | Contract dimensions |
driver_route | Backend path covered by the cell |
expected_behavior | DELIVER or REFUSE |
test_status | PASS, FAIL, SKIP, or ENVIRONMENT_ERROR |
observed_behavior | DELIVERED, REFUSED, NO_EFFECT, ERROR, or NOT_RUN |
refusal_code | Structured code when observed behavior is REFUSED |
oracles | App state and attached desktop observations |
known_issue | Optional issue id; it never changes a failure to a pass |
evidence | Video, trajectory, screenshots, structured state, and log paths |
This removes the ambiguous EXPECTED_REFUSAL status. A refusal contract passes
only when expected_behavior=REFUSE, observed_behavior=REFUSED, the code is
allowed, and all no-side-effect oracles pass. Reports count delivered and
refused passes separately.
Do not restore the full Cartesian product automatically. Select cells by driver route:
equivalent_to cell or an unsupported
contract reason.The shared catalog currently declares 40 cells per harness application, or 80 cells across Electron and Tauri per platform. Add a missing combination when it reaches a different driver route. Remove a combination only when another cell proves the same route with an equal or stronger oracle.
Add one testkit interface that snapshots desktop state before and after an operation:
DesktopObservation
foreground window
target z-order and minimized state
focus-change journal
real cursor position
optional leaked-input journal
The testkit DesktopObserver provides this interface with native Windows,
macOS, and Linux backends.
Attach the observer to:
For successful background delivery, the cell requires both target-state change and unchanged desktop invariants. For refusal, the cell requires no target change and unchanged desktop invariants. A focus-only pass never proves input delivery.
Use these boundaries:
Each fixture reset must return a generation token. The next cell verifies the new token and clean marker state before acting. Until a harness has this reset contract, keep process-per-cell isolation.
Video remains required for every canonical E2E cell. Test video capture once in the lane preflight. A recorder failure aborts the lane before the case catalog runs, rather than generating the same permission failure for every cell.
Each OS runner performs one preflight and emits one environment record.
Common checks:
ffprobe;Platform checks:
| Platform | Required preflight |
|---|---|
| Windows | Non-Session-0 interactive desktop, input desktop, foreground sentinel, FFmpeg, UIA visibility |
| macOS | App-bundle daemon identity, live socket, Accessibility, Screen Recording, fixture window visibility |
| Linux X11 | X server, DBus, AT-SPI, window manager, capture, input backend |
| Linux Wayland | Compositor, DBus, AT-SPI, portal/capture path, libei or declared refusal path |
Canonical invocations set strict mode. Missing required capabilities produce
ENVIRONMENT_ERROR; they never return from a test as a pass.
| Current file | Final owner or action |
|---|---|
cross_platform_behavior_test.rs | Shared Electron/Tauri case catalog and external fixture-state and desktop-side-effect oracles |
harness_wpf_test.rs | WPF-specific rows using the common case/result runner |
harness_winui3_test.rs | WinUI3-specific rows; keep only toolkit-distinct behavior |
harness_web_test.rs | WebView2 and Page/CDP behavior; do not mix Page targeting with AX/PX labels |
harness_appkit_test.rs | Canonical AppKit rows, including foreground/background AX scroll and the exact PX background-drag refusal |
harness_swiftui_test.rs | Canonical SwiftUI tree/capture, background action, and popover-trigger rows; transient-panel enumeration remains a separate gap |
harness_gtk3_test.rs | Minimal GTK3/AT-SPI rows for X11 and Wayland |
| Legacy Windows UX guard target | Deleted after typed launch, cursor, shared, capture, and desktop-scope owners passed the replacement audit |
modality_input_e2e_test.rs | Deleted; shared cells own web actions and the Notepad row had no delivery oracle |
modality_background_test.rs | Deleted; typed WPF background action rows and capture ownership passed the replacement audit |
capture_contract_test.rs | Sole owner for tree/image inclusion behavior; canonical prerequisites fail instead of skipping |
desktop_scope_<os>_test.rs | Platform-specific window/desktop scope contracts |
modality_focus_test.rs | Deleted; shared click/type cells own focus preservation and launch focus has its typed action owner |
installed_app_launch_macos_test.rs | Supporting typed Calculator/TextEdit launch and focus rows in the canonical logged-in macOS lane |
installed_app_textedit_macos_test.rs | Supporting typed TextEdit AX integration in the canonical logged-in macOS lane; schema assertions remain in protocol tests |
harness_libreoffice_test.rs | Optional installed-app lane; exclude from the canonical run and counts |
protocol_*, schema, transport tests | Unit/protocol gate; no desktop video and no behavioral matrix rows |
tests/fixtures/shared/scenarios.json | Prune only after selector and marker-reference audit |
The contributor invocation stays selector-free. Lane selection remains private CI plumbing and diagnostic state.
CI may fan the complete matrix into shared, native, and capture/scope jobs. Those are execution partitions, not alternate public test suites.
Rust emits one record for every declared cell. A shared Rust reporter then:
Do not parse test ... ok lines to create behavioral rows. Cargo/JUnit output
may still provide failure annotations for unit tests.
Upload one artifact archive per internal lane, with one stable evidence
subdirectory per behavioral cell. Each GitHub summary row links its exact
video path text to the owning lane archive. The adjacent trajectory.json
path remains in results.jsonl and the archive. GitHub cannot deep-link to a
file inside an archive; the exact path keeps the row unambiguous without
multiplying artifact uploads by the cell count.
CaseSpec, result enums, refusal-code enums, and one schema version.Exit: a missing session, fixture, permission, or recorder fails once as an environment error, and a synthetic CaseSpec set renders a valid report.
Exit: no fake drag pass, no arbitrary error accepted as refusal, no shared legacy test, and one result/evidence bundle per shared cell. This source convergence is complete; platform delivery defects remain visible as cell failures.
Exit: Windows has no guard/modality family, and every background cell has both target-state and desktop-side-effect evidence.
Exit: native cells emit the same records as shared cells, X11 and Wayland are separate dimensions, and macOS failures distinguish TCC from driver behavior.
Exit: no canonical cell depends on VM-specific coordinates, fixed shared ports, or an unexplained sleep.
install-local and TCC preflight.#1922 closes.Exit: every declared cell is delivered, refused according to contract, or fails with a linked unresolved bug. Environment failures are separate.
A test or fixture path may be deleted only when:
#[should_panic] known-gap E2E test remains.