Repository navigation
fix: avoid macOS daemon startup crash after fork - #4422
Conversation
Preserve the interpreter invocation, startup status pipe and run identity across a macOS-only exec before building modules. Keep the Linux fork path unchanged and mark the LFS-backed replay regression self_hosted.
There was a problem hiding this comment.
go2 replay realtime (arm64)
Details
| Benchmark suite | Current: 4bd9278 | Previous: fbe126e | Ratio |
|---|---|---|---|
peak memory |
1591.758 MB |
1564.824 MB |
1.02 |
peak threads |
352 threads |
357 threads |
0.99 |
network (transport) |
2722.305 MB |
2727.328 MB |
1.00 |
disk write |
1.664 MB |
1.488 MB |
1.12 |
instructions |
114.921 G |
114.929 G |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #4422 +/- ##
==========================================
+ Coverage 81.84% 81.96% +0.12%
==========================================
Files 1656 1657 +1
Lines 154293 154388 +95
Branches 12988 13461 +473
==========================================
+ Hits 126275 126547 +272
+ Misses 24435 24243 -192
- Partials 3583 3598 +15
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 18 files with indirect coverage changes 🚀 New features to boost your workflow:
|
|
@aclauer pls review |
|
Review follow-up: dotenv-only
|
| Version / configuration | Startup | Status / stop |
|---|---|---|
Exact base 9197ea5b1dca07c6c10f4b1141fd627754c146c7; .env only |
Succeeds | Both work |
PR head; .env only |
Succeeds | Status cannot find it; stop exits 1 |
| PR head; same setting exported in the shell | Succeeds | Both work |
The affected test daemon was cleaned up by sending SIGTERM to its verified PID; no test processes or registry entries remained. No hardware was used.
Cause: the CLI imports the shared directory constants before loading dotenv. The new exec inherits the dotenv-modified environment and resolves a different registry root. Later control commands still resolve the original root. Late dotenv loading is pre-existing; the daemon/control-command split is introduced by this change.
One possible fix is loading dotenv before imports freeze those paths, preserving shell-environment precedence and test isolation. That has not been implemented or validated and affects shared CLI startup beyond macOS. Would you prefer addressing this here, or in a separate linked CLI-initialization fix? Flagging it for scope guidance rather than silently broadening this PR.
|
Removed Verification: 76 targeted daemon/CLI tests passed, including the real asset-free subprocess lifecycle; pinned lint/format hooks passed. No production-code changes in this commit. |
What these tests protectShort version: These tests check that a background run starts with the right settings, can be reached, and can be found and stopped. They also check that the Mac fix leaves Linux startup alone. These are the tests added or changed by this PR:
The smaller checks catch specific startup mistakes; the last test checks the real workflow. Its Go2 case replays recorded data, not a live robot, and runs on the self-hosted test runner because it needs the larger recording. |
Problem
On macOS, the daemon can crash while initializing native libraries after the double fork, even though coordinator construction happens after forking.
Reproduced on unchanged
mainat60aee3c0(macOS 27 arm64, Python 3.12.13):The launcher exits 1 with
daemon exited during startup. The crash report shows SIGSEGV in CoreFoundation preferences /Curl_macos_init, withcrashed on child side of fork pre-exec. The replay regression passes with this patch.Changes
-mentry points.self_hosted, keeping the existing asset-free case in regular CI.No navigation/controller changes or process-lifecycle redesign.
Validation
CI verification
Verified on October 3, 2026 at head
b96d3dac: 31 successful checks, 6 skipped, none failed or pending. The standard Python/macOS/Linux/ARM matrix, self-hosted Linux/macOS tests, large tests, lint/type checks, native builds, CodeQL and coverage checks passed.CI run 37153553534, attempt 2 passed on the unchanged commit. The first attempt had a macOS Codecov upload
ExpiredTokenafter all tests passed, plus a Linux foreground simulation timeout that passed its diagnostic rerun. One failed-job rerun passed both jobs; no code, workflow, test skips or timeouts were changed. The macOS rerun passed 280 tests including the added Go2 daemon replay case; the large-test rerun passed all 7 selected tests. Existing skips remain skips, not validation claims.Architecture / related work
A separate read-only architecture review found no introduced blockers:
Re-exec resets the daemon coordinator's interpreter; forkserver independently isolates module workers. These are complementary boundaries, not competing process models.
Review scope / limits
Draft for maintainer review of the exec boundary, status-FD lifetime, run identity and failure handling. Local validation used macOS replay data; remote CI exercised the repository's selected Linux/macOS and simulation suites. This is not hardware validation or an assertion that every optional/skipped test was executed. Independent agent review does not replace maintainer approval.