# The consolidation of komorebi, komorebi-for-kde and komorebi-for-mac: final-state summary Status (written at wip-cand8, b6f4c2ae, 2026-10-31; sections 1, 6, 7 and 8 were brought up to date by the cutover stack, `wip-cutover`, and say so; everything else is as written then, with the paths and version numbers of that time): a summary for a maintainer who was not there. Gates for candidate 8 were still running when this was written; the results stated below are the ones that had finished, with the candidate they ran on. Companion documents: [ARCHITECTURE-UNIFIED.md](ARCHITECTURE-UNIFIED.md) (the map), [build-and-test.md](build-and-test.md) (commands, flakes), [macos-hand-check-plan.md](macos-hand-check-plan.md) (what the owner must run on a Mac), [known-issues.md](known-issues.md) (open decisions). ## 1. What the repository is now Three programs (Windows, KDE Plasma, macOS) were three repositories with copies of the same ideas. They are one repository with one Cargo workspace (one `Cargo.lock`), shared crates under `crates/`, and one package per user-facing program, the platform chosen by `cfg(target_os)`. | Package (binary) | Directory | How the platform is chosen | |---|---|---| | `komorebi` | `komorebi-daemon/` | `main.rs` is a `cfg_select!` calling `daemon::run()` of `komorebi-windows`, `komorebi-linux` or `komorebi-macos`, which are target-gated dependencies | | `komorebic` | `komorebic/` | `src/windows.rs`, `src/linux/`, `src/macos/`; the arguments are in `crates/komorebic-core` | | `komorebi-client` | `komorebi-client/` | `src/{windows,linux,macos}.rs` behind one `Connection` | | `komorebi-bar` | `komorebi-bar/` | `src/windows/`, `src/macos/`; shared part in `crates/komorebi-bar-core`. KDE has no bar | Three platform layers remain, because the platform layers are different code, each in its own directory since the cutover stack: `komorebi-windows/` (Windows; it was `komorebi/`), `komorebi-linux/` and `komorebi-hotkeys/` (KDE; with `nix/linux/`, the NixOS module and the VM suites, and `docs/linux/`) and `komorebi-macos/` (macOS; with `nix/macos/`, the nix-darwin module, and `docs/macos/`). The trees `komorebi-for-kde/` and `komorebi-for-mac/` of the first version of this summary no longer exist. The daemon libraries have no binary; the `komorebi` package is the binary. The crate map and the tree layout are in [ARCHITECTURE-UNIFIED.md](ARCHITECTURE-UNIFIED.md); how to build each is in [build-and-test.md](build-and-test.md). ## 2. What was shared, and how | Crate | What it holds | How it was moved | |---|---|---| | `komorebi-collections` | `Ring`, `LockableSequence` | the KDE copy was canonical; Windows and macOS switched to it | | `komorebi-protocol` | `SocketMessage`, payloads, state types | one enum for all platforms (guard arms for platform-only messages); the wire format is pinned by per-platform goldens generated from the three daemons before the move | | `komorebi-config` | `StaticConfig` and the `komorebi.json` types, schema generation, and the **one window-rule matcher** (`komorebi_config::rules`: `should_act`, `should_act_individual`; see [rule-matching.md](rule-matching.md)) | one union type with per-platform schema views; the matcher replaced three copies, each daemon keeps a thin wrapper with its own signature | | `komorebi-transport` | the command listener, pause filter, subscriber registry, `EventChannel` | the daemons take the window manager lock; the crate does not name a mutex | | `komorebi-backend` | the `Backend` trait (what a daemon asks of its platform), plain-data infos, `FakeBackend`, contract tests, the id `Registry` | derived from the evidence in [seam.md](seam.md); Windows goes through it for the clusters in [backend-switch.md](backend-switch.md); KDE runs on it; macOS has an adapter the daemon does not use yet | | `komorebi-core` | the model, generic over the platform window: `Container`, `Workspace

` (with `update_shared`, a skeleton with hooks), `Monitor

`, `WorkspaceGlobals`, and `command` (the shared commands over a `CommandHost`: workspace navigation, moves across workspaces and monitors, lock and unlock, focus-workspace with the pointer preamble) | converge in place, then pure move, then converge per daemon; hooks (`WorkspacePlatform`, `MonitorPlatform`, `HideSpot`) carry what differs; details in [core-model.md](core-model.md) | | `komorebi-settings` | the run-time settings as one immutable snapshot replaced as a whole, instead of process-wide statics (paddings, aspect ratio, layout defaults, display preferences, animation, transparency, window-handling behaviour) | see [settings.md](settings.md) | | `komorebi-bar-core` | bar configuration, widgets, platform seam, schema views | [bar-unification.md](bar-unification.md) | | `komorebic-core` | the command line of every platform (clap definitions, docgen), platform enums | the generated `docs/cli` pages were the oracle | The state JSON each daemon prints is unchanged by the shared `Monitor

`/`Workspace

` (platform extras are flattened in, pinned by tests). Native tests of the shared code run on Linux and protect all three daemons ([e2e-to-unit.md](e2e-to-unit.md) says which e2e checks became native). ## 3. What intentionally stays per platform, and why - **The window manager, the command router and event processing.** About 26-37% of the text is shared and the rest is real behaviour (Win32 cloaking, rule engines with different lists, KWin desktops, AppKit accessibility). `WindowManager

` is not built: [core-model.md](core-model.md) sections 17, 18, 20 and 24 list the blockers per platform; section 21 documents the commands that were shared; section 23 is a design note for a common event vocabulary ("thin events"), not implemented. - **macOS `Workspace::update`.** It differs in a scrolling early return and a wider SkyLight disable/re-enable batch; adopting `update_shared` would change scrolling on macOS and cannot be verified without a Mac (section 22; decision D15 below). - **Backends and subsystems**: borders, animations, DWM ghosts, KWin bridge and effects, AppKit observers, wallpapers, hotkeys. The trait is the seam; the rules are in [seam.md](seam.md). - **The bar's rendering code** (frame margins, `network.rs`, the window seams in `bar.rs`/`main.rs`): step B4 of [bar-unification.md](bar-unification.md) was not done because it cannot be shown mechanical without a screen. - **Command line**: each platform keeps its own commands and help text; most are common. - **Packaging**: [unified-binaries.md](unified-binaries.md): one binary, a library per OS. ## 4. How it was verified ### Oracles (checked in files, compared byte for byte) Wire goldens (`komorebi-protocol`), the three `schema.json` and `schema.asc.json` (now `schemas/{windows,linux,macos}/`), the two `schema.bar.json` (size and hash pinned in `komorebi-bar-core`), the `docs/cli`, `docs/linux/cli` and `docs/macos/cli` pages (before the cutover: `docs/cli` of each tree), JSON pins of `Container`, `Monitor` and the state of the three daemons from real fixtures, and derivation drift (`nix eval ...drvPath`). Details in [ARCHITECTURE-UNIFIED.md](ARCHITECTURE-UNIFIED.md) section 5. ### Gates, by candidate Each candidate was a merge of the finished stacks, gated once. Results as recorded (host load and flakes noted in [build-and-test.md](build-and-test.md) section 6): | Candidate | Windows e2e (QEMU guest) | KDE VM | macOS (a Mac Mini: compile, unit tests, nix) | nix pre-landing | |---|---|---|---|---| | baseline (before the work) | stale markers (4 checks) fail; `commands` fails about 1 in 10 sweeps | `vm-commands` never green; `vm-core-e2e` once | n/a | n/a | | 5 (core: Container, Workspace, Monitor, `update_shared`, settings) | 38 scenarios: green apart from the 4 stale markers and flakes (`commands` 265/1 and `config_reload` 7/1 from a lock-hold stall, `resize` 25/1); the `bar` scenario could not open a window (guest has no OpenGL 2.0), schema byte-equal | 16 of 16 fast suites green, `vm-commands` green for the first time | cargo unit tests and clippy green; nix: packages and module checks green after toolchain and source-filter fixes | n/a | | 7 (shared rule matcher, shared commands, stall fixes) | not run as a whole | not run | cargo green, nix green | Windows clippy green; Windows fmt and deny, and two KDE clippy lints, failed and were fixed in 8; packages built | | 8 (pointer picks the monitor, bar wgpu fallback, macOS deep dedup, formatting) | in progress at the time of writing: `commands` 266/0 (first fully clean), `config_reload` 8/0 | queued | nix all green, cargo 665 passed, 0 failed, 1 ignored | in progress | Stress runs of lock-heavy command sequences compared baseline and candidate 5: non-answers of `komorebic state` fell from 80-83% to 14-15%, and "border manager stuck" warnings from 21-31 to 3-5 (the stall fixes work; the stall itself still occurs now and then). ### Flake registry (summary; full table in build-and-test.md) Known: Windows `commands` stall (about 1 in 10-12 sweeps, cause not found), `lifecycle_hidden`, `resize`, four stale known-failure markers (`maximizing.user`, `placement` integer case, `unknown_monitor` x2, same on baseline), a runner quirk for `focus_follows_mouse`. Candidates: daemon start failing with `AllowSetForegroundWindow` at high load, `hotplug` with the recorder. KDE: `vm-commands` and `vm-core-e2e` were load-dependent on the old baseline. ## 5. What is NOT verified - **macOS has never run as a daemon in these builds.** Only compile, clippy, unit tests and nix builds ran on a Mac. Every behaviour change that touches macOS (hiding, the registry, the shared Container API, settings, pointer-picks-monitor, bar clicks) is listed in [macos-hand-check-plan.md](macos-hand-check-plan.md). - **The bar at run time.** The Windows `bar` scenario passes schema and config checks; the guest has only software rendering, so window checks depend on the new wgpu fallback (WARP), which had not run in a guest when this was written. The macOS bar never rendered under these changes. - **The KDE `slow` VM tier** (18 suites: keys, rdp, drag and live resize, lock-sleep, wallpaper, look, ...) was not run; only the `fast` tier (16 suites) was, on candidate 5. Candidate 8 had not been run on the VM yet. - Windows: no scenario covers transparency or animation; only `config_reload` touches the settings path. - Behaviour changes marked BEHAVIOUR in revision bodies are landed only after the e2e or VM gate named in the body. ## 6. Known issues From [known-issues.md](known-issues.md): (1) KDE skipped the pointer-monitor preamble for three workspace commands, now resolved for all platforms by owner decision; (2) `Monitor::focus_workspace` overwrites a workspace name; (3) `Workspace::remove_container` focuses the container next to the focused one rather than the removed one; (4) a subscriber that stops reading can stall the daemon on macOS (a 1 s write timeout was tried and reverted because it can truncate a notification). Items 2-4 are inherited behaviour, pinned by tests, left for a decision. Other open items: the Windows lock-hold stall; `docs/linux/dev/COMPARE.md` and `scripts/linux/compare-gen.py` are stale (the KDE-only justfile this paragraph used to name was merged into the one root `justfile` by the cutover stack). ## 7. The cutover: done in the cutover stack, and still open The stack `wip-cutover` (on `wip-main`; `jj log -r 'wip-main..wip-cutover'`) did the mechanical part. Each row says what the first version of this section proposed and where it stands. | Item | State now | Open | |---|---|---| | shim packages | `komorebic-linux`, `komorebic-macos`, `komorebi-client-linux`, `komorebi-client-macos`, `komorebi-bar-macos` deleted | none | | version crates and numbers | one `crates/komorebi-version` instead of `version-linux`, `version-macos`; one workspace version, 0.2.0 (the 0.1.42 / 0.1.0 / 0.1.1 of the first version of this section are history) | none | | directories | `komorebi/` is `komorebi-windows/`; the KDE and macOS libraries are `komorebi-linux/` and `komorebi-macos/`; `komorebi-hotkeys/`, `spikes/` at the top level; `docs/{linux,macos}`, `nix/{linux,macos}`, `scripts/linux`, `schemas/{windows,linux,macos}` | none | | `deny.toml`, `rustfmt.toml`, toolchain, `.gitignore`, licence, `justfile` | one of each at the root | none | | docs | the READMEs and CONTRIBUTING of the ports are `docs/linux/` and `docs/macos/`; paths and links were fixed; the Windows mkdocs site excludes `docs/linux`, `docs/macos` and `docs/dev` | `docs/cli` per platform stays until the commands are merged; `docs/linux/dev/COMPARE.md` is stale and `scripts/linux/compare-gen.py` with it; the KDE documents still say "this repository" for what is now one of three | | flakes | the `kfde-*` and `kfm-*` flakes were still three when the directories moved (`komorebi-for-kde/flake.nix`, `komorebi-for-mac/flake.nix`) | one root flake is being written by the agent `flake-one`; the nix commands in the documents name the old attributes until it lands | | workflows | the files of the old repositories are in `docs/dev/old-ci/{linux,macos}/` (inert: under `docs/` GitHub does not run them); the Windows `.github/` is as it was | the proposal stands, not decided: one CI that builds the three packages, runs `crates/` natively, the nix checks, the Windows e2e on a host with KVM and the KDE VM tiers; one release workflow with one tag and one version; the Homebrew formula template and the macOS docs-site files wait in the archive | | URLs | unchanged | where the repository and the sites are published is not decided: the old GitHub repositories (`LGUG2Z/komorebi-for-kde`, `LGUG2Z/komorebi-for-mac`), `komorebi-for-mac.lgug2z.com`, the update check of the macOS bar, the `$schema` URLs of the example configurations and generated schemas | | tools | `tools/` is committed; `tools/format-all` has one group | `tools/kde-closure-check` still reads the old path of `root-crates.json` | ## 8. How to continue 1. **Get the macOS pass done first.** [macos-hand-check-plan.md](macos-hand-check-plan.md). Nothing deeper should land on macOS before it. 2. **`WindowManager

`.** Blockers per platform: [core-model.md](core-model.md) sections 20 (KDE, macOS) and 24 (Windows). The unmanaged-window gate is not behaviour-neutral to move into the router (a `gate_message` hook is proposed in section 22.1). Rule: gate every step with the e2e/VM suites, one daemon at a time. 3. **Thin events.** Design note in section 23 (`CoreEvent`, shared handlers for gone, appeared and minimized); risks: side-effect order per arm, macOS focus change carries no window. 4. **Commands.** `komorebi_core::command` is partial: sixteen arms and focus-workspace are shared; the remaining arms are per platform (section 21 lists what stays and why). **The rule matcher is done** (one implementation). 5. **The deeper-dedup boundary.** Share a function only when a native test can pin it; where the text differs for a real platform reason, put the difference behind a hook (as `HideSpot` and `begin_pass` do) rather than converging behaviour you cannot verify. 6. **Decide items 2-4 of the known issues**, then move them into the shared code with the e2e gate. 7. **Finish the cutover** (the open column of section 7: the one flake, CI and releases, the URLs), then regenerate the Windows `docs/cli` and the schema docs. ## 9. The decisions that shaped the design Rewritten from the decisions log. "Owner" marks a decision the owner made; the others were made to unblock and are reversible as stated. | # | Decision | Why | Reverse | |---|---|---|---| | 1 | One workspace, one `Cargo.lock`; flakes re-export the nested ones and the root builds them from the whole repo | a spike showed 859 to 900 packages with no conflicts; the root source can then be shared | keep three workspaces | | 2 | macOS checks on Linux use a fake C compiler and only type-check | nothing else runs without a Mac | none | | 3 | The wire oracle is a schema-driven golden generator run on the three daemons before the move | the move must not change what is sent | none | | 4 | One union `StaticConfig` with per-platform schema views; per-platform defaults documented in a table instead of converging every doc | users' files must keep loading; schemas are checked in | one type per platform | | 5 | `komorebic-core` generic over the platform, no `cfg` in the definitions; the CLI docs of each tree are the oracle | the pages are what users read | per-platform clap structs | | 6 | Shared transport: the daemon locks the window manager, the crate never names a mutex | different locks per daemon | none | | 7 | The bar is `komorebi-bar-core` plus a platform trait, one config enum, schema = per-platform views of one union; macOS accepts and skips Windows-only keys with a warning | one config file works everywhere | separate bars | | 8 | `Backend` is derived from evidence (seam.md), not written from memory of one platform | a trait written from one platform bakes in its assumptions | none | | 9 | Settings handle is `RwLock>>` with a writer mutex; the platform part is a type parameter | no new dependency; one consistent snapshot per use | use `ArcSwap` | | 10 | Unified layout: one package each for `komorebic`, `komorebi-bar`, `komorebi-client`; the daemon as three OS-gated libraries behind one thin binary | rewriting about 1700 `crate::` paths and moving KDE tests was the alternative | merge the libraries later | | 11 | Shared `Monitor

` keeps each daemon's state JSON (flattened platform extras); the runtime `required` order in generated schemas may change | state JSON is pinned by fixtures and user tooling | one superset JSON | | 12 | `Workspace`/`Monitor` move by classifying each difference (cosmetic, same behaviour, real, missing), converging, then moving | a 25-member trait would hide real differences | per-platform copies | | 13 | `Workspace

` hook signatures: fallible `begin_update`, `end_update` returns a pass, `place_window` returns a result; the Workspace move is atomic | review of the first design against KDE and Windows | finer hooks | | 14 | Windows `komorebic visible-windows` lists unreadable windows with empty strings | a shared `visible_window_details` needs infallible details | restore the skip | | 15 | `Layout::Custom` stays behind a Windows hook in the shared Workspace | unreachable on KDE; no KDE change | move the arms later | | 16 | The KDE `vm-commands` handover failure is a test-precondition bug: fix the scenario, keep the check strict | an empty workspace cannot recalculate its layout; a check that cannot pass tests nothing | weaker check (rejected) | | 17 | Freeze new behavioural stacks and stop the model move at the `WindowManager` boundary until gates catch up | shared text is 26-37%; gates were the critical path | resume sharing after gates | | 18 | Revert the 1 s subscriber write timeout; fix the stale bar-running flag, the hwnd writer, dead-window bookkeeping, and add `komorebi-core` to default members | the timeout can truncate a notification and does not bound the stall | none (design needed: a bounded queue per subscriber) | | 19 | Owner: KDE and macOS get the Windows pointer-picks-the-monitor behaviour for the workspace commands | consistency across platforms | per-platform preamble | | 20 | Owner: explore deeper deduplication where it improves cross-platform testing | tests protect all three | stop at the boundary | | 21 | Owner: software OpenGL fallback for the Windows bar (eframe `wgpu`, glow first, retry on failure) | the e2e guest has no OpenGL 2.0 | remove the feature | | 22 | macOS keeps its own `Workspace::update` | adopting `update_shared` changes scrolling and cannot be verified without a Mac | adopt after the Mac pass | | 23 | Formatting of real sources is applied last, with `tools/format-all` | formatting early conflicts with every open stack | none | | 24 | The rule-list lock order is documented at the statics of each daemon (KDE, macOS, Windows) | two orders deadlocked on reload | none | | 25 | Constant commit stamp (`nix-check`, `e2e`) in checks and e2e builds; only `packages.default` carries the real commit | a docs-only commit must be a cache hit | none | | 26 | Gates are batched into candidates; a rebase invalidates a claim, so the gate is re-run at the tip | one revision compiled only because of a later fix | none |