feat: Linux support — graceful automation skip on non-macOS (fixes #31) - #51
feat: Linux support — graceful automation skip on non-macOS (fixes #31)#51YasienDwieb wants to merge 1 commit into
Conversation
Select the scheduler adapter at the composition root: launchd on macOS, a no-op UnsupportedSchedulerAdapter elsewhere. setup/config no longer crash on launchctl off macOS; render reports scheduling unavailable from the adapter's actual outcome via a new AutomationTaskApplyResult.scheduled field.
|
@YasienDwieb I'm not a maintainer, but I mentioned this PR in #67 as the one that introduces the seam the other platform PRs work around, and it felt wrong to say that without testing it. I'm on Windows 11, so I checked it there — a platform this PR doesn't claim but does affect. It works. Concrete A/B on the two commands that reach the scheduler. On On this branch: I also confirmed the adapter genuinely touches nothing — no Two things I found that aren't objections, just follow-ups you may or may not want in scope: 1. The suite is still red on Windows — the tests bypass the seam. That's arguably correct — they're macOS-behaviour tests — but it means this PR fixes the product without making the suite runnable off-darwin. A 2. {
"task": "sync",
"label": "com.codealmanac.sync",
"plist_path": "C:\Users\...\Library\LaunchAgents\com.codealmanac.sync.plist",
"installed": false
}A Windows-shaped path to a launchd artifact that can never exist. Nothing is written, so it's cosmetic rather than a bug — but it's the field I argued in #67 has leaked from For what it's worth I still think this is the PR that should go in first out #48/#32/#51, because it's the only one that adds a place for the decision to live rather than guarding another call site. If it lands I'm happy to send the skipif markers as a follow-up rather than bundling them here, and I'll keep testing on Windows for anyone who wants it — per #24 the team doesn't have a box for it. |
Fixes #31.
Problem
codealmanac setup --yes(andconfig set automation.*) crash on Linux:create_appunconditionally wiresAutomationServicewithLaunchdSchedulerAdapter, which shells out to macOSlaunchctl. The crash also left a half-configured global state (config.tomlwritten before the scheduler failed).Approach — Option A: graceful skip, not a rewrite
Every lifecycle/read command is already platform-neutral; the only macOS blocker is scheduled automation, and
SchedulerAdapteris already a port. So:UnsupportedSchedulerAdapter— a no-op implementation of the existingSchedulerAdapterport (no subprocess, no~/Library/LaunchAgentswrites; reports "not installed").core/platform.scheduler_supported()— single platform predicate.create_appselects launchd on macOS, the unsupported adapter elsewhere. Platform detection lives only at the composition root.AutomationTaskApplyResultgains ascheduledfield set from the adapter's real result (install().installed). Setup output now says "Scheduled automation unavailable" with a "runsync/gardenmanually" pointer instead of falsely claiming schedules were installed. Render stays free ofsys.platformchecks.Because nothing fails on the unsupported path, the half-configured-state bug is gone as a side effect —
setupcompletes and exits 0.Real Linux scheduling (systemd user timers) is deliberately left as a follow-up (Option B); this PR unblocks all Linux usage today.
Verification
uv run ruff check .clean; 570 tests pass (6 new intests/test_linux_support.py, platform forced viasys.platformso they're host-independent).config set automation.sync.enabled true) exits 0,automation statusworks, no launchd artifacts created.install().installedisTrue, so every existing render branch is taken as before.A design writeup is in
docs/plans/2026-07-24-linux-support-option-a.md.