No branch on the repo is protected and there are no rulesets, so the
file never enforced owner review. Its only effect was auto-requesting a
review from the other owner on every PR, which nobody acted on.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The repo moved to the omacom org. GitHub redirects the old URLs, but
`omarchy channel set dev` was still cloning from basecamp/omarchy, which
left every dev checkout with a stale origin remote that confuses gh
(pr create fails with "No commits between omacom:quattro and
basecamp:<branch>"). Update the clone URL, the quattro upgrade tarball,
the update-confirm release link, the systemd Documentation link, and
the manual.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSFKDatumRZHB8zqk5CP5C
1Password 8.12 changed its app id from "1Password" to
"com.onepassword.OnePassword" (its .desktop file now declares that as
StartupWMClass). Our window rule only matched the old name, so the main
window came up tiled and lost the no_screen_share protection.
Match both forms so older installs keep working.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSFKDatumRZHB8zqk5CP5C
A floating window on the console is not laid out by the gaps, so it no
longer stretches the panel to full width. Moving an app onto or off the
scratchpad and toggling floating now refit too, via
window.move_to_workspace and window.update_rules; both were measured on
Hyprland 0.56.2 to carry the settled count.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012UyVoFTM98Tduoxg7qZax7
udev patterns are shell globs, so nvme[0-9]*n[0-9]* also matched every
partition. Partitions have no queue/scheduler, and udev logged a "Could not
chase sysfs attribute" for each one at boot. Restrict the match to
SUBSYSTEM block with DEVTYPE disk, which also keeps mmcblk boot areas and
NVMe multipath nodes out.
Reword the comment: kyber targets a read latency rather than bounding it,
and the kernel's default choice depends on the device rather than being a
fixed NVMe versus everything-else split.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The kernel leaves NVMe on none and everything else on mq-deadline. Neither
bounds latency once the device queue fills, so a large build, copy, or
package upgrade can make the desktop sluggish while reads wait behind a
wall of writes.
Kyber keeps separate read and sync-write queues and throttles the depth it
submits to hit a 2ms read target, which keeps interactive reads flowing
under heavy writes at negligible CPU cost. The trade is a small ceiling on
peak throughput on very fast devices, which matters for a storage server
chasing IOPS but not for a desktop.
Ships as a package-owned udev rule in /etc so it applies at boot and on
hot-plug. Existing installs pick it up at the next boot. zram is left alone
since it has nothing to schedule.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The update conflict tests stub sudo and pacman, but omarchy-update-pacman
now puts systemd-run between them, so on a systemd-booted host the tests
would reach for the real system manager. Stub systemd-run to drop the
wrapper's options and run the command, and cover the helper's own
invocation composition in a new test.
Raised by codex review.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zcqENR1UbhuwC1v5u3Wop
omarchy-channel-set runs under set -e, so a failure after it has begun
mutating the system (dev link, pacman channel, packages) died silently
with the switch half-applied. Trap ERR once the mutation phase starts
and say how to pick the switch back up.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The floating-terminal presentation wrapper showed the green "Done!"
prompt for every exit code except Ctrl-C, so a failed update or channel
switch closed looking like a success. Pass the command's exit code
through to omarchy-show-done and render a red "Failed (exit code N)!"
prompt when it is non-zero. The pkg install/remove pickers get the same
treatment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Upgrading systemd runs its post_upgrade scriptlet mid-transaction, which
reexecs both the system manager and every user manager. When pacman runs
inside a user-session scope (the floating update terminal), that reexec
can SIGKILL it and abandon the transaction halfway, with packages
upgraded but none of the post-transaction hooks run.
Route every Omarchy-owned system mutation through a new hidden
omarchy-update-pacman helper that registers the transaction as a PID 1
scope via systemd-run, keeping it out of the user manager's cgroups.
System scopes survive the system manager's own reexec, and as a bonus the
transaction now also survives its terminal window closing. On unbooted
systems (the installer chroot) the helper runs pacman directly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The theme laid out the logo, password entry and progress bar once, when
the script first ran. Plymouth sizes its window to the largest display
present, so a monitor that comes up after plymouthd starts (a Thunderbolt
display whose DisplayPort tunnel is established about a second after the
kernel's display driver loads, a dock, an MST hub) got the prompt drawn at
the internal panel's coordinates: off-center on the external display and
partly off-screen on the panel. With the lid closed the passphrase box was
effectively invisible.
Move the layout into a function and re-run it from the refresh callback
whenever Window.GetWidth/GetHeight change, which is how Plymouth's script
plugin exposes a display being added or removed. Existing bullets are
repositioned too.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The battery service ran `powerprofilesctl get` every two seconds to keep the
active profile visible to the wallpaper and lock services. That command is a
PyGObject script, so the shell spawned a Python interpreter for it tens of
thousands of times a day. Roughly once a day one of those exits into a CPython
3.14 finalization race (python/cpython#124619): the GLib D-Bus worker thread
calls PyGILState_Ensure after the interpreter is torn down and the process
dies with SIGSEGV, leaving a core dump and a crash notification behind.
Read the ActiveProfile property straight from power-profiles-daemon with
busctl, the same way omarchy-powerprofiles-set already reads UPower. The
output is JSON, so an empty or malformed reply when the daemon is not running
still reads as no active profile, matching the previous behaviour.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011gbfh4Mi9dK6SAd1P2xMTi
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>
Electron rewrites ~/.config/Claude for as long as the app runs, so removing the package and deleting the directory while it is open leaves the app running and the directory back within the same second, holding fresh Preferences and crash-reporter state. Driven end to end on an edge worker: remove via the menu with the app open left 11 processes and a recreated ~/.config/Claude; with the app quit first the directory stayed gone. The test stubs pkill so the suite cannot take a developer's own Claude with it, and asserts the remover reached for it.
Co-Authored-By: Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.com>
Follows the ChatGPT flow: the Install > AI entry runs
omarchy-install-ai-claude in a floating terminal, which installs the
claude-desktop package (Anthropic's Linux desktop beta, repacked from
their Debian repo in omarchy-pkgs) and opens the app. Remove > AI
drops the package along with ~/.config/Claude and ~/.cache/Claude,
the Electron directories the desktop app owns, while keeping
~/.claude, ~/.claude.json, and ~/.cache/claude-cli-nodejs: those
belong to the Claude Code CLI, which ships in its own package and
survives this removal, just as the ChatGPT remover keeps the Codex
CLI.
The menu mark is a new U+E90E glyph in the Omarchy icon font, from
Simple Icons' Claude mark, so it reaches desktops through the next
omarchy-settings release.
Co-Authored-By: Fable 5 <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.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>
Chainfire (@Chainfire) privately reported the passwordless asdcontrol watchdog reboot on August 25, 2026.
The fix landed in the 4.0.2 security work: #9200 (df819a6f) removed Omarchy's unrestricted sudo grant, while omacom/omarchy-pkgs#242 and omacom/omarchy-pkgs#243 restricted the package's grant. This records the reporter credit omitted from those changes.
Reported-by: Chainfire (https://github.com/Chainfire)
Thanks to _SiCk // afflicted.sh for independently reproducing the passwordless sudo persistence issue and reviewing the proposed fix in #7990. The follow-up fix landed in #9387 (945af75a), with the 4.0.3 backport in cf459ba7.
The original boot-cleanup patch was contributed by @Adolanium. Preserve that co-author credit alongside this acknowledgment.
Co-authored-by: Adolanium <94890352+Adolanium@users.noreply.github.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>
Arch removed the prebuilt broadcom-wl package on 2026-09-02 and rebuilt
broadcom-wl-dkms with replaces=(broadcom-wl). A replaces entry only helps
upgrades of an already-installed package, so the name is now unresolvable
as an explicit pacman target and the ISO offline mirror download fails
with "target not found: broadcom-wl".
broadcom-wl-dkms depends on dkms, so the explicit dkms in the hardware fix
is redundant; linux-headers stays because the module has to build against
the running kernel.
1Password reads the display scale itself, the way Electron apps do, so
on a scaled monitor it comes up oversized next to every other window.
Pin it with --force-device-scale-factor=1 and let the compositor scale
it.
The app menu is covered by the packaged .desktop, which we build
ourselves in omarchy-pkgs. This is the other route in: the hotkey runs
the binary directly and never reads that file.
Claude-Session: https://claude.ai/code/session_01JB9phxP56gnP7qSidkkUJE
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Clicking the keyboard layout widget switches one device, chosen by
filtering the seat through UNTYPED_KEYBOARDS and then taking whichever
survivor sits furthest through the layout list. That filter has to
recognise every non-keyboard by name, and a laptop registers far more as
a keyboard than it lists.
On a Dell XPS 14, Hyprland reports ten keyboards and one of them is a
keyboard. The filter catches three of the other nine, leaving vendor
hotkey blocks (intel-hid-events, intel-hid-5-button-array,
dell-privacy-driver, dell-wmi-hotkeys) and two HID endpoints ahead of
at-translated-set-2-keyboard, which sorts last. Every click switches
hid-sdw:...-consumer-control instead, so the label cycles convincingly
while typing never changes. Device order is stable across polls, so it
is deterministic rather than a race, and needs no pre-existing bad state.
Switch every keyboard holding the same layout list instead, naming an
absolute index. "next" advances each device from wherever it sits, so a
seat that has already drifted apart stays drifted and merely inverts;
one index converges it in a single click, and a seat in lockstep leaves
the reading nothing to disagree about. Keyboards given their own
kb_layout hold a different list and are left alone, since an index into
this list would not mean the same layout to them.
The evdev KEY bitmap would separate these cleanly - the real keyboard
emits 167 keys, the pseudo-devices at most 19 - but hyprctl devices
reports no capability information, so the switch is taken out from
behind the name filter rather than the filter being lengthened.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Hiding the top bar and removing the window gaps are the two things you
do to give the screen entirely to your windows, and doing both took two
hands and two hotkeys. `omarchy toggle fullscreen desktop` does them
together.
It only leaves full screen when both halves are in it, so hitting the
hotkey with just the bar hidden (or just the gaps gone) pulls the other
half into line instead of flipping the one you already set.
Claude-Session: https://claude.ai/code/session_01JB9phxP56gnP7qSidkkUJE
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>