Skip to main content

We merged macOS support. It failed on every real Mac in six places.

Orbi runs itself: every change to the runner is delivered by the runner, reviewed by a second session, merged by a gate, and shipped as a tagged release. On 2026-09-14 that loop delivered macOS support (Issue #849, PR #851): a scheduler layer with a launchd implementation next to the systemd one, a platform-gated install.sh, 2,510 lines added, 3,083 tests green. Nobody had run it on a Mac. The review session had verified the launchd branch through an injected fake, which is what the ticket asked for. The next morning a line-by-line read against launchctl(1) and a real /bin/sh found six defects that would have failed on any real Mac.

1. The secrets never reached the runner

The launchd wrapper sourced the env file and exec’d the CLI:
The env file is plain KEY=value lines, the same file systemd reads through EnvironmentFile=. Under POSIX sh, assignments in a sourced file stay shell-local unless set -a is on, so the exec’d process saw no provider key. Every tick would have failed authentication. A test asserted the broken wrapper string. Fixed in #867: [ -r env ] && { set -a; . env; set +a; }.

2. install.sh died on every stock Mac anyway

The new platform gate correctly required launchctl on Darwin. Four lines later the script called timeout, which macOS does not ship, and one sed -i without a suffix, which BSD sed rejects. The “must fail” the ticket set out to remove had moved from the prerequisite check to the middle of the script, with a worse message. CI could not see it because the test harness symlinked /usr/bin/timeout into the stub PATH. Fixed in #868.

3. PATH without /opt/homebrew/bin

The plist copied the Linux PATH. On Apple Silicon, gh, Homebrew’s uv and pi live under /opt/homebrew/bin; the first gh subprocess would have raised FileNotFoundError. Fixed in #869.

4. Idle recovery read /proc/stat

The idle-session recovery computed boot time from /proc/stat with no guard. On macOS the poll loop would have died with an exception instead of escalating a stuck session. Fixed in #870.

5. No sync-engine-source before the tick

The systemd unit runs orbi sync-engine-source as an ExecStartPre step so a deployment tracks its configured engine source. The plist only re-installed the CLI; engine_source_track was dead configuration on macOS, silently. Fixed in #871.

6. The docs still said systemd only

grep -rin launchd docs README.md returned nothing. Fixed in #872.

What this says about the loop

All six are the same failure shape: a contract verified against a fake, never against the platform. The review session did exactly what the acceptance criteria asked and the criteria were wrong. The fix was not a better model. It was six one-outcome tickets, each with an acceptance step a sandbox can actually execute (a real /bin/sh sourcing a real file; a PATH without timeout), delivered by the same loop the same day, and then a macOS CI job (#896) so the next port cannot pass on a fake alone. The loop did not catch its own blind spot. A human reading the diff did, in about an hour, and the loop closed all six before the next release.

Sources