Files
komorebi/docs/dev/MIGRATION-SUMMARY.md
LGUG2Z 1b3178cede docs(dev): the cutover account in the map, the summary and the unified-binaries note
ARCHITECTURE-UNIFIED.md: the status line, section 1 (a table of where each platform is now),
the package paragraph (shims gone, version crate), the oracle paths (schemas/*, docs/*/cli),
section 6 (now "done / still open" instead of "what is still the old layout"), the tier-0 row,
and the helper tools paragraph (tools/ is committed now).
MIGRATION-SUMMARY.md: status line, the three-trees paragraph, the oracle paths, the known
issues paragraph, and section 7, which was "Cutover list (not done)" and is now a table of what
the cutover stack did and what is open; item 7 of "How to continue".
unified-binaries.md: the status banner (with a map from the old directory names to the new
ones), the Mac Mini commands (the shim line removed, schema.bar.json under schemas/macos,
`komorebi 0.2.0`), and "What is left for the cutover" is now a done / still open list.

Still open, as stated in the three: the one flake (flake-one is writing it), CI and release
workflows (the old ones are archived in docs/dev/old-ci), the site and repository URLs,
COMPARE.md. The 0.1.42 / 0.1.0 / 0.1.1 numbers and the old directory names that stay in
narrative sections are history and are marked as such. Not touched: build-and-test.md and
flake.md (owned by flake-one).

Verified: the relative links of the three files resolve. The statements about what the stack
did are taken from the revision descriptions of wip-cutover, not re-measured.
2026-11-05 15:03:27 -08:00

20 KiB

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 (the map), build-and-test.md (commands, flakes), macos-hand-check-plan.md (what the owner must run on a Mac), 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; how to build each is in 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) 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; Windows goes through it for the clusters in 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<W>, Workspace<P> (with update_shared, a skeleton with hooks), Monitor<P>, 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
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
komorebi-bar-core bar configuration, widgets, platform seam, schema views 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<P>/Workspace<P> (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 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<P> is not built: 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.
  • The bar's rendering code (frame margins, network.rs, the window seams in bar.rs/main.rs): step B4 of 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: 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 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 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.
  • 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: (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. Nothing deeper should land on macOS before it.
  2. WindowManager<P>. Blockers per platform: 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<W>, 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<Arc<Settings<P>>> 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<P> 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<P> 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