100 Commits
Author SHA1 Message Date
LGUG2ZandClaude Sonnet 5.5 a4fb4c5f8f docs: the docs site and schemars-docgen are documented as part of the komorebi repository
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:05:38 -08:00
LGUG2ZandClaude Sonnet 5.5 9fce48d9d7 ci(starlight): the docs site is built when its files change, informationally
The `starlight` filter of ci-paths.yaml, and a job that runs `npm ci` and `npm run build` in starlight/. It is not
in the needs of `required` yet: the site has not been built by CI before.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:05:07 -08:00
LGUG2ZandClaude Sonnet 5.5 f19788100b docs(starlight): regenerate the macOS references with just docgen
The pages the unified macOS binaries and schemas make, as of this commit: the commands the site did not
have yet (consume, expel, pan and others) and the changed help of the rules. The Windows pages are not
regenerated: komorebic for Windows does not run on this host.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:03:32 -08:00
LGUG2ZandClaude Sonnet 5.5 47cde0d279 build(just): docgen writes the pages of the docs site into starlight/, in their final form
`just docgen` takes no path now: it runs schemars-docgen from the tree (a member of the workspace) and
komorebic, writes the schema pages and the per-command pages into starlight/, and runs the script of the
site that makes the komorebic page of the platform from them, so nothing is left for `npm run build` to
generate. Only Windows and macOS have pages in the site.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:03:32 -08:00
LGUG2ZandClaude Sonnet 5.5 01f16a2335 ci: schemars-docgen is linted and tested with the rest, and left out of the Windows tests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:02:07 -08:00
LGUG2ZandClaude Sonnet 5.5 3396a7f1ed build(nix): schemars-docgen is built and checked by the flake, and in the dev shell
nix/docgen is nix/nixgen for the other tool: its own platform in workspace-src.nix (a stub in every other
platform, the templates it embeds kept), docgen-lint and docgen-test in the default check sets, and the
package in the dev shell, where `just docgen` runs it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:00:31 -08:00
LGUG2ZandClaude Sonnet 5.5 14c9b615c7 build(deny): schemars-docgen is under the Komorebi license, as schemars-nixgen is
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 14:00:25 -08:00
LGUG2ZandClaude Sonnet 5.5 b9f42cc0e4 style(docgen): the lints and the format of the workspace
clippy --fix and cargo fmt with the toolchain and rustfmt.toml of this repository, so that the clippy check
the flake runs with -D warnings passes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 13:59:28 -08:00
LGUG2ZandClaude Sonnet 5.5 4ecb2b9924 build(workspace): schemars-docgen is a member of the workspace, not a default member
It is a development tool, like schemars-nixgen: the Cargo.lock and the manifest take its dependencies, and tera
takes the values of serde_json itself, so it stays off the workspace's serde_json_lenient.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 13:59:27 -08:00
LGUG2Z c33464613c merge: the history of komorebi-starlight 2026-11-10 13:57:36 -08:00
LGUG2Z 84b2c3d04b merge: the history of schemars-docgen 2026-11-10 13:57:36 -08:00
LGUG2Z 739b0881d3 chore(layout): move the files of the repository to where they live in the komorebi monorepo 2026-11-10 13:57:20 -08:00
LGUG2Z 9ed5e0c625 chore(layout): move the files of the repository to where they live in the komorebi monorepo 2026-11-10 13:57:20 -08:00
LGUG2Z 2bbc36c87b fix(ci): run the whole recorder and the e2e tools on the macOS runner
The macOS job named 36 of the recorder's 731 tests by the module paths of its macOS sources. Its
tests are platform-neutral by module but worded in cfg!(target_os = "macos") branches (the options of a
recording, the keys of the viewer, the frames), so a Linux or Windows run takes the other side of
each and only a Mac takes this one, and a filter by name left most of that untested and would go stale
with every module. It runs the whole suite now: it compiles the same test binary the filter needed and
adds about 30 s of tests. In a depth-1 git checkout, as the runner has, all 731 pass (3 skip by design).
The filterset recorder-macos of tools/test-partition is gone with its use.

The test window and the agent of the e2e guests (class tool) have macOS sources (AppKit, the
accessibility API) that no other job compiles, and the tests of the test window's command line ran
nowhere. The job runs their tests and clippy.

With the os_sensitive step before it, every class of tools/test-partition that has macOS tests has a
run on a Mac: the macOS packages (macos-nextest), os_sensitive, the mixed ones (komorebi-client and
komorebi-bar in macos-nextest, the recorder whole) and tool.
2026-11-06 16:10:03 -08:00
LGUG2Z 5df5c5c3c5 fix(ci): run the os_sensitive packages on the macOS runner, as on the Windows one
Packages whose tests depend on the OS are in the class os_sensitive of
tools/test-partition.json, and a test of one can only pass on the host whose
behaviour it checks. windows-native-test runs them on Windows because the
Linux run cannot stand in for it. macos-hosted did not: macos-nextest builds
with -p of the macOS packages, so komorebic-core, whose docs_cli test
compares docs/macos/cli with what the macOS command line renders, ran on
no Mac in CI, and relied on the Linux run until the wording of Class
differed per platform (run 42311003287).

macos-hosted now runs `cargo nextest run` of the os_sensitive packages in the
native dev shell (92 tests; they pass on a Mac, checked locally). The cost is
macOS minutes on a public repository, which are free, and a compile of four
crates and their dependencies, a few minutes without a cache. docs/dev/ci.md
says so.
2026-11-06 16:07:50 -08:00
LGUG2Z 24bdab8c2a fix(test): build the macOS pages test on a macOS host only
`komorebic-core::docs_cli every_macos_page_is_as_checked_in` failed in the linux-test run (1 of 1633 tests):
six pages of docs/macos/cli looked stale. ApplicationIdentifier::Class has three wordings through cfg_attr
(Class on Windows, "Accessibility subrole or role, such as AXStandardWindow" on macOS, "Role or subrole"
elsewhere) and the command line renders the wording of the host it is built for, so a Linux host cannot
render the pages as they are checked in, and the pages are right. The test skipped on Windows only, on the
belief that the linux run covered it, which stopped being true when macOS got its own wording.

The test file is built for a macOS host only now (#![cfg(target_os = "macos")]), so on another host there is
no test, where an early return would have been one that passes without having checked. Its remaining
guard, a missing docs/macos/cli, is an assertion: it was for the source tree of the Windows workspace, which
does not build the test any more.
2026-11-06 16:07:50 -08:00
LGUG2Z e0ffc1992d test(e2e): drop the known failures of layout_flip on macOS
The mirror checks were not off because the daemon tiles asymmetrically: the
agent read a stale work area (see the previous revision). With the displays
read fresh, layout_flip passes 62 of 62 without the marks, on five runs.
2026-11-06 15:18:25 -08:00
LGUG2Z fb5f23a67a fix(e2e-agent): read the displays of the macOS guest in a process of their own
NSScreen keeps the visible frame it had when the process started: turning the
run loop does not refresh it, and the agent, which waits in accept, is up for
the whole run. The Dock of the guest shrinks its icons once the first test
windows are open (1280 points are not wide enough for the persistent apps and
the windows), its height going from 74 to 65 points, so the work area gains 9
points at the bottom, and the agent kept saying y 30, height 696 while a fresh
process said 705. The daemon reads the screens as it starts, so a shared check
that took the work area from the agent held the daemon to an area that was not
the system's, and the mirror checks of layout_flip were off by the difference.

The verb now runs the agent again with --print-displays, which prints what a
new process sees and exits, and answers in process only if that fails.
2026-11-06 14:54:37 -08:00
LGUG2Z 55339cfcb3 test(e2e): the macOS class checks pass, so they are no longer marked
The test window now has a class (its accessibility subrole), and the state has it, so
manage_rules, state_fidelity and rules_class pass against a real guest: the three
known failures of the class difference and the skip of rules_class are removed. A run
with a mark on a check that passes reports it as a failure, so they could not stay.
placement stays skipped, for the initial_window_placement_rules the daemon does not have.
2026-11-06 14:49:36 -08:00
LGUG2Z bb3c986f26 feat(e2e): give the macOS test window a class by its accessibility subrole
--class was accepted and ignored on macOS, so the scenarios that match a window by
a class could not run. The test window now sets its accessibility subrole to the
class (NSWindow setAccessibilitySubrole), and the host passes the app id of spawn as
--class.
2026-11-06 14:49:36 -08:00
LGUG2Z 1849547153 docs(macos): say what Class is on macOS in the schema and the komorebic help
The schema and the help of komorebic described the Class identifier as 'Role or
subrole'. It is the accessibility subrole of the window, or its role when it has no
subrole, and a rule matches either.
2026-11-06 14:49:36 -08:00
LGUG2Z cf60d00e75 feat(macos): the class of a window is its subrole, or its role without one
macOS has no window class, so a Class rule matched the role, which every ordinary
window shares (AXWindow) and which tells nothing: the subrole is what tells an
ordinary window (AXStandardWindow) from a dialog, a floating panel or a system
dialog. A Class rule already matched either of the two when the daemon decided to
manage, float or place a window on a workspace; the state showed neither as a
class, and an initial placement rule saw only the role.

The class of a window is now its subrole, or the role when it has none
(window::class_of). The state of a window has it as `details.class`, as the
state of a Windows or a KDE window does, so the tools that read the state read it
the same on every platform; a state from an older daemon without it still parses.
The rule a toggle of floating adds names the class, and ignore-rule matches a
window once when it matches by both role and subrole. A rule that names AXWindow
keeps matching, because rules take either.
2026-11-06 14:49:36 -08:00
LGUG2Z 7d584a809a fix(macos): refresh the known windows after a workspace rule moved one
enforce_workspace_rules moves a window between workspaces at the start of
an event but known_window_ids is only rebuilt at the end of process_event,
so it still said the window was on the focused workspace. A Show for the
application in the same event then passed the 'already associated with
another workspace' check, found the window in no container of the focused
workspace, and created a container for it there again: the window ended up
in the containers of both workspaces and in view on the one the rule sent
it from. Windows guards the same place by looking for the window in every
workspace before adding it; here the index is brought up to date when the
enforcement moved something.
2026-11-06 14:40:50 -08:00
LGUG2Z cf845a1fba fix(e2e): run closing as the Windows runner does, and list what the macOS guest cannot run
closing needs the window that was opened last, which the Windows runner passes and this one did not,
so it never ran. maximizing uses `komorebic toggle-maximize`, which the macOS komorebic does not
have, and resize returns without a check when the platform has no pointer: both counted as passes of
their ten tiling checks. They are listed as skipped now, with the reason, as is the sweep of every
command, which is not wired to the macOS command set yet.

The two marks on the initial workspace rule go: that check fails on some runs and passes on others,
and a mark on a check that does both says it can go every other run.
2026-11-06 14:40:50 -08:00
LGUG2Z 560e542c53 docs(e2e): stacks and fullscreen are how macOS works, not defects of the daemon
The reasons of the two marks said the daemon fails to put a stack's windows away and has no fullscreen
detection. Both are how macOS is meant to work here: a stack is the focused window raised over the
others, and a fullscreen window is a Space of its own that the daemon does not tile. The checks stay
marked, because the shared scenarios ask what Windows and KDE do, but the reasons say that.
2026-11-06 14:40:50 -08:00
LGUG2Z c9ca340d4e feat(e2e): mark the initial workspace rule of the macOS daemon, and mark per scenario
An initial workspace rule applied to a window leaves it on the first workspace as well as putting it
on the one the rule names: after it, the state has the window in the containers of both, and it is
in view at its tile (the daemon's log says it hid and restored it). It is the same on every run, in
the scenario and on its own, so the two checks about it are marked as a known failure of the macOS
daemon.

The marks are of one scenario each now: the check that says a window is put away while another workspace
is in view is a check that passes in rules_exe, and a mark on its words would have said so.
2026-11-06 14:40:50 -08:00
LGUG2Z 8dc6390eb1 fix(e2e): wait for the macOS daemon to take up a command, and for its file watcher
komorebic is done when a command is in the daemon's queue, not when it has run, and a scenario opens a
window right after giving a rule or a name: both are events of the same queue, and the window could be
first. The platform asks for `state` after every command that changes something, which the daemon
answers in the order it got things (the rules scenario failed a different check on each run before,
and its named-workspace checks pass three runs in three now).

The daemon watches its configuration file with FSEvents, and a file written in the first half second
after it answered is never noticed (0 s: not reloaded in 12 s; 0.5 s: reloaded after 2.6 s; later
ones after two or three seconds). A start waits a second for it, as nobody edits a file that fast.
config_reload goes from 6 of 8 checks to 8 of 8.
2026-11-06 14:40:50 -08:00
LGUG2Z 5257e11b8d feat(e2e): what the macOS daemon does differently, marked, and what it does not have, skipped
The first run of the scenarios against a real guest ended with 32 failing checks of 586. A window the
daemon puts away is parked with a sliver showing (fixed in the platform), and the rest are differences of
the macOS daemon, marked on the checks that are about them, with the reason:

- stacks: it stacks by raising the focused window over the others, and never puts the others away
- fullscreen: it has no fullscreen detection, and tiles a window over the whole display
- layout_flip: it tiles 10 px below the top of the work area and 12 px above its bottom
- class: a window has none, the daemon matches the role, which every test window shares

placement is skipped as a whole: the macOS daemon has no initial_window_placement_rules.
2026-11-06 14:40:50 -08:00
LGUG2Z 25f50076af feat(e2e): mark one check of a scenario as a known failure of a port
A step could be marked as a known failure of a port, which hides every failing check of it: too
coarse for a scenario in which one or two checks are about a difference of the port and the others
are not. RecordedChecks takes marks now: a part of the description of a check, and the reason. A
marked check that fails is recorded as a known failure, as a step is, and one that passes is a failure
of its own, so that a mark does not outlive the difference it was for. A port that gives no marks
is as it was.

The run of the runner's own subprocess calls says it does not check them.
2026-11-06 14:40:50 -08:00
LGUG2Z 098b7d011e fix(e2e): a window the daemon parked with a sliver showing is put away
macOS does not let a window go entirely off the screen, so the daemon hides one at the corner of
its display with one pixel of it showing (five for a window that scrolled away). The port
called a window hidden only when no pixel of it was on a display, so it said every window the
daemon had put away was in view. A window is put away now when no more than a sliver of it shows,
in either direction.

Against a real guest this takes workspaces, workspace_moves, monocle, lifecycle_hidden, rules_exe
and startup_rules from failing to passing (18, 18, 22, 23, 5 and 5 checks).
2026-11-06 14:40:50 -08:00
LGUG2Z 18d7f9a506 docs(e2e): how the macOS guest works and how to run it
A README for e2e/platforms/tart_macos: what a run does, why the grants live in the golden image and
are inherited through Terminal, why the agent is a Rust program (the stock image has no developer
tools), and the limits (one display, no input, two guests per host, Apple hardware only, so not in
CI). docs/dev/ci.md and e2e-to-unit.md said macOS had no integration coverage; they now say where it
runs and what it cannot reach.
2026-11-06 14:40:50 -08:00
LGUG2Z 98b3b4bc0c build(nix): the programs of the macOS e2e guest, and nix run .#e2e-macos
komorebi-e2e-macos builds the test window and the agent for macOS in a derivation of its own, on the
dependency artifacts of the macOS build, so that fullBuild (what users get) does not change. The
e2e-macos app, on aarch64-darwin, runs the scenarios of the shared core against the golden Tart image
with this checkout's daemon, as e2e-windows does for Windows: tart comes from nixpkgs, where its
licence makes it unfree, so only it is allowed, here. KOMOREBI_E2E_BINS runs another build, and
KOMOREBI_E2E_TART another tart.
2026-11-06 14:40:50 -08:00
LGUG2Z fa2742a75d feat(e2e): scripts that build the golden image of the macOS guest and its programs
build-golden.sh makes the golden Tart image from a vanilla one of Cirrus Labs: it provisions the
guest over SSH (provision.sh: the agent starts at login from a Terminal window, no state of earlier
runs is left, Terminal does not reopen old windows), stops for the one step that cannot be
scripted (a person allows Screen Recording and Accessibility for Terminal, once, and the grants live
in every clone), checks that the agent answers, and shuts the guest down from inside, so that
the last writes are on the disk the clones copy. build-bins.sh builds the programs under test for
the host and points the libiconv of the nix store at the system's, as the nix package does.
2026-11-06 14:40:50 -08:00
LGUG2Z 77e43d1602 ci(e2e): run the unit tests of the macOS port with those of the Windows one
The check that runs the Python of the e2e guests that needs no guest (windows-e2e-tests, on
Linux) now also runs platforms/tart_macos/tests: the ignore rules, which windows count as hidden,
the paths a scenario may ask for, and that every scenario the macOS runner names exists in the core.
2026-11-06 14:40:50 -08:00
LGUG2Z 6823d13ddd feat(e2e): a runner for the macOS guest
python -m platforms.tart_macos.run GOLDEN BIN_DIR OUT SCENARIO... gives each scenario a fresh
clone of the golden image, uploads the programs under test, starts the daemon and runs the shared
scenario, on the checks, step loop and result file the Windows runner uses (komorebi_vm.runner).
The scenarios a guest cannot run (a second display, a pointer, a lock screen, a window class) are
listed with the reason and skipped, not failed.

The unit tests of the port need no guest: the ignore rules, which windows count as hidden, the
paths a scenario may ask for, and that every scenario the runner names is a module of the core.

tiling (10 checks) and focus (17) pass against a real guest.
2026-11-06 14:40:50 -08:00
LGUG2Z 9b236b14be feat(e2e): a macOS guest and platform for the shared scenarios
A clone of a golden Tart image is booted without a window and its agent (komorebi-e2e-agent, started
at login from the Terminal that holds the Accessibility and Screen Recording grants) is the only
thing the host talks to: no SSH, no password. MacosPlatform implements the Platform protocol
through it: the windows and displays are what the accessibility API says, the daemon is a program
the agent starts, a window the daemon put away is one off every display, and a screenshot is
screencapture.

A guest has one display and nothing here injects input, so has() says no to a pointer and to the
lock screen, and a second display is refused. The windows of the guest's own programs (the
Terminal, the desktop widgets of the image) are left to themselves by an ignore rule the platform
adds to every configuration.
2026-11-06 14:40:50 -08:00
LGUG2Z 80f221b8e7 refactor(e2e): one host client for the agent of every platform
The host side of the agent spoke a protocol that is the same for every platform, and lived in the
Windows port. Move it to komorebi_vm/agent.py, which the Windows module re-exports so that its
imports stay as they are, and give it the calls the protocol gained: spawn with an environment and
an output file, `status` of a spawned program and `send` to its standard input.
2026-11-06 14:40:49 -08:00
LGUG2Z ceaea8ca1b refactor(e2e): lift what a runner needs of a port out of the Windows runner
RecordedChecks (the checks a runner keeps for its result file), the step loop with its timings
and known failures, the commit under test and the step keyword helper have nothing to do with
Windows. Move them to komorebi_vm/runner.py, with the known-failure tables passed in, so that a
runner of another port uses them instead of importing a sibling port or copying them.

The Windows runner keeps its tables and the names it and its tests use. The unit tests of the core
and of the Windows runner pass unchanged (163), and ruff reports what it did before.
2026-11-06 14:40:49 -08:00
LGUG2Z f4715a3465 feat(e2e-agent): the macOS verbs on the shared protocol, in the one agent crate
Move the macOS agent into komorebi-e2e-agent as macos.rs, answering the protocol of protocol.rs
with its own verbs (os_windows, displays, activate, minimize, close, move; a window is found by
its title), and remove the separate komorebi-macos-e2e crate that had copied the protocol. There
is one binary name and one port on every platform, so the host has one client and one description
of the protocol.

AppKit is for the main thread, so the macOS agent serves one connection at a time on it, where
the Windows one serves each on a thread. A spawned program on macOS gets a standard input that
stays open, as the test window leaves when its input closes. The crate is built for macOS and
Windows now and stubbed on Linux, as komorebi-testwin is.
2026-11-06 14:40:49 -08:00
LGUG2Z 71a9018e4b refactor(e2e-agent): split the protocol from the verbs of a platform
The agent of the Windows guest speaks a line protocol (one JSON request, one JSON answer, raw bytes
after the line for put_file and get_file) that has nothing to do with Windows. Move it into
protocol.rs, with a trait for the verbs a platform adds, so that another platform answers the same
protocol instead of copying it, and the host has one client.

The Windows verbs (os_windows, displays, activate, minimize, close) and the code that reads them
from the session are moved to windows.rs, not changed; the agent still serves each connection
on a thread of its own. What the protocol gains for every platform: an environment and an output
file for a spawned program, a `status` of one, and a `send` to its standard input, which the
Windows agent does not use yet. The port is one for every platform, as the Windows one already was.
2026-11-06 14:40:49 -08:00
LGUG2Z 0f5cfc8b99 fix(e2e): the macOS agent keeps its children's input open, can write to it, and sees new windows
A spawned program now gets a pipe for its standard input that the agent keeps open: the test window
leaves when its input closes, which with /dev/null was at once. A new `send` verb writes a line to
it, which is how a scenario gives the window a command (fullscreen, title, resize).

The workspace keeps its list of running applications, and which one is active, up to date from
the main run loop, and the agent waits in accept: it listed the applications of the time it
started and never saw a window that opened later. It now lets the run loop turn before it lists
windows.
2026-11-06 14:40:49 -08:00
LGUG2Z 6ed535be38 feat(testwin): a window for macOS, on the same command line and commands
An AppKit window of a regular application, so that the daemon sees it as it sees any program: the
same options and standard input commands as the Win32 one (title, colour, size, minimum size,
fixed, late rename, fullscreen as a borderless window over its display, report with the frame in
points from the top left of the main display, close, exit). The main thread runs the event loop
in short turns and takes the lines the input thread queued; the program leaves when its window is
closed. A window class has no meaning on macOS, so --class is accepted and ignored.

The macOS source set now builds the crate; Linux still stubs it.
2026-11-06 14:40:49 -08:00
LGUG2Z 23b23a6b66 refactor(testwin): share the command line and the commands across platforms
The test window of the e2e suite is a Win32 program whose command line (--title, --class,
--color, --size, --min-size, --fixed, --rename-to, --after) and standard input commands (title,
color, resize, report, close, exit) are the contract every scenario port relies on. Move what
knows no window system into cli.rs, with tests that run anywhere, and the Win32 window into
win32.rs, so that another platform can implement the same window against the same contract.

The Windows window itself is moved, not changed: it reads its options and its commands from
cli.rs now, and the colour is converted to a GDI one where it is used. A fullscreen command is
added to the shared set; on Windows it does what the WM_APP + 2 message does.
2026-11-06 14:40:49 -08:00
LGUG2Z 6b8ab7d959 feat(e2e): a guest agent for macOS that answers the host and describes windows
The Windows guest has komorebi-e2e-agent; this is its macOS counterpart, speaking the same
protocol (one JSON line per request, raw bytes after it for put_file and get_file) so that the
host client is the same. Beyond exec, spawn and file transfer it answers with the windows the
accessibility API gives (title, frame, focus, minimized) and the displays with their work areas,
and it can activate, minimize, close and move a window.

It has to be started by a program that holds the Accessibility and Screen Recording grants, so
that the daemon it spawns is attributed to that program.
2026-11-06 14:40:49 -08:00
LGUG2Z a90147e3ff fix(tools): fast-check builds its native and KDE checks for the host, from any shell
The default dev shell exports CARGO_BUILD_TARGET=x86_64-pc-windows-msvc for the Windows cross
build. The native and the KDE checks run `cargo test` through `nix shell nixpkgs#gcc`, which is
not a dev shell and keeps the environment it was started from, so run from inside `nix develop`
they built their test programs for Windows and failed with "cannot execute binary file" (exit
126), the failure the recorder step of the macOS job had in CI.

Both lines now drop the variable. The macOS check names its target and the Windows check wants
the variable, so they stay as they were. With the variable exported, the native line fails with
126 before and passes (139, 8, 14, 20 and 16 tests) after.
2026-11-06 11:39:39 -08:00
LGUG2Z f0731112a5 fix(ci): run the recorder's macOS tests in the native dev shell
The step ran `cargo nextest` in `nix develop`, whose default shell exports
CARGO_BUILD_TARGET=x86_64-pc-windows-msvc for the Windows cross build. On the
macOS runner that made cargo build the recorder's test executables for Windows,
and nextest then failed to list them:

  for `komorebi-recorder::assert`, command `.../target/x86_64-pc-windows-msvc/debug/deps/assert-ce31ab528bf795df.exe --list --format terse`
  exited with exit code 126 ... cannot execute binary file

Use `nix develop .#native`, the shell without the cross environment that exists
for exactly these `cargo -p komorebi-<platform>` commands. The schemas step
keeps the default shell: `just jsonschema` runs cargo through `env -u
CARGO_BUILD_TARGET`. docs/dev/ci.md notes the shell.
2026-11-06 11:34:42 -08:00
LGUG2Z df2f3ae0ad fix(ci): run the macOS job on macos-latest right after lint, on every change to its paths, and drop the self-hosted one
macos-14 was retired on 2 November 2026 and the runner label was no longer
supported, so macos-hosted was cancelled and failed required.

Only Apple silicon and the latest macOS are supported, and a standard hosted
runner is free on a public repository, so:

- macos-hosted runs on macos-latest
- it runs on a pull request that touches the macOS paths, without the label
  ci:macos
- it needs only changes and lint, like windows-native-test, and no longer
  waits for the KDE VM suites
- macos-selfhosted.yaml, its install-nix input, its path filter, the label
  ci:macos-self and the variable MACOS_SELF_HOSTED are removed
2026-11-06 10:04:26 -08:00
LGUG2Z 9c5f0c459f fix(ci): measure the free space of a runner before deleting toolchains from it 2026-11-06 09:41:31 -08:00
LGUG2Z 865ffcebda docs(ci): what is guaranteed about KVM on hosted runners, what is drawn again, and what is not covered 2026-11-06 09:18:43 -08:00
LGUG2Z 996330f19b fix(ci): draw the host of a VM suite again when it has no KVM, in the same run
A hosted runner of the ubuntu-24.04 pool is not promised nested virtualization, and
3 of the 16 suites of run 42182554717 failed the check of /dev/kvm. rerun-no-kvm.yaml
could not help: a workflow_run workflow runs only from the default branch, where the
file is not, and a run cannot rerun its own failed jobs while it is in progress.

The step "Enable KVM" of the nix action is now the only one that enables KVM
(enable_kvm of install-nix-action is off), runs before the disk and the install,
waits for udev to settle and polls /dev/kvm for 30 seconds instead of testing it
once, 42 ms after an asynchronous udevadm trigger, and prints what tells a missing
device from a rule that was not applied yet. With defer-no-kvm a host without KVM
is an output, not a failure.

_vm.yaml calls _vm-attempt.yaml up to three times: the suites that found no KVM are
listed through the artifacts of the run and run again on new machines, and the last
attempt fails them. vm-fast and vm-slow are calls of it. rerun-no-kvm.yaml is
removed.
2026-11-06 09:18:43 -08:00
LGUG2Z d57b2d7ed3 fix(ci): build from source when a download from the cache fails
A NAR of 460 MB was cut by the edge of the cache ("Stream error in the HTTP/2 framing layer") and the
resumed request failed the same way, so the job kwin-events failed before its VM test started. With
fallback = true Nix builds the derivation from source when a substitute cannot be downloaded: one dropped
connection costs minutes of compiling in that job, not the job. HTTP/2 is left at its default: nothing
shows that turning it off prevents the error.
2026-11-06 09:17:08 -08:00
LGUG2Z 97e1637393 fix(test): do not check the pages of the cli for macOS on a Windows host
ApplicationIdentifier::Class is described per platform ("Class" on
Windows, "Role or subrole" elsewhere), and the description is taken from
the host the test is built for. every_macos_page_is_as_checked_in renders
the pages of komorebic for macOS from the command line, so on the Windows
runner six of them (ignore-rule, manage-rule, workspace-rule,
named-workspace-rule, initial-workspace-rule and
initial-named-workspace-rule) came out with "- class: Class" and were
reported as stale, though the checked-in pages are right.

The pages are the same on the hosts that have the macOS wording, and the
linux test run checks them, so skip the test on Windows.
2026-11-05 23:41:05 -08:00
LGUG2Z 8642f7b184 fix(ci): quote the description of the push input of the nix action
An unquoted ": " inside the description made action.yaml invalid YAML ("mapping values are not allowed in
this context"), so every job that uses the action would have failed at its first step.
2026-11-05 16:48:38 -08:00
LGUG2Z 82366f7134 fix(nix): evaluation no longer reads store paths that builtins.path makes
nix flake check --no-build failed in the nix-eval job on the runner with "path ...-Cargo.lock is not
valid": crane's vendorCargoDeps runs pathExists on the lock at evaluation time, and `repoFile` is a
store path that builtins.path makes during evaluation. Nix 2.35 with an empty store, as on a fresh
runner, does not have it yet under --no-build; Lix, and a store that had seen the file, do.

The parsed lock now comes from the flake source (cargoLockParsed in nix/lib.nix), and the Windows guest
reads tools/test-partition.json the same way. The derivations are unchanged (same drv paths), so no
cache key moves. Reproduced and fixed with upstream Nix and an empty store:
nix shell nixpkgs#nixVersions.latest -c nix flake check --no-build --all-systems --store <empty dir>
2026-11-05 16:48:10 -08:00
LGUG2Z 166e5f353a ci(cachix): write to the cache on every push, read it in every job that runs nix
The write token goes to cachix-action on a push (master and the temporary komomono
trigger, without naming either), a manual run and a schedule; a pull request, a fork
and the merge queue only read. The flake-lock update goes through the nix action too,
and the nixpkgs canary reads the cache but never pushes (push: "false" replaces
cache: "false").

docs(ci): the write rule, the read-only path and a checklist for the first push to
komomono.
2026-11-05 16:40:29 -08:00
LGUG2Z 17fb726ecf ci(hygiene): print the tool versions of the flake, and write down that stage 0 runs the latest
The tools of stage 0 are the latest releases, taken from the flake, and the code is changed to satisfy
them, not the other way round: every run says which versions it had, and docs/dev/ci.md says how they
are kept current (the weekly lock update, and nix flake update rust-overlay or nixpkgs in between).
2026-11-05 16:37:59 -08:00
LGUG2Z cb2f49dc17 build(nix): update rust-overlay, so the nightly rustfmt of the checks is the latest
The pin was a week old: the hygiene shell had the nightly of 2026-10-27. The nightly of
2026-11-04 formats the whole tree unchanged. Every other tool of the shell was already the latest
release (actionlint 1.7.12, shellcheck 0.11.0, cargo-deny 0.20.2, ripgrep 15.2.0, jq 1.8.2, cargo 1.99.1).
2026-11-05 16:37:59 -08:00
LGUG2Z d5d5fe7fe7 ci(hygiene): stage 0 runs the tools the flake pins, not the ones of the runner
The first stage ran shellcheck, rustfmt, cargo-deny and actionlint as the runner image, the
rust-toolchain rustup and two actions shipped them, and the runner's older shellcheck reported
SC2317 on a trap handler that the flake's does not. hygiene, fmt and deny now run every step in
`nix develop .#hygiene`, so CI and a local run use the same versions.
2026-11-05 16:31:20 -08:00
LGUG2Z 194fbd4281 build(nix): a hygiene dev shell with the pinned tools of the first CI stage
devShells.hygiene is the toolchain with the nightly rustfmt, cargo-deny, shellcheck, actionlint, jq,
ripgrep and git, without the Windows SDK, the VM tooling and the rest of the full shell, so that the
first stage sets up in seconds and a local `nix develop .#hygiene` runs what CI runs.
2026-11-05 16:31:20 -08:00
LGUG2Z 15c8a04459 ci(actionlint): run actionlint 1.7.12 in the hygiene job
1.7.7 does not know the hosted Arm Windows runner and reported windows-11-arm as an unknown label.
1.7.12 is the latest release and the version nixpkgs gives `just lint`, so the hygiene job and a
local run now agree.
2026-11-05 16:21:14 -08:00
LGUG2Z 658827fcd5 fix(lint): build errors with Report::msg, not the eyre! macro the workspace bans
The ban (`just lint`, and now the hygiene job) came from the macOS and KDE repositories and was
never applied to the Windows code, so the unified tree had sixteen uses of the macro: seven in
the Windows daemon, six in komorebi-core (written during the unification), one in the KDE
backend and two in the recorder. Each becomes eyre::Report::msg, with format! where the message
has arguments.
2026-11-05 16:13:37 -08:00
LGUG2Z 416cda802d docs(ci): the ladder, the gaps, the settings the owner changes and the komomono checklist 2026-11-05 15:51:50 -08:00
LGUG2Z 2e649ef8e5 ci(windows-guest): an experimental, manual workflow builds the Windows guest and runs the Windows tests in it 2026-11-05 15:51:50 -08:00
LGUG2Z 04c79adefd ci(sweeps): the slow VM suites, the NixOS evaluation and the nixpkgs canary run outside the ladder; the flake lock is updated weekly 2026-11-05 15:51:50 -08:00
LGUG2Z e727f6b14d ci(macos): the macOS jobs run on the self-hosted Mac when the repository enables it 2026-11-05 15:51:50 -08:00
LGUG2Z 3a7d3d9e1f ci: the ladder of stages, with one required aggregator, and the reusable macOS jobs 2026-11-05 15:51:50 -08:00
LGUG2Z 21e86824e4 ci(paths): .github/ci-paths.yaml says which files concern which platform 2026-11-05 15:51:50 -08:00
LGUG2Z d49b155045 ci(actions): a composite action installs nix, enables KVM and reads the Cachix cache 2026-11-05 15:51:50 -08:00
LGUG2Z e23bfcfb36 build(nix): the macOS options and nix-darwin module validations are checks on every system
They take a plain pkgs and evaluate against any nixpkgs, so a Linux runner can build them. They stay in the macos-options set.
2026-11-05 15:51:50 -08:00
LGUG2Z f05d7fe737 build(just): test-partition-check, test-windows (the partitioned subset) and test-windows-full (the whole Windows suite by hand) 2026-11-05 15:51:50 -08:00
LGUG2Z 56afe5b286 build(nix): the tests of komorebi-macos-core run in linux-nextest and macos-nextest 2026-11-05 15:51:50 -08:00
LGUG2Z 98295e0065 build(nix): windows-nextest runs the windows and os_sensitive packages of the partition, windows-nextest-full all of them 2026-11-05 15:51:50 -08:00
LGUG2Z 8e1a2b261c refactor(macos): the animation maths, the arrival record and the enhanced-UI rules move into komorebi-macos-core, which needs no macOS 2026-11-05 15:51:50 -08:00
LGUG2Z b83cf06cc6 feat(tools): test-partition says which packages are tested where, and fails when the classification and the workspace disagree 2026-11-05 15:51:50 -08:00
LGUG2Z 3464fb0c58 test(transport): the listener tests run on Windows too, against the uds_windows listener the Windows daemon uses 2026-11-05 15:51:50 -08:00
LGUG2Z a81a1120cd docs: update paths for crates/ and the komorebi directory, add a contributor path map 2026-11-05 15:03:27 -08:00
LGUG2Z c234b1d884 build(tools): route the justfile and tools to crates/ 2026-11-05 15:03:27 -08:00
LGUG2Z 279ac0f759 style: format the files the path edits touched with rustfmt and nixfmt 2026-11-05 15:03:27 -08:00
LGUG2Z 259575a1b3 build: route the workspace to crates/
The workspace members, the path dependencies, the include_str! and test
fixture paths, the recorder's indexed source keys, and the Nix source
filter and path literals follow the new layout. Cargo.lock is unchanged.
2026-11-05 15:03:27 -08:00
LGUG2Z 6def6bdfbf refactor(layout): move library and dev-tool crates under crates/, rename komorebi-daemon to komorebi
Pure renames, no content changes. This revision does not build on its own;
the next one routes the workspace to the new paths.
2026-11-05 15:03:27 -08:00
LGUG2Z fdfb0cef73 chore(deps): dependencies.json lists this workspace's crates by name and version
cargo deny list names a workspace crate by its path+file:// source, which is a path on the machine that
ran it (C:/Users/... before, /Users/... now). depgen strips it, and the file is regenerated for all three
platforms' crates.
2026-11-05 15:03:27 -08:00
LGUG2Z 5b3e3f598a build(deny): ignore RUSTSEC-2025-0059, servo-fontconfig is unmaintained
It is reached only through font-loader, which komorebi-bar looks up system fonts with; it was
already in the lock before the recorder and schemars-nixgen merged.
2026-11-05 15:03:27 -08:00
LGUG2Z 112e624c5e build(nix): regenerate the Nix options from the schemas 2026-11-05 15:03:27 -08:00
LGUG2Z b16fedebe7 docs(nixgen): schemars-nixgen has a README, and the docs say how to run it 2026-11-05 15:03:27 -08:00
LGUG2Z a342dc39f5 ci(windows): the Windows tests leave out schemars-nixgen 2026-11-05 15:03:27 -08:00
LGUG2Z 58ecb37f38 build(just): nixgen-linux and nixgen-macos run schemars-nixgen from the tree 2026-11-05 15:03:27 -08:00
LGUG2Z c6d29a9268 build(nix): schemars-nixgen is built and checked by the flake, and in the dev shell
nix/nixgen has the package and the clippy and nextest checks (in the default check sets), from a
"nixgen" platform of nix/workspace-src.nix that is the tool alone; it is a stub in every other
platform's source, so an edit of it rebuilds none of theirs.
2026-11-05 15:03:27 -08:00
LGUG2Z 622646c3f7 build(workspace): schemars-nixgen is a member of the workspace, not a default member
It is a development tool: a bare cargo build leaves it out, publish is off, and no release
ships it. Its manifest uses the workspace dependencies it shares and keeps its own version.
2026-11-05 15:03:27 -08:00
LGUG2Z 3d33fac0cd merge: the history of schemars-nixgen 2026-11-05 15:03:27 -08:00
LGUG2Z e499ebb774 docs(recorder): the recorder is documented as part of the komorebi repository 2026-11-05 15:03:27 -08:00
LGUG2Z a0f7d871e5 ci(windows): the recorder is built with the rest, its tests informational until they have run on Windows 2026-11-05 15:03:27 -08:00
LGUG2Z e803212de2 build(just): the recorder is a target of every platform, with record, icon and build-windows-recorder 2026-11-05 15:03:27 -08:00
LGUG2Z 1db6d7d3fd build(nix): the recorder is built, checked and shipped by the flake
nix/recorder holds the native packages and checks of macOS and Linux (the recorder and the
sources it indexes are the "recorder" platform of nix/workspace-src.nix), the Windows
build has it as one of its members, and the dev shell gets ffmpeg, resvg and GStreamer. The
flake inputs komorebi-mac and komorebi-windows are gone: the sources are in the tree.
2026-11-05 15:03:27 -08:00
LGUG2Z 170a6f51c0 feat(catalog): index the komorebi source in this repository, not a checkout of each komorebi
The recorder is a member of the repository it indexes: each flavor is built from the
directory of its own daemon (komorebi-macos, komorebi-windows, komorebi-linux) and the shared
crates that log. The statements of the container, workspace and monitor moved into
komorebi-core, so the targets of the real log sample in the fixture are komorebi_core's.
KOMOREBI_SRC and KOMOREBI_COMMIT replace the three KOMOREBI_*_SRC variables, for a build
that is not in a checkout.
2026-11-05 15:03:27 -08:00
LGUG2Z 9854f90db7 build(wix): the komorebi installer ships komorebi-recorder, which has no installer of its own 2026-11-05 15:03:27 -08:00
LGUG2Z c64c71541b build(workspace): komorebi-recorder is a member of the workspace, at 0.1.0-dev 2026-11-05 15:03:27 -08:00
LGUG2Z 38a56fd544 merge: the history of komorebi-recorder 2026-11-05 15:03:27 -08:00
LGUG2Z c69fb806b3 build(just): the justfile does the right thing on Windows, macOS and Linux 2026-11-05 15:03:27 -08:00
LGUG2Z 598b57ff9b docs(dev): the plan records the nextest answers and that the sponsor texts are done
N1 is answered: macos-nextest runs in nix on a Mac (383 tests on the Mac Mini), the Windows
unit tests run in the Windows guest through nix run .#windows-nextest (964 tests, 964
passed, one run) with the check windows-nextest-build cross-compiling the 48 test
executables, and the neutral crates' tests run in linux-nextest (1532 tests). The remaining
open point is only whether the first CI also gets a hosted windows-latest cargo test job.
The R6 follow-up (the sponsor-only [FEAT] texts) and 2.2a are marked done.

Verified: documentation only; the numbers are from the agents' reports (win-nextest, and the
Mac Mini run of macos-nextest).
2026-11-05 15:03:27 -08:00
LGUG2Z 5d85e3f719 docs(dev): the plan records the owner's answers on macOS, docs, nightly and the old repositories
What: CUTOVER-PLAN.md records further owner decisions. R2: macOS is part of 0.2.0-dev; the owner tests it and releases 0.2.0 when ready; the macOS hand-check gates only the release, and the pre-release-later idea is dropped. R3: Starlight is the site for all docs, gh-pages is deprecated: new section 4.9 gives the retirement steps and what happens to mkdocs.yml (exclude_docs stays only while MkDocs builds anything) and the Windows docs source. R4: known failures do not block the dev-line landing. R5: published schema names agreed. R7: the owner handles the old macOS repository, so its banner/mirror/archive steps are removed (the KDE repository steps stay). R11 and every nixpkgs step are removed (no upstream package, the flake is the distribution). New section 4.8: the macOS nightly and its Homebrew formula must come from workflows of the joined repository, starting from docs/dev/old-ci/macos/workflows/*; not in the first CI; one nightly release for the whole repository. N1 (nextest) is marked in progress with the owner's two feasibility questions, not decided. Section 6 now lists what is truly open: the push (owner), the macOS hand-check, the self-hosted Windows runner, N1, and the release-time items.

How verified: sources read, nothing executed; the owner decisions are as relayed by the coordinator.
2026-11-05 15:03:27 -08:00