Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e08344a
Bar widgets: an unchanged widget is not written again at each apply
gillesgoetsch Oct 29, 2026
822a2d1
Update VM on a locked VM no longer strands Omarchy's lock screen
gillesgoetsch Oct 29, 2026
2fe16f9
Update VM twice on a locked VM: the newest widgets, one shell restart
gillesgoetsch Oct 29, 2026
a08249f
Update VM on a locked VM: the colour fix waits for the unlock in the …
gillesgoetsch Oct 29, 2026
a347293
OmacVM.app: the display widget is not rebuilt under Omarchy's lock
gillesgoetsch Oct 29, 2026
53b0d08
omacvm check: a widget queued under the lock comes right after the un…
gillesgoetsch Oct 29, 2026
cb53385
Docs: no plugin writes or rescans while Omarchy's session is locked
gillesgoetsch Oct 29, 2026
5b8f820
CHANGELOG 3.2.13: an update of a locked VM keeps Omarchy's lock scree…
gillesgoetsch Oct 29, 2026
d5ad6e1
CHANGELOG 3.2.13: the pull request's number
gillesgoetsch Oct 29, 2026
d51d591
plugin-lock.sh: the rescanPlugins guard skips Python's caches
gillesgoetsch Oct 29, 2026
b1fc105
Omanotch: its display panel clone is not rebuilt under Omarchy's lock
gillesgoetsch Oct 29, 2026
23223aa
Control centre or Omanotch off under Omarchy's lock: nothing written …
gillesgoetsch Oct 29, 2026
93c7af6
Docs: feature offs and Omanotch's display panel under Omarchy's lock
gillesgoetsch Oct 29, 2026
036996c
CHANGELOG 3.2.13: feature offs under the lock (#618)
gillesgoetsch Oct 29, 2026
48ca85f
Omanotch: the display panel asks the lock before it tidies a cut-off …
gillesgoetsch Oct 29, 2026
02d21f3
Troubleshooting 46: switching Omanotch off under the lock did the sam…
gillesgoetsch Oct 29, 2026
54bc05e
CHANGELOG 3.2.13: Omanotch off under the lock as measured, and its ne…
gillesgoetsch Oct 29, 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
6 changes: 6 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,9 @@ jobs:
- name: New bar widgets load after Update VM, also after an unlock (Omarchy's lock status, not isLocked)
run: src/tests/restart-shell.sh

- name: Update VM on a locked VM writes no bar widget and asks no rescan until the unlock
run: src/tests/plugin-lock.sh

- name: omacvm check warns when Omarchy does not lock again after an unlock
run: src/tests/lock-stuck-check.sh

Expand Down Expand Up @@ -783,6 +786,9 @@ jobs:
- name: Omarchy's shell colours on Qt 6.12 on Linux (GNU tools, the live shell's log by its pid)
run: src/tests/shell-colors.sh

- name: Bar widgets under Omarchy's lock on Linux (GNU tools, flock, as in the VM)
run: src/tests/plugin-lock.sh

- name: OmacVM's edits of Omarchy's shell on Linux (GNU tools, as in the VM)
run: src/tests/omarchy-shell.sh

Expand Down
23 changes: 22 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ GitHub.
| `src/lib/mac.sh` | Mac helpers: `gssh`, Parallels (`vm_ip` by DHCP lease, `vm_state`, `vm_start`), UTM (`vm_type`, `utm_ip`, `utm_state`, `utm_start`, `utm_wait_stopped`) and Fusion (`fusion_list`: Fusion's `vmInventory`, running VMs and `$FUSION_DIR`, `fusion_ip` from `vmnet-dhcpd-vmnet8.leases`, `fusion_state`, `fusion_start`, `fusion_host`) |
| `src/guest/omacvm-omanotch.service` | The omanotch feature: one-shot user unit that runs `/usr/local/share/omacvm/omanotch/guest/install.sh` in the first desktop session (it needs Hyprland running), skipped once `~/.local/bin/notchcast` exists. `src/guest/install.sh` removes notchcast when that copy changed (stamp `~/.local/state/omacvm/omanotch`), so it installs again |
| `src/omanotch/` | Omanotch (`git subtree`, history kept): `mac/` (Omanotch.app, Swift; `mac/test.sh` = offline tests), `guest/` (`notchcast`, the bar, background and display panel patches, `notchbar.lua`; `guest/tests` = offline tests). The patches know omarchy-mac's shell and upstream Omarchy's newer one (October 2026: notch floor, a bar thickness per screen, BackgroundMedia, `Commons.Color`): where an anchor differs, each form is listed (`replace_variant`); Omarchy's own files of both in `src/tests/omarchy-shell` (also `src/tests/notch-wallpaper.sh`, `src/tests/omarchy-shell.sh`). Toasts under the bar (parked: only the gap; OmacVM.app's FullPanel: the strip's height + the gap): the bar's `barSizeFor` (newer Omarchy asks it), `guest/omanotch-notifications.sh` on an older notification service. The bar and background clones follow Omarchy's updates like Omarchy's own plugins (`guest/copies.py`, installed to `~/.local/lib/omanotch`, run by `install.sh`, the user unit `omanotch-copies.service` at each login and from OmacVM's pacman hook `zz-omacvm-omanotch-copies.hook` after an update): Omarchy's new plugin with the patch again, merged three ways (`git merge-file`) with the clone against what was written last (`~/.local/state/omanotch/copies/<name>/written`), shell lines of shell-colors.py not counted; overlap or a patch that does not fit: the clone stays as it is (check WARN `bar copy`); a clone without a record (3.1.11 and older) is made new once, kept first as `~/.config/omarchy/plugins/.$USER.bar.before-3.1.13` when it differed (WARN with the path for 14 days or until deleted; shell-colors.py skips dot folders); the shell restarts and must load it (no errors on its path, the bar's notchbar IPC) or every changed file goes back (`guest/tests/test_copies.py`). Its own README. Work on it here; github.com/gillesgoetsch/omanotch is archived and points here |
| `src/lib/install-plugin.sh`, `src/lib/omacvm-plugins` | Omarchy shell plugin install; queues until the shell runs (first login); restarts the shell once when a plugin's files changed |
| `src/lib/install-plugin.sh`, `src/lib/omacvm-plugins` | Omarchy shell plugin install; validates the copy first, writes nothing when the folder has it already, queues until the shell runs (first login); restarts the shell once when a plugin's files changed. While the session is locked (`src/guest/session-lock.sh`) the copy waits in `~/.local/state/omacvm/plugins-staged/<id>` and `src/guest/restart-shell.sh`'s job after the unlock puts it in place (section 8). Tests: `src/tests/plugin-lock.sh`, `src/tests/restart-shell.sh` |
| `src/lib/sign.sh` | Signs Mac apps with `designated => identifier "<id>"`, so TCC grants survive rebuilds |
| `src/control/` | Feature `control-centre` (docs/adr/0030-0032): `omacvm` in the VM, a Textual TUI (`omacvm_cc/tui.py`; `state.py` = the pure status rules, `controller.py` = data, `bridge.py` = the client with the Bridge's proof, `report.py` + `collect.py` = report a problem with redaction and a gate, also run by the Mac's `omacvm report` with macOS's python3 3.9, or OmacVM.app's 3.13 on a Mac without the Command Line Tools: keep it 3.9-compatible). `omacvm status [--json]`, `omacvm report`, `omacvm notify` (user timer: one notice per new version unless update checks are off), `omacvm extra-brightness [on\|off] [--json]` (OmacVM.app's Extra brightness over the control port, answered by the app itself: `/omacvm/app/extra-brightness`, `ExtraBrightnessGuest` in `OmacVMWindow/ExtraBrightness.swift`, ADR 0031; only while that VM's window is in front). Guest install (`guest/install.sh`): Textual from OmacVM's own wheels (`vendor/`, pinned with `SHA256SUMS`, unpacked by `omacvm_cc/vendor.py` into the user's `~/.cache/omacvm/python/`; no pacman: a VM older than the mirrors gets every file as a 404), `/usr/local/bin/omacvm`, root socket `omacvm-check.socket` (runs `guest/check.sh --tsv` for the desktop user, nothing read from the caller), desktop entry, a marked row in `~/.config/omarchy/extensions/omarchy-menu.jsonc`, bar plugin `plugins/omacvm.control`. Tests: `tests/` (pytest + Textual Pilot against `tests/fakes.py`), `tests/vm_e2e.py` drives the real TUI in a test VM |
| `src/release/` | Update manifests (ADR 0032): `parts.tsv` (files → parts, first match wins, `core` last), `manifest.py digests|build|parts`, `sign.swift keygen|sign|verify` (Ed25519), `release-key.sh sign|team|teams|spare` (signs with the Keychain key and checks it), `keys.py` (verify on the command line: plain-python Ed25519, either key, kept spares). `omacvm apply` writes the digests it installed to the VM's `/etc/omacvm/installed.json`. Release keys: `src/lib/release-key.pub` (main) and `release-key-spare.pub`, either one signs; documents carry `kind` and `devid_teams`; who holds the private halves and how to rotate: `docs/release-keys.md`. A release: `release.sh` (every step, `--dry-run`, rollback; `docs/releasing.md`). Tests use `OMACVM_FEED_URL` + `OMACVM_FEED_KEY` (Bridge), `OMACVM_APPCAST_KEY` (app test builds), `OMACVM_RELEASE_TEST_KEYS` (command line), all throwaway keys |
Expand Down Expand Up @@ -662,6 +662,7 @@ The less obvious ones, with causes and where the fix lives, are in
| A command replacement in `/usr/local/bin` is ignored by the bar | the Omarchy shell runs with `/usr/share/omarchy/bin` (symlinks to /usr/bin) first on PATH; Hyprland does not | call `/usr/local/bin/...` by full path from QML (see `omacvm.wifiqr`) |
| `Target not found` / "handler will not be used" for a widget's IPC | a clone kept the stock widget's IPC target | give clones their own target (`omacvm.wifi`) |
| Updated widget does not change | a running shell keeps loaded plugins | `src/lib/install-plugin.sh` flags changes; `src/guest/install.sh` restarts the shell (`guest/restart-shell.sh`; locked: right after the unlock) |
| Omarchy's lock screen without a password field after `omacvm update`/`apply` on a locked VM (3.2.12 and older); journal: "Session lock object was destroyed without unlocking", "lock-stranded: recovering", "lock-pending: screen-stabilizing" | omarchy-mac 4.0.3's shell reloads every plugin, its lock screen too, on a write under `~/.config/omarchy/plugins` or a `rescanPlugins` call; the new lock screen never gets the lock Hyprland still holds | shut the VM down and start it again; from 3.2.13 nothing is written or rescanned under the lock (section 8; docs/troubleshooting.md, 46) |
| Disabling an old clone brings the stock widget back next to the new one | `clonedFrom` hand-back | rename ids in `shell.json` instead, or disable the stock widget |
| SSIDs null, `location_authorized: false` | Location Services not granted (new bundle id or reset) | Privacy & Security › Location Services |
| Media keys still show the macOS popup | Accessibility missing (`permissions:` line, `omacvm check`), no VM in front (Parallels/UTM/Fusion: not full screen), or capture switched off in the bridge menu | `media keys:` / `media key ...: to macOS:` lines in the bridge log |
Expand Down Expand Up @@ -766,6 +767,26 @@ The less obvious ones, with causes and where the fix lives, are in
`copies.py` do. Root's `sudo -u` in an apply has no locale: there `qs ipc`
prints Qt's locale warning on stdout before every answer (Omarchy's own
scripts misread it too), so give Omarchy's commands `LC_ALL=C.UTF-8`.
- Do not write under `~/.config/omarchy/plugins` or call `omarchy-shell
shell rescanPlugins` while the session is locked or the shell does not
answer (`src/guest/session-lock.sh`: busy counts as locked). The shell of
omarchy-mac 4.0.3 reloads every plugin on either (an inotify watch on that
folder; hidden entries skipped), its lock screen too, and the new one has
no password field until a VM restart (3.2.11, 3.2.12). Upstream Omarchy
keeps such services over the reload (omacom/omarchy#9485); omarchy-mac
4.0.3 lacks it. Stage the copy outside the folder and let
`guest/restart-shell.sh`'s job put it in place after the unlock, before its
one restart (`lib/install-plugin.sh`, `lib/omacvm-plugins`); `omarchy plugin
validate`, `enable` and `disable` are safe (they read the folder or change
shell.json only). Feature offs under the lock: the control centre goes out
of the bar and its folder stays until the next apply; Omanotch's own
uninstall waits for the unlock (`guest/off.sh`, `guest/restart-shell.sh`);
Omanotch's `display-panel.py` asks Omarchy's lock status as `copies.py`
does. Do not try to recover with a shell restart instead:
`omarchy-restart-shell` refuses while the lock is requested, and a restart
under the lock is what #612 removed. `src/tests/plugin-lock.sh` fails for a
new `rescanPlugins` caller that does not ask first. Not Omarchy's #738 (no
lock after an unlock): that one stays Omarchy's.
- Do not leave files in the desktop user's runtime folder (`/run/user/UID`,
`XDG_RUNTIME_DIR`): it is a tmpfs of 10 % of the VM's memory, and when it is
full, Hyprland and apps die of SIGBUS on their shared-memory buffers there
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,30 @@
What's new in each OmacVM release. The release notes on GitHub say the same
in more words.

## 3.2.13 (unreleased)

- **Updating a running VM whose screen is locked no longer leaves Omarchy's lock screen without
its password field (#617).** `omacvm update` and `omacvm apply` wrote OmacVM's bar widgets again and
asked Omarchy's shell to rescan them. The shell of omarchy-mac 4.0.3 then reloads all its
plugins, its own lock screen too, and under the lock the new lock screen had no password field
until the VM was shut down and started again (3.2.11 and 3.2.12). Now nothing is written there
and nothing is rescanned while the session is locked: changed widgets wait beside it, and right
after the unlock they go in place and the shell restarts once. A widget that did not change is
no longer written again at every update, and two updates under one lock end with the newest
widgets and one restart. Upstream Omarchy fixed the reload itself (omacom/omarchy#9485);
omarchy-mac does not have it yet. Needs only the update to 3.2.13 (`omacvm update`, or the
app's update and Update VM): that update already waits for the unlock.

- **Switching the control centre or Omanotch off while the VM is locked no longer leaves the lock
screen without its password field either (#618).** The control centre leaves the bar at once
and its folder goes at the next update; Omanotch stops at once and removes its copies of
Omarchy's plugins right after the unlock (with 3.2.12 the lock screen stayed without its
password field until the VM was shut down and started again). Omanotch's display panel (UTM,
VMware Fusion, Parallels) is no longer rebuilt under the lock after an Omarchy update; the next
start of Omanotch does it. The update to 3.2.13 installs Omanotch's VM side again (its files
changed), also on a locked VM, and the lock screen keeps working there. Needs the update to
3.2.13.

## 3.2.12

In an OmacVM.app VM, memory the VM gives back now leaves the Mac, and the VM gives back its
Expand Down
39 changes: 39 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ reviews, measuring pitfalls, how the VM apps work inside) are in
redraws once). OmacVM VMs have the idle lock on by default, so an idle VM
stays open while it is stuck. `omacvm check` warns while it is stuck
("Omarchy's lock").
- **The lock screen has no password field after an update of a locked VM**
(`omacvm update` or `omacvm apply` on a running VM whose screen was
locked, 3.2.12 and older): typing the password does nothing. Shut the VM
down and start it again. From 3.2.13 such an update waits for the unlock
(46).
- **Touch ID asks for the password instead**: the line above the password
prompt says why ("Touch ID not available (VM not in front), use your
password", "the Mac is still starting: try again in a moment", ...).
Expand Down Expand Up @@ -1327,3 +1332,37 @@ Findings 7, 11, 12, 13, 16 and 17 are notes for developers now:
`src/app/guest/omacvm-desktop-recover` (the note), `src/cmd/check.sh`;
tests `app/runtime/Tests/vcpu/test-vcpu-stall.sh`, `src/tests/vm-stalls.sh`,
`swift run desktop-tests`; ADR 0060.

## 46. All routes: the lock screen has no password field after an update of a locked VM

- **Symptom:** `omacvm update` or `omacvm apply` ran while the VM's Omarchy
session was locked (3.2.12 and older; also switching Omanotch off). The
lock screen comes back without its password field, and typing the password
does nothing. Omarchy's shell log (`journalctl --user -t omarchy-shell` in
the VM) has "Session lock object was destroyed without unlocking", then
"lock-stranded: recovering" and "lock-pending: screen-stabilizing".
- **Cause:** the shell of omarchy-mac 4.0.3 reloads all its plugins, its own
lock screen too, whenever a file under `~/.config/omarchy/plugins` changes
or it is asked to rescan them. Every update wrote OmacVM's bar widgets
there again and asked for a rescan. The new lock screen never gets the
lock Hyprland still holds, so it waits without a password field. Upstream
Omarchy keeps its lock screen over such a reload (omacom/omarchy#9485);
omarchy-mac 4.0.3 does not have that yet. This is not Omarchy's other lock
bug above (omacom/omarchy-mac#738), which only stops the next lock.
- **Fix:** shut the VM down and start it again (`omarchy-restart-shell`
refuses while the lock is asked for). From 3.2.13 an update of a locked VM
writes nothing there and asks for no rescan: changed widgets wait in
`~/.local/state/omacvm/plugins-staged`, and right after the unlock they go
in place and the shell restarts once. A widget that did not change is not
written again at all. `omacvm check` says "right after the next unlock" for
a widget that waits. Switching the control centre or Omanotch off while
the VM is locked does the same: the control centre leaves the bar at once
and its folder goes at the next update; Omanotch stops at once and removes
its copies of Omarchy's plugins right after the unlock.
- **Where:** `src/guest/session-lock.sh` (the one lock check),
`src/lib/install-plugin.sh`, `src/lib/omacvm-plugins`,
`src/guest/restart-shell.sh`, `src/app/guest/omacvm-displays` (the display
widget), `src/omanotch/guest/monitor/display-panel.py` (Omanotch's display
panel), `src/guest/off.sh` and `src/control/guest/install.sh` (the offs);
tests `src/tests/plugin-lock.sh`, `src/tests/restart-shell.sh`,
`src/tests/features-off.sh`, `src/omanotch/guest/tests/test_display_panel.py`.
27 changes: 26 additions & 1 deletion src/app/guest/omacvm-displays
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ SCALE_WAIT = 4.0
WIDGET = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "omarchy/plugins/omacvm.monitor"
WIDGET_BUILD = Path("/usr/local/share/omacvm/app/guest/monitor-widget/build.py")
OMARCHY_PANEL = Path("/usr/share/omarchy/shell/plugins/panels/monitor/Panel.qml")
SESSION_LOCK = Path("/usr/local/share/omacvm/guest/session-lock.sh")


def log(message: str) -> None:
Expand Down Expand Up @@ -588,10 +589,25 @@ def run_sync() -> None:
log(f"display sync failed: {error}")


def session_state() -> str:
"""The desktop session: down, busy, locked or open (guest/session-lock.sh;
open without it)."""
try:
out = subprocess.run(["bash", str(SESSION_LOCK)], capture_output=True, text=True,
timeout=15, check=False).stdout.strip()
except (OSError, subprocess.SubprocessError):
return "open"
return out if out in ("down", "busy", "locked", "open") else "open"


def refresh_widget() -> None:
"""Rebuild omacvm.monitor when an Omarchy update changed its display panel.
The installer may be rewriting the widget right now: a file gone midway
is not an error (it used to stop the agent)."""
is not an error (it used to stop the agent). Not while the session is
locked: a write in the plugins folder or a rescan reloads Omarchy's lock
screen without its password field (omarchy-mac 4.0.3); Update VM stages
its own copy then, else the agent's next start does it. A shell that is
starting gets 20 s to answer."""
stamp = WIDGET / ".source-sha256"
import hashlib
try:
Expand All @@ -603,6 +619,15 @@ def refresh_widget() -> None:
except OSError as error:
log(f"display widget not checked: {error}")
return
state = session_state()
for _ in range(20):
if state != "busy":
break
time.sleep(1)
state = session_state()
if state in ("locked", "busy"):
log("display widget not rebuilt while the session is locked (Update VM or the next start does it)")
return
try:
subprocess.run([sys.executable, str(WIDGET_BUILD), str(WIDGET)], timeout=30, check=True)
subprocess.run(["omarchy-shell", "-q", "shell", "rescanPlugins"], timeout=30, check=False)
Expand Down
Loading
Loading