Symptom

Krill Desktop built .deb (Linux) and .msi (Windows) artifacts and the Compose nativeDistributions block even listed TargetFormat.Dmg, but no pipeline ever produced a macOS installer — so Mac users had nothing to download.

Root cause

Two gaps, neither in application code: (1) jpackage can only emit a .dmg from a macOS host, and there was no CI job running on the existing self-hosted macOS runner; (2) the desktop packageVersion was a hand-edited literal, decoupled from the canonical version.txt. macOS desktop is the same jvm("desktop") target as Linux/Windows, so no new Kotlin target or expect/actual was needed — only a macOS build host, packaging metadata, and a publish workflow.

Fix

composeApp/build.gradle.kts: read packageVersion from the repo-root version.txt, and add a macOS { } block (bundle ID zone.krill.desktop, app name, optional signing gated on the APPLE_SIGNING_IDENTITY env var so v1 ships unsigned). New .github/workflows/Deploy macOS.yml runs on [self-hosted, macOS, ARM64], builds :composeApp:packageReleaseDmg, and publishes versioned + latest keys to s3://cms.krill.systems/distro/macos/ with a CloudFront invalidation (E1MKRDHOFUF59Y, the same distribution the screenshot/SDK-docs workflows use). A Download-category post documents the link and the Gatekeeper open-steps for the unsigned build.

Prevention

When a cross-platform desktop target advertises an installer format (TargetFormat.Dmg), confirm a build host for that OS actually exists in CI — the format being listed does not mean anything ships. Drive every installer’s version from version.txt, never a hand-edited literal, so all platforms stay in lockstep at release time. Platform-specific runtime surfaces in desktopMain (the expect/actuals) are only proven on the OSes CI actually runs the app on; a Linux-only build pipeline silently leaves the macOS paths unexercised — track that as a manual smoke checklist until a macOS build exists.