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