Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e425fac
Call every adapter, with a stub grown from the callee's own contract
JE-Chen Aug 23, 2026
4ec0c03
Resolve the swept module path against the package, not just "not stdlib"
JE-Chen Aug 23, 2026
79dc259
Measure coverage with the WebRTC extra, so the subsystem is measured …
JE-Chen Aug 23, 2026
e2a7a58
Sweep a third registry, and reach the callees the contract hid behind
JE-Chen Aug 23, 2026
0498a86
Test what the trust store decides while nobody is watching
JE-Chen Aug 23, 2026
b0d6d5f
Test the auth boundary and the link arithmetic, and raise the floor t…
JE-Chen Aug 23, 2026
9cfbebf
Record the WebRTC decision, the new floor, and where the last five po…
JE-Chen Aug 23, 2026
5ba623c
Test the file channel and the media renegotiation the extra just unlo…
JE-Chen Aug 23, 2026
abb29fa
Ratchet the floor to 75 and re-read the matrix after the WebRTC tests
JE-Chen Aug 23, 2026
81ca550
Test the last five WebRTC modules, which never needed the split
JE-Chen Aug 23, 2026
dc3c61d
Test the window backends, which were never Linux-only to test
JE-Chen Aug 23, 2026
090c5c0
Test the accessibility backends, and fix 37 guards that caught nothing
JE-Chen Aug 23, 2026
3fbc0a2
Test the hotkey backends, and stop X11 leaking the key you rebound
JE-Chen Aug 24, 2026
2c947ba
Record where the coverage climb stands: 82.40 locally, floor waits on CI
JE-Chen Aug 24, 2026
fc50ffb
Read the AX value constants from the framework that declares them
JE-Chen Aug 24, 2026
7b98e8a
Raise the fixture's error through a parameter, not a dict subscript
JE-Chen Aug 24, 2026
fdf40d5
Build the real object when an adapter's callee is a class
JE-Chen Aug 24, 2026
5efb03e
Stop what the sweep starts, and refuse to leave a thread behind
JE-Chen Aug 24, 2026
682cec6
Patch the bridge on the module that actually reads it
JE-Chen Aug 24, 2026
0e8e25b
Raise the floor to 81, which meets the target set on 2026-08-23
JE-Chen Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,20 @@ jobs:
# snapshot of `je_auto_control/` straight into site-packages and masks
# the editable install for any sub-package the snapshot doesn't include
# (admin, usb, remote_desktop, vision, …).
- name: Install the project itself
#
# `[webrtc]` is part of the install and is load-bearing for what this job
# measures. Eleven modules under `utils/remote_desktop` raise ImportError
# at module level without `aiortc`/`av` — 2,090 statements that were a
# hard 0% here no matter what anyone wrote. Worse than the number: the
# tests that cover the WebRTC host's auth, TLS, tokens and file transfer
# were already written and `importorskip`ped straight past on every
# square, so they ran on developer machines and nowhere else. Measured on
# this tree, one variable changed: 513 of those statements are covered by
# tests that exist today. The extra is NOT added to `typing-stable-api`
# below — that gate must not depend on what is installed.
- name: Install the project itself, with the WebRTC extra
shell: bash
run: pip install -e . # NOSONAR githubactions:S8544 githubactions:S8541 # reason: installs the checked-out project itself, so there is no upstream version to lock and no third-party setup script to run
run: pip install -e ".[webrtc]" # NOSONAR githubactions:S8544 githubactions:S8541 # reason: installs the checked-out project itself, so there is no upstream version to lock and no third-party setup script to run

- name: Install the test tooling
shell: bash
Expand Down
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ only when documented here with a migration path.

### Added

- **`ac_rrule_next`, `ac_rrule_occurrences` and `ac_format_date` now declare
the string format they parse.** Their `dtstart` / `now` / `value` properties
carry `"format": "date-time"` (or `"date"`) in the tool's input schema, which
the descriptions already said in prose and the schema did not. A client
generating values from the schema alone used to produce a plain string and
get a `ValueError` out of `datetime.fromisoformat`.

- **Windows on arm64 installs.** `opencv-python`, `cryptography` and
`je_open_cv` now carry the environment marker
`sys_platform != 'win32' or platform_machine != 'ARM64'`, because none of
Expand Down Expand Up @@ -380,6 +387,47 @@ only when documented here with a migration path.

### Fixed

- **Changing a hotkey's combo on X11 left the old key grabbed for the life of
the daemon.** `LinuxHotkeyBackend._sync_one` dropped the previous
registration from its own table without calling `ungrab_key`, so the *old*
combo stayed grabbed on the X server: it was swallowed from every
application, fired nothing, and `_ungrab_all` could not release it at
shutdown because it no longer knew about it. Rebinding `ctrl+alt+k` to
something else made `ctrl+alt+k` dead system-wide until the process exited.
The Windows backend has always unregistered at the same point; the X11 one
now does too. Unaffected on Windows and macOS.

- **A window closing mid-call let a COM error escape every Windows
accessibility read.** `comtypes` reports a provider failure as `COMError`,
which derives straight from `Exception` — the reason
`windows_query._uia_errors()` exists — but only the two tree-walking guards
in `backends/windows_backend.py` used that tuple. The other 37, covering
every control pattern (`get_value`, `invoke`, `toggle`, `read_table`, the
text and grid reads, …), named `(OSError, AttributeError, …)` and therefore
contained none of them. An application that stopped responding, or a window
that closed between the search that found an element and the call that read
it, raised `COMError` out of the `ac_*` tool or `AC_*` command instead of
answering `None` / `False` / `[]`, and past the executor's
`AutoControlException` boundary. All 37 now use the same tuple. This only
widens what is caught: no call that used to succeed behaves differently.

- **The WebRTC viewer ended every clean disconnect with an unhandled task
exception.** `WebRTCDesktopViewer._consume_video` caught
`(OSError, RuntimeError)`, but aiortc signals the end of a track by raising
`MediaStreamError`, which derives straight from `Exception` and so matched
neither. Nothing awaits that task, so the normal end of a session — the host
stopping its screen share, or the connection closing — reached the console as
asyncio's "Task exception was never retrieved" traceback instead of the
"video stream ended" line the host's own drain loop already logged. The
stream is unaffected either way; only the logging changes.

- **A `null` in a remote-desktop entry's `tags` became a tag named `"None"`.**
`AddressBook.set_tags()` cleaned its input with `str(t).strip()`, and
`str(None)` is the non-empty string `"None"`, so a JSON `null` in the array —
what a client sends for an omitted tag — was stored as a tag and then listed
by `all_tags()` alongside the real ones. Nulls are now dropped. Tags that
were already stored this way stay until the entry's tags are set again.

- **Typing text through the key-event route raised `AttributeError` on the
three platforms that cannot do it.** `type_unicode_keys()` (and
`AC_type_unicode_keys` / `ac_type_unicode_keys`) called the backend's
Expand Down
9 changes: 8 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Layering: entry points (`cli.py`, `gui/`, socket / REST / MCP servers) → execu

```bash
pip install -r dev_requirements.txt # dev deps
pip install -e .[gui] # + GUI extra
pip install -e .[gui,webrtc] # + GUI and WebRTC extras
python -m pytest test/unit_test/headless # headless unit tests
python -m pytest test/integrated_test/ # cross-module workflows
python -m coverage run -m pytest # the suite WITH coverage (see below)
Expand All @@ -41,6 +41,13 @@ have their import-time lines recorded as never executed: measured, that is
suite). `test/unit_test/headless/test_coverage_measurement.py` holds CI to the
correct spelling.

**Measure it with the `[webrtc]` extra installed**, which is why it is in the
line above. Eleven modules under `utils/remote_desktop` raise `ImportError` at
module level without `aiortc`/`av` — 2,090 statements, about 4 points — and the
tests covering the WebRTC host's auth, TLS, tokens and file transfer
`importorskip` straight past. `quality.yml` installs the extra so the floor is
measured against the same tree a developer sees.

`pyproject.toml` pins `python_files = ["test_*.py"]` on purpose: the `*_test.py` files under `test/unit_test/` are manual demo scripts whose module bodies drive the real mouse and keyboard on import. Never loosen that setting.

## Feature Delivery Rules
Expand Down
Loading
Loading