diff --git a/11-Features.md b/11-Features.md index 800ae53..47acf9c 100644 --- a/11-Features.md +++ b/11-Features.md @@ -4669,3 +4669,46 @@ three files exist, and that none is trivially small. Measured on merge: 1420 gre `ProtectHome=read-only` to `herdr.service` fails 1 test; removing the login shell from `herdr-inner.sh` fails 1; removing its `stty` fails 1; deleting that file errors 3 and fails the build rather than passing vacuously. + +## The host no longer idle-sleeps while a member is working + +`fleetd` now keeps the machine awake for as long as at least one member is live, and lets it sleep +again once the last one goes. On macOS it does this by holding a `caffeinate -i` child process. + +**The knob.** A new top-level block, on by default: + +```yaml +idleSleepGuard: + enabled: true # set false to turn the guard off +``` + +It is a **deferred** key: the daemon reads it once at startup, so a change needs a restart. A +reload reports it as such rather than pretending it applied. + +**Why it exists.** The Mac that runs this fleet was set to idle-sleep after one minute on battery +(`pmset -g custom` reported `sleep 1`). Over one night the daemon's AMQP link dropped 13 times, and +every drop had a sleep or wake event in `pmset -g log` in the same minute or the minute before. The +broken AMQP link is only the visible symptom. The real cost is a member that freezes mid-turn with +the host — and a long turn with nobody typing is exactly the case that goes idle. Before this, the +fleet needed a human sitting at the keyboard to keep running, which defeats the point of delegating +long work. + +**How it knows.** The guard hangs off `SessionManager`'s existing `onAcquire`/`onRelease` hooks and +its `size()`. It does not count members a second way, so it always agrees with the numbers +`fleet_list` reports. Only a real 0→1 or 1→0 crossing touches the OS. + +**Gotchas — three, and all of them are by design.** + +- **It is macOS-only.** `caffeinate` ships on no other platform, so on Linux — fleet01, for example + — the guard is a clean no-op. It says so once at INFO and never again, so a daemon running for + weeks does not fill its log. fleet01 has the same exposure and does **not** get the fix from this + change; a Linux mechanism (`systemd-inhibit`) is a separate job. +- **`-i` is idle sleep only.** Closing the lid still sleeps the host, and so does an operator asking + for sleep. That is deliberate: the guard stops an unattended host sleeping under a member's turn, + it never overrides the operator. `caffeinate -s`/`-d` would do that and are not used. +- **It fails safe, and silently.** If the mechanism cannot start — binary missing, process table + full — `acquire()` returns null and nothing is ever held. The guard never throws, and never blocks + a spawn, a release or shutdown. So "the guard is enabled" is not proof the host is awake. The + proof is a live `caffeinate` process, or a member that survives an idle night. + +fleetd #355 / #374.