Commit Graph
3123 Commits
Author SHA1 Message Date
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