Pinning /usr/bin ran the packaged helper even where the migration came from somewhere else. Under a dev link the migration is read from the checkout while /usr/bin still holds the last installed package, and a package predating __migrate prints its usage and fails the update. By name, the unprivileged call goes through PATH and the sudo call through secure_path, which is how other migrations reach their helpers and which lands on /usr/bin on an install.
Co-Authored-By: Codex XHigh <noreply@openai.com>
The re-point of a user plugin link stranded by the elsewhen package moving from plugins/ to shell/plugins/ landed in migration 1789581661, which every machine updated between that migration shipping and the package moving had already applied. omarchy-migrate keys completion on the filename alone, so the repair never ran where it was needed. On a dev checkout the shell discovers the packaged plugin only through that link, and a rescan drops a dangling one, so Elsewhen vanished from the bar on the first shell start after the package upgrade.
Renaming the file runs the whole migration again everywhere. Each step is safe to repeat: the package add is a no-op once installed, both link branches are guarded, the rescan is best-effort, and the put keeps an existing placement. The new test holds a state directory with only the old marker and checks the renamed file is still pending, which the old name could not pass.
The 2026-09-19 revision of this migration, and a symlink then shipped under config/, pointed every install's ~/.config/omarchy/plugins/omacom.elsewhen at /usr/share/omarchy/plugins/omacom.elsewhen. elsewhen 1.0.0-2 moved to shell/plugins, so the link dangles, the shell lists the plugin as enabled but never loads it, and the bar carries an empty slot. The migration kept any existing link, resolving or not, so a rerun could not repair it. A link into /usr/share/omarchy that no longer resolves is Omarchy's own and is re-pointed; a link the user made is still left alone.
A bare `omarchy-shell shell rescanPlugins` exits 1 when no shell answers on the caller's tree, and omarchy-migrate runs migrations under `set -e`, so an update run from a TTY or over ssh, or one whose shell had gone down while the package replaced its QML, died at "omarchy-shell is not running" before its post-update hooks, shell restart and reboot prompt, and re-failed the same way every time until a shell could be asked. The rescan is now best-effort, like `omarchy-bar put` already is and like #11117 makes the plugin commands' closing rescan: the update restarts the shell once the migrations are through, and a session without one has no bar to place on. An unknown widget on a live shell still leaves the migration pending.
Choosing Hermes as the default agent built it through mise: a pipx environment with no checkout, so `hermes update` had nothing to move, and the only Hermes that could update itself was the one Hermes Desktop set up. Both paths now run the same setup. omarchy-install-hermes-cli installs the hermes-desktop package and runs upstream's installer from it, pinned to the packaged release and started on main, exactly as Install > AI did; omarchy-install-ai-hermes is that plus opening the app. The terminal, the default agent and the app share one runtime, and it updates itself.
--check answers whether --now has anything left to do, not merely whether a hermes runs: choosing Hermes from the menu asks first and opens a terminal only on a no, so a yes has to mean no minutes-long step would run where nobody can see it. With the app installed that means the runtime's own command, its completion marker and the seeded packaged app; a finished runtime whose command is gone, somebody else's, or its own but unable to run gets it back from upstream's path stage without bootstrapping again. Either way the command has to be the one PATH finds, because omarchy-agent runs bare `hermes` and Omarchy puts mise's shims ahead of ~/.local/bin; a command in the way is named rather than installed over. The modes are named outright because the app's launcher used to call this command with no arguments to reconcile a mise copy; a default of --now would turn every launch into an install. --check still refuses to run the retired wrapper, since running it built Hermes through mise, and a machine whose migration is pending can still have it on PATH.
Provisioning no longer writes the wrapper, Remove Preinstalls no longer looks for it, and the wrapper, the environment it built and what proves them Omarchy's are known to the installer alone: --retire-mise is the migration's whole job, and --now runs the same removal once the runtime installer has saved the wrapper aside, so a user who chose Hermes before their migration ran is not left with mise's shim answering `hermes`. Only the wrapper proves the environment is Omarchy's, at its path or in that saved copy, so the environment goes first and the wrapper last, judged by mise neither having it installed nor still requesting it; a removal that leaves either behind, or a listing that cannot be read, mise missing included, stops with the commands to finish by hand and leaves the migration pending. The migration that once installed the wrapper is kept as a no-op for late updaters, and one whose default agent was Hermes is told to choose it again.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.com>
Restarting immediately after the plugin rescan races Quickshell IPC handler creation and can crash the exiting shell. The normal update flow already restarts after migrations. Let this migration finish through live enablement and placement without adding timing workarounds.
Link the package into the existing plugin directory and let bar put handle enablement, clock-relative placement, and the missing-clock fallback. Preserve user checkouts and existing placements, and seed the same link for new users. This removes the Atreyu packaged-discovery prerequisite and the checkout cleanup and JSON rewrite machinery.
Retire only checkouts whose refs and reflogs are reachable from recorded origin history, and preserve ignored files. Leave configs without an explicit supported bar layout untouched so migration does not replace the shell fallback with an almost empty bar.
Co-Authored-By: Codex XHigh <noreply@openai.com>
Elsewhen (omacom.elsewhen) arrives as the elsewhen package under
/usr/share/omarchy/plugins, the packaged root the shell scans between its
bundled plugins and the user's. It opens the right section of the default
bar, just before the tray, and a migration installs the package, writes the
widget into a customized shell.json in the same spot, and retires a pristine
pre-package clone of the upstream repo that the package now shadows.
Bring in the legacy-grant classifier fix and the reserved-prefix
quarantine from #9457 so this branch no longer carries a stale copy of
that command.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Upstream now runs every Omarchy-owned pacman transaction through the
hidden omarchy-update-pacman helper so a mid-transaction systemd reexec
cannot kill it. Keep the deferred pre-refresh-pacman hook and the
command-scoped sudo wrapper, and call the helper from the refresh and
channel commands; the wrapper still applies to the helper's own sudo.
The sudo boundary fixture copies the helper into its root and runs a
systemd-run stand-in that execs the wrapped pacman step in place.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Remove unsafe project bin PATH injection
* Cover customized unsafe Mise paths
* Revoke legacy Mise Work trust
* Harden legacy Mise trust cleanup
* Preserve ignored Mise Work configs
* Scope Mise path cleanup to env
* Accept paranoid Mise ignore marker
Reported-by: infosec-us-team
Cubic keeps pushing until packets drop, which stands queues up in the path
on fast links. BBR paces to its estimate of bottleneck bandwidth and minimum
RTT instead, cutting queueing latency while keeping throughput. fq is the
qdisc BBR is built to pace through.
tcp_bbr and sch_fq are modules in every kernel Omarchy ships and autoload
when the sysctls are set. The migration re-applies the shipped file so new
connections switch without a reboot, no-ops once the live values match, and
flags a reboot if applying fails.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The speaker's USB firmware stops answering control requests when the host
stops the audio stream after WirePlumber's 5 s idle suspend. The kernel
then logs usb_set_interface failed (-110), clock source 1 is not valid,
and cannot set freq 48000 err -110; PipeWire fails to start the sink and
only a replug recovers it. On one machine this happened on six days over
three weeks, up to hundreds of timeouts a day.
A WirePlumber rule sets session.suspend-timeout-seconds = 0 for the KEF
node only, so the stream is never stopped and the trigger never fires.
Other sinks keep the default. The migration seeds the file for existing
installs and restarts WirePlumber if it is running, since conf.d is only
read at startup.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Mirrors the hey-cli stub: the wrapper in ~/.local/bin installs and
upgrades through mise on first run, so the CLI tracks releases instead
of going stale as a manually dropped binary.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Relay the Elgato Cam Link 4K as a 16:9 virtual camera
Browser meeting apps such as Zoom's web client ask the Cam Link for a
standard-definition stream, and Chromium settles on the smallest mode it
offers, 640x480. The Cam Link fills that 4:3 frame by cropping its 16:9
input, and the app then paints the frame into a 16:9 tile, so everyone
comes out stretched wide. The web client has no HD switch to avoid it.
Hide the raw capture node from users and re-expose it through v4l2-relayd
as a 1280x720 virtual camera with the same name, so there is still just
one "Cam Link 4K" to pick and no way to negotiate 4:3 from it. udev
starts the relay whenever the Cam Link enumerates and stops it on unplug,
and the relay only pulls frames while something is watching. The sink
runs unsynced because v4l2src stamps each frame with its capture time,
which a synced sink treats as already late and drops.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Take the review fixes for the Cam Link 4K relay
Tie only the device's stop into the relay instance. A start dependency on
it left a job waiting on a device that never comes whenever the base
v4l2-relayd.service is started without a Cam Link attached, since the
package generator wants every configured instance.
Let the loopback unit rerun on each relay start, so a deleted or unloaded
device is recreated on replug instead of the oneshot staying satisfied.
Start the relay outright at the end of the migration. The udev trigger
only starts it when the rule is new to the device, and a failed module
build would otherwise pass silently with the raw camera already hidden.
Run the hardware fix after the Panther Lake kernel swap, as it pulls in a
DKMS module that would otherwise build twice.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>