Skip to content

test(cua-driver): cross-platform capture_mode matrix coverage + modality docs - #2053

Merged
f-trycua merged 3 commits into
mainfrom
test/modality-matrix-capture-mode-coverage
Jun 27, 2026
Merged

test(cua-driver): cross-platform capture_mode matrix coverage + modality docs#2053
f-trycua merged 3 commits into
mainfrom
test/modality-matrix-capture-mode-coverage

Conversation

@f-trycua

@f-trycua f-trycua commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

What

Two halves of one ask: document the modality test matrix and cover it across platforms.

Docs (fumadocs)

New explanation page Capture and Dispatch Modalities (docs/content/docs/explanation/capture-and-dispatch-modalities.mdx), wired into the Explanation nav + index. It documents the three independent axes of every action and the validity matrix:

  • capture_modeax / vision / som (what the agent observes)
  • dispatchbackground (default) / foreground (how input lands)
  • capture_scopewindow (default) / desktop (the coordinate space)

Validity: window scope supports any combination; desktop scope is vision + foreground only (no window handle → no AX tree → vision; screen-absolute input → must own the desktop → foreground). The two rejected combinations (desktop+ax/background, and window-less click under window scope → desktop_scope_disabled) are called out, with an honest per-platform support table.

Tests

New modality_capture_mode_test — the cross-platform spine of the capture_mode axis. It drives each platform's native controlled harness (WPF / AppKit / GTK3) and asserts:

capture_mode oracle
ax accessibility tree present (btn-increment), no image
vision screenshot present, no tree
som both (the default)

The oracle reads the canonical structured fields (tree_markdown, screenshot_png_b64) so it is transport-agnostic. A bounded settle-retry handles cold-start AX registration, and a pid-diff new-instance window resolver keeps the three tests deterministic under --test-threads=1. Missing OS grants (Screen Recording / Accessibility) skip-with-note rather than false-fail.

This closes the audited "capture_mode only on Windows" gap — the macOS/Linux native vision path was previously untested.

TEST_SUITE.md gains a per-platform modality-matrix coverage table with an honest remaining-gaps note.

Verification — runtime-green with real assertions on all three platforms

Platform Harness Result
macOS AppKit ax tree-only · vision image-only · som both; 3× deterministic
Linux (Ubuntu 24.04) GTK3 + live AT-SPI ✅ all 3 assertions exercised; pyatspi-confirmed
Windows WPF + UIA (Session 2) ✅ all 3 assertions; test result: ok. 3 passed; finished in 6.01s

See thread comments for the full per-platform run logs.

Honest remaining gaps (documented, not closed here)

  • window/vision/foreground has no explicit test on any platform.
  • The desktop-scope window-less actuator is Windows-only (macOS click still requires pid), so the desktop cell and its negative gate stay Windows-only until that loop lands on macOS/Linux.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Added a new explanation page covering capture and dispatch options, supported combinations, and platform-specific behavior.
    • Updated the explanation index to include the new topic.
    • Refreshed test-suite documentation with a clearer coverage matrix and current support notes.
  • Tests

    • Added a cross-platform integration test for capture mode behavior across Windows, macOS, and Linux.

Add `modality_capture_mode_test` — the cross-platform spine of the
`capture_mode` axis (ax / vision / som). It drives each platform's native
controlled harness (WPF / AppKit / GTK3) and asserts the contract:

  ax     → accessibility tree present (btn-increment), no image
  vision → screenshot present, no tree
  som    → both (the default)

The oracle reads the canonical structured fields (`tree_markdown`,
`screenshot_png_b64`) so it is transport-agnostic, with a bounded settle-retry
for cold-start AX registration and a new-instance window resolver (pid-diff)
so the three tests stay deterministic under `--test-threads=1`. Vision/AX
grants that are absent skip-with-note rather than false-fail.

This closes the audited "capture_mode only on Windows" gap (the macOS/Linux
native vision path was previously untested). Verified green on macOS against
the AppKit harness; Windows/Linux run via the same `#[ignore]` lane on a VM.

Docs: new explanation page `capture-and-dispatch-modalities.mdx` documenting
the three modality axes (capture_mode × dispatch × capture_scope) and the
validity matrix (window scope = any combo; desktop = vision + foreground only),
wired into the Explanation nav + index. TEST_SUITE.md gains a per-platform
modality-matrix coverage table with an honest remaining-gaps note
(window/vision/foreground untested; desktop-scope actuator Windows-only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jun 27, 2026 12:31am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4bdd3073-f53e-44be-8345-abccbd7afd97

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Added a new capture-and-dispatch modalities explanation page, linked it from the explanation index, and updated modality coverage notes. Added a cross-platform Rust integration test that checks capture-mode output across ax, vision, and som.

Changes

Capture modality docs and test coverage

Layer / File(s) Summary
Explanation docs and navigation
docs/content/docs/explanation/capture-and-dispatch-modalities.mdx, docs/content/docs/explanation/index.mdx, docs/content/docs/explanation/meta.json
Adds the new modalities explanation page and includes it in the explanation section navigation metadata.
Shared modality test harness
libs/cua-driver/rust/crates/cua-driver/tests/modality_capture_mode_test.rs
Adds cross-platform harness launch, image/tree detection helpers, snapshot retry logic, and window resolution for the modality test.
Capture-mode assertions and coverage notes
libs/cua-driver/rust/crates/cua-driver/tests/modality_capture_mode_test.rs, libs/cua-driver/test-harness/TEST_SUITE.md
Adds ignored ax, vision, and som assertions and updates the test-suite matrix for modality coverage and remaining gaps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • trycua/cua#2038: Adds modality coverage and a desktop_scope_disabled gate in the same area of capture-mode behavior.
  • trycua/cua#2043: Updates get_window_state capture-mode assertions with related protocol-level test coverage.

Poem

🐰 I hop through modes by moonlit light,
ax, vision, som all feel just right.
With trees and images set in view,
I nibble docs and tests anew.
Hop, hop — the capture path shines bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly reflects the main changes: cross-platform capture_mode test coverage and new modality documentation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/modality-matrix-capture-mode-coverage

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/content/docs/explanation/capture-and-dispatch-modalities.mdx`:
- Line 30: The internal docs links in capture-and-dispatch-modalities.mdx are
using the wrong route prefix and do not resolve in CI. Update the broken
`/docs/...` links at the referenced places to match the site’s existing docs
route shape, using the same pattern as the other navigation links (for example
`/explanation/...`). Keep the changes limited to the affected markdown links in
this doc.

In `@libs/cua-driver/rust/crates/cua-driver/tests/modality_capture_mode_test.rs`:
- Line 1: The new test file is missing the project’s SPDX license header. Add
the standard SPDX header at the very top of modality_capture_mode_test.rs before
the existing module/doc comment, matching the header style used by other files
under libs/cua-driver so CI’s SPDX check passes.

In `@libs/cua-driver/test-harness/TEST_SUITE.md`:
- Line 57: The family inventory count is stale after adding
modality_capture_mode_test, so the section header in TEST_SUITE.md should be
updated to match the new total. Adjust the “Inventory by family” heading to
reflect 19 files/rows, keeping the table count consistent with the entry list
and the modality_capture_mode_test row.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e528fc01-b305-441f-b45d-eed171e77312

📥 Commits

Reviewing files that changed from the base of the PR and between 8e2de25 and 97bf3ca.

📒 Files selected for processing (5)
  • docs/content/docs/explanation/capture-and-dispatch-modalities.mdx
  • docs/content/docs/explanation/index.mdx
  • docs/content/docs/explanation/meta.json
  • libs/cua-driver/rust/crates/cua-driver/tests/modality_capture_mode_test.rs
  • libs/cua-driver/test-harness/TEST_SUITE.md


| `dispatch` | Behavior |
|---|---|
| `background` (default) | Input is routed to the target process/window/element directly. The user's frontmost app, real cursor, and window z-order are untouched. This is the [no-foreground contract](/docs/explanation/the-no-foreground-contract). |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the internal doc routes.

Line 30, Line 69, and Line 73 link to /docs/... URLs that do not resolve, and docs CI is already failing on them. These should use the same route shape as the rest of the docs navigation (for example /explanation/... instead of /docs/explanation/...).

Suggested patch
-| `background` (default) | Input is routed to the target process/window/element directly. The user's frontmost app, real cursor, and window z-order are untouched. This is the [no-foreground contract](/docs/explanation/the-no-foreground-contract). |
+| `background` (default) | Input is routed to the target process/window/element directly. The user's frontmost app, real cursor, and window z-order are untouched. This is the [no-foreground contract](/explanation/the-no-foreground-contract). |
@@
-| `dispatch: background` (the contract) | ✅ | ✅ | ✅ (X11/AT-SPI; native Wayland input is a [known gap](/docs/explanation/the-no-foreground-contract#linux)) |
+| `dispatch: background` (the contract) | ✅ | ✅ | ✅ (X11/AT-SPI; native Wayland input is a [known gap](/explanation/the-no-foreground-contract#linux)) |
@@
-`capture_mode` is recognized everywhere, so window-scope automation — the four window-scope rows of the matrix — works on all three platforms. The desktop-scope loop (`get_desktop_state` plus window-less screen-absolute input via hit-testing) is complete on Windows and rolling out to macOS and Linux; on those platforms a window-less action under window scope is still rejected. See the [MCP tool reference](/docs/reference/cua-driver/mcp-tools) for per-tool parameters and the [no-foreground contract](/docs/explanation/the-no-foreground-contract) for how background dispatch is implemented on each OS.
+`capture_mode` is recognized everywhere, so window-scope automation — the four window-scope rows of the matrix — works on all three platforms. The desktop-scope loop (`get_desktop_state` plus window-less screen-absolute input via hit-testing) is complete on Windows and rolling out to macOS and Linux; on those platforms a window-less action under window scope is still rejected. See the [MCP tool reference](/reference/cua-driver/mcp-tools) for per-tool parameters and the [no-foreground contract](/explanation/the-no-foreground-contract) for how background dispatch is implemented on each OS.

Also applies to: 69-69, 73-73

🧰 Tools
🪛 GitHub Actions: CI: Check Docs Links / 0_Check Internal Links (next-validate-link).txt

[error] 30-30: docs:check-links (tsx scripts/check-links.ts) failed: Invalid URL/not-found reference at /docs/explanation/the-no-foreground-contract.

🪛 GitHub Actions: CI: Check Docs Links / Check Internal Links (next-validate-link)

[error] 30-30: docs:check-links reported invalid URL: /docs/explanation/the-no-foreground-contract not-found at content/docs/explanation/capture-and-dispatch-modalities.mdx:30:176

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/content/docs/explanation/capture-and-dispatch-modalities.mdx` at line
30, The internal docs links in capture-and-dispatch-modalities.mdx are using the
wrong route prefix and do not resolve in CI. Update the broken `/docs/...` links
at the referenced places to match the site’s existing docs route shape, using
the same pattern as the other navigation links (for example `/explanation/...`).
Keep the changes limited to the affected markdown links in this doc.

Source: Pipeline failures

@@ -0,0 +1,257 @@
//! modality_capture_mode_test — the **capture_mode** axis (`ax` / `vision` /

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the SPDX header to this new file.

CI's SPDX check is failing for files under libs/cua-driver; as a newly added source file, this one should carry the project's standard SPDX header at the top to avoid adding to the warn-only violation count.

🧰 Tools
🪛 GitHub Actions: CI: SPDX Headers / 0_Check SPDX headers (warn-only).txt

[error] 1-1: spdx-headers.py --check failed: 349 file(s) missing SPDX header under /home/runner/work/cua/cua/libs/cua-driver. Process completed with exit code 1.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/cua-driver/rust/crates/cua-driver/tests/modality_capture_mode_test.rs`
at line 1, The new test file is missing the project’s SPDX license header. Add
the standard SPDX header at the very top of modality_capture_mode_test.rs before
the existing module/doc comment, matching the header style used by other files
under libs/cua-driver so CI’s SPDX check passes.

Source: Pipeline failures

| | `harness_appkit_test` | mac | AppKit: AX tree, AXPress, NSTextField, NSScrollView, NSMenu |
| | `harness_swiftui_test` | mac | SwiftUI: AX tree, `.popover()` |
| **modality_** | `modality_background_test` | win | background-modality / no-focus-steal sentinel + `capture_mode` ax/vision/som |
| | `modality_capture_mode_test` | mac+win+linux | `capture_mode` axis on each native harness: `ax`→tree-only, `vision`→image-only, `som`→both |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the inventory count.

Adding modality_capture_mode_test makes the family table 19 rows, but the section header still reads "Inventory by family (18 files)". Bump it to 19 to keep the count accurate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/cua-driver/test-harness/TEST_SUITE.md` at line 57, The family inventory
count is stale after adding modality_capture_mode_test, so the section header in
TEST_SUITE.md should be updated to match the new total. Adjust the “Inventory by
family” heading to reflect 19 files/rows, keeping the table count consistent
with the entry list and the modality_capture_mode_test row.

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Linux visual regression artifacts

Matrix jobs now run independently. Download visual artifacts from this workflow run.
Each background-GUI job uploads a .gif of the interaction plus two annotated PNGs (<app>.png raw, <app>-atspi.png with AT-SPI element boxes); the cua-driver-linux-som-overlays artifact adds <app>-som.png cua Set-of-Marks overlays:

  • cua-driver-linux-cursor-click-gif
  • cua-driver-linux-background-terminal-gif
  • cua-driver-linux-parallel-drag-xserver
  • cua-driver-linux-background-gui-chromium
  • cua-driver-linux-background-gui-tk
  • cua-driver-linux-background-gui-gtk3-gedit
  • cua-driver-linux-background-gui-gtk3-mousepad
  • cua-driver-linux-background-gui-gtk3-scite
  • cua-driver-linux-background-gui-gtk4-characters
  • cua-driver-linux-background-gui-qt5-manuskript
  • cua-driver-linux-background-gui-qt5-klog
  • cua-driver-linux-background-gui-qt5-openambit
  • cua-driver-linux-background-gui-qt6-kate
  • cua-driver-linux-background-gui-qt6-kcalc
  • cua-driver-linux-background-gui-qt6-okular
  • cua-driver-linux-background-gui-qt6-qownnotes
  • cua-driver-linux-background-gui-electron-zettlr
  • cua-driver-linux-background-gui-electron-joplin
  • cua-driver-linux-background-gui-electron-logseq
  • cua-driver-linux-som-overlays

Open workflow run and download artifacts

Linux VM verification surfaced a cold-start race: a freshly launched GTK3
harness needs the AT-SPI atk-bridge to register on the a11y bus, which on a
cold bus can take several seconds — longer than the prior 6×400ms (~2.4s)
settle budget, so the first cold run env-skipped the `ax`/`som` tree assertions
(still green, but not exercised). Widen to 16×500ms (~8s) so the tree-bearing
modes reliably exercise their assertions on cold CI boots. No added latency
when the tree is present immediately (macOS stays ~2.4s).

Cross-platform verification of this test: macOS (AppKit) and Linux (GTK3,
Ubuntu 24.04) both runtime-green with real assertions; Windows compiles +
graceful-skips under Session 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@f-trycua

Copy link
Copy Markdown
Collaborator Author

Cross-platform verification complete

Ran the new modality_capture_mode_test on real desktops across all three platforms:

Platform Harness Result
macOS AppKit runtime-greenax→tree-only, vision→image-only, som→both; 3× deterministic
Linux (Ubuntu 24.04, GTK3) GTK3 + live AT-SPI runtime-green — all 3 assertions genuinely exercised; harness btn-increment independently confirmed via pyatspi; real PNG captured
Windows (VM) WPF compiles clean; skips gracefully under Session 0 (no desktop). Runtime assertions not exercised — the interactive desktop (Session 2) is a different user than the repo owner; out of verify-only scope

So the oracle is runtime-proven on 2 of 3 platforms with real assertions, and compile + graceful-skip verified on Windows.

Follow-ups surfaced during verification

@f-trycua

Copy link
Copy Markdown
Collaborator Author

Windows runtime-green ✅ — all three platforms now verified with real assertions

Completed the real Windows run in an interactive Session 2 desktop (user fbonacci, STATE Active) via an interactive-token scheduled task. The test exe drove the WPF harness through real UIA:

test capture_mode_ax_returns_tree_only      ... ok  (✅ tree present (btn-increment), no image)
test capture_mode_som_returns_tree_and_image ... ok  (✅ tree=true image=true)
test capture_mode_vision_returns_image_only  ... ok  (✅ image present, no tree)
test result: ok. 3 passed; 0 failed; finished in 6.01s

Final verification matrix

Platform Harness Run
macOS AppKit ✅ real assertions
Linux (Ubuntu 24.04) GTK3 + live AT-SPI ✅ real assertions
Windows WPF + UIA (Session 2) real assertions

The earlier Session-0 skip was just the SSH session having no desktop; the cross-user ACL concern did not actually block running the cuademo-profile exe as fbonacci.

New reference page `reference/cua-driver/contracts.mdx` — the cross-cutting
contract map behind the two surfaces: CLI vs MCP transport state, where
set_config lands (anonymous→disk global vs session→in-memory override), the
capture-scope contract (window needs pid/window_id; desktop is x,y-only,
screen-absolute, skips window_id/list_windows), the action-routing decision
(element → window-local pixel → screen-absolute → desktop_scope_disabled gate),
and per-platform support. Tables + cross-links to Process model and Capture and
dispatch modalities rather than re-explaining them. Wired into the Cua Driver
reference nav.

Also fixes the internal-link convention: `capture-and-dispatch-modalities.mdx`
(and the new page) used a `/docs/...` prefix that doesn't match the rest of the
docs (`/explanation/...`, `/reference/...`). Normalized to the prefix-less form
so the cross-links resolve.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@f-trycua

Copy link
Copy Markdown
Collaborator Author

Added a consolidated Interface Contracts reference page (reference/cua-driver/contracts.mdx) — CLI vs MCP transport state, config persistence (anonymous→disk vs session→memory), the capture-scope contract, and the action-routing decision — wired into the Cua Driver reference nav. Also fixed an internal-link convention slip (/docs/.../...) in the modalities page so its cross-links resolve.

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.

1 participant