Treat bare desktop canvas as ready
Allow the Linux Desktop state collector to report a healthy canvas when XFCE has no active application window, as long as the display, visible windows, and screenshots are available. Document the readiness rule in the linux-desktop skill and add regression coverage for the bare-desktop active_window=null case.
Alessandro committed
May 10, 2026 at 00:07 UTC
854a96e88046ceeb4565aa9b0448aec76f71401f
3 files changed
+57
-1
plugins/_desktop/helpers/desktop_state.py
-1
@@ -372,7 +372,6 @@ def collect_active_window(env: dict[str, str], capabilities: dict[str, str], err
372
return None
373
result = run([capabilities["xdotool"], "getactivewindow"], env=env, timeout=3)
374
if result.returncode != 0:
375
- errors.append(command_output(result) or "xdotool could not read the active window.")
375
return None
376
window_id = result.stdout.strip().splitlines()[0] if result.stdout.strip() else ""
377
if not window_id:
plugins/_desktop/skills/linux-desktop/SKILL.md
+2
@@ -60,6 +60,8 @@ $DESKTOP key ctrl+s
60
61
The script targets the persistent `agent-zero-desktop` X display, sets `DISPLAY`, `XAUTHORITY`, and `HOME` to the XFCE profile, then uses `xdotool` for input. Startup normally prepares this session. If `check` fails during explicit Desktop work, report that the Desktop runtime is not ready instead of installing packages ad hoc.
62
63
+If `observe --json --screenshot` shows a reachable display, visible Desktop/window entries, and a fresh screenshot, the Desktop is usable even when `active_window` is `null`; a bare XFCE desktop can have no active application window. Treat missing screenshots, missing display, or unavailable `xdotool`/`xwd` as blockers and stop with the specific readiness message instead of repeating clicks or inventing a fallback.
64
+
65
For direct app launches without coordinates:
66
67
```bash
tests/test_office_desktop_state.py
+55
@@ -92,6 +92,61 @@ def test_desktop_state_collects_x11_state_from_mocked_tools(tmp_path, monkeypatc
92
assert [window["title"] for window in state["windows"]] == ["LibreOffice Calc", "Terminal"]
93
94
95
+def test_desktop_state_allows_missing_active_window_when_display_is_reachable(tmp_path, monkeypatch):
96
+ session_dir = tmp_path / "sessions"
97
+ profile_dir = tmp_path / "profiles" / desktop_state.SESSION_ID
98
+ session_dir.mkdir(parents=True)
99
+ profile_dir.mkdir(parents=True)
100
+ (session_dir / f"{desktop_state.SESSION_ID}.json").write_text(
101
+ '{"display": 120, "profile_dir": "%s"}' % profile_dir,
102
+ encoding="utf-8",
103
+ )
104
+
105
+ monkeypatch.setattr(desktop_state, "SESSION_DIR", session_dir)
106
+ monkeypatch.setattr(desktop_state, "PROFILE_DIR", tmp_path / "profiles")
107
+ monkeypatch.setattr(desktop_state.shutil, "which", lambda name: f"/usr/bin/{name}")
108
+
109
+ def fake_run(command, **kwargs):
110
+ del kwargs
111
+ name = Path(command[0]).name
112
+ if name == "xrandr":
113
+ return _completed(command, stdout="Screen 0: current 543 x 792, maximum 1920 x 1080\n")
114
+ if name == "xdotool" and command[1:3] == ["getmouselocation", "--shell"]:
115
+ return _completed(command, stdout="X=43\nY=244\nSCREEN=0\nWINDOW=18874412\n")
116
+ if name == "xdotool" and command[1] == "getactivewindow":
117
+ return _completed(
118
+ command,
119
+ returncode=1,
120
+ stderr="XGetWindowProperty[_NET_ACTIVE_WINDOW] failed (code=1)\n",
121
+ )
122
+ if name == "xdotool" and command[1] == "search":
123
+ return _completed(command, stdout="18874412\n")
124
+ if name == "xdotool" and command[1] == "getwindowname":
125
+ return _completed(command, stdout="Desktop\n")
126
+ if name == "xwininfo":
127
+ return _completed(
128
+ command,
129
+ stdout=(
130
+ " Absolute upper-left X: 0\n"
131
+ " Absolute upper-left Y: 0\n"
132
+ " Width: 543\n"
133
+ " Height: 792\n"
134
+ ),
135
+ )
136
+ if name == "xprop":
137
+ return _completed(command, stdout='WM_CLASS(STRING) = "xfdesktop", "Xfdesktop"\n')
138
+ raise AssertionError(f"unexpected command: {command}")
139
+
140
+ monkeypatch.setattr(desktop_state.subprocess, "run", fake_run)
141
+
142
+ state = desktop_state.collect_state()
143
+
144
+ assert state["ok"] is True
145
+ assert state["active_window"] is None
146
+ assert state["errors"] == []
147
+ assert [window["title"] for window in state["windows"]] == ["Desktop"]
148
+
149
+
150
def test_desktop_state_screenshot_capture_uses_xwd_and_pillow_when_available(tmp_path, monkeypatch):
151
monkeypatch.setattr(desktop_state, "SCREENSHOT_DIR", tmp_path)
152
capabilities = {"xwd": "/usr/bin/xwd"}