fix(next): retire browser pages before chrome teardown
Releasing hidden browser chrome could abort in Chromium’s AppKit hierarchy tracker: a descendant registered a weak reference to an ancestor already deallocating. BrowserEntry now closes the page and detaches its content before releasing chrome. Final release off main retains both objects through main-actor cleanup; explicit close uses the same retirement path.
The reported crash was matched by executable UUID to nightly commit 1f0f687b2f32a9f506e19e7f683e8362fd3da6b2. The crash marker’s newer version described the on-disk update. A live isolated baseline reproduced the same SIGABRT stack: weak_register_no_lock → objc_storeWeak → NSPointerArray.addPointer → Chromium hidden-ancestor tracking → NSView finalization.
Verification
Head: 83e5935206c0777a5d5ba2a4e67e7c53ec27ebc5.
Focused native command (run with each source SHA):
cmux-ci run --class light --script scripts/ci/package-test-lane.sh \
--ref SHA --arg=suite --arg=Packages/macOS/CmuxNext \
--arg=BrowserEntryRetirementTests \
--env CMUX_CI_XCODE_APP=/Applications/Xcode_26.6.app
- Before: test-only commit
76e2236c6806d386aff96e2e699f185a6c4c64f2, step348a7122cf4795d1daac2f4e: failed as expected because the released owner left its page open (1 of 2 tests failed). - After: exact-head step
f8ca670571d6b40dd99dca84: 3 tests passed, 0 skipped, exit 0 on Xcode 26.6. Covers hidden-owner release, repeated close, and final release off main with real WebKit/AppKit objects. The test process exited with no live test windows. Intermediate test compilation errors were repaired before this pass. - Exact-head tagged build:
3a1260654af924576823694f, tag27166-browser-view-teardown: passed; downloaded artifact SHA-2560782e3b69176e581324c0b78c349defd645ae1ca78d6a0c6f0e50d97c3e39e63, original bundle signature verified before isolated launch. - Live Chromium before: comparison commit
dcc8aa3a1df9d67342947c9aadc222615f70d89d, build9020003ddbefb27438b98c79, crashed on the first owner-release probe with the matching weak ancestor stack. - Live Chromium after: exact head/build above passed 10/10 owner-release probes. Every result reported
live_before_release,closed,content_detached,chrome_released, andpassedas true. The app stayed inactive, the frontmost application stayed the same, and the isolated app quit gracefully and exited afterward. - Swift syntax, prepush guardrails, and diff whitespace checks passed. The App source growth warning is advisory.
Observed / Not run
Observed: original crash and matching symbols; same-stack live baseline crash; 10 exact-head Chromium runs passing every lifecycle assertion; independent correctness review on this head. The native suite passed all 3 tests with no skips.
Not run: Sentry queries (CLI unauthenticated), a broad package sweep, and Cloud behavior (desktop lifecycle change only). Capture-service attempts had no available GUI runner; local verification uses separate tagged bundle IDs, data/configs, and control sockets with activation disabled.
No capture: no rendered UI change. Native runtime receipts and the baseline crash are retained privately.
Impact and review
BrowserEntry is the ownership boundary for both desktop browser engines. The retirement adapter lives in CmuxNextBrowser. The debug-only control endpoint exercises real CEF ancestor tracking; the helper remains available to the Browser module because the App’s bench flag does not propagate to it.
Review (review_browser_retirement): SHIP at 83e5935206c0777a5d5ba2a4e67e7c53ec27ebc5. Earlier review findings about macOS 14 Mutex support and module imports are resolved. Conflict-only merge gate passed at head 83e5935206c0777a5d5ba2a4e67e7c53ec27ebc5 against base 85b76c63b5c9b95ddd1c6bc5c671d5172c2df911. Local, remote, PR, and reviewed heads match. Incoming base changes do not touch the five changed paths. No open merge freeze or failing current-head GitHub check was found. Pending unrelated checks follow the repository’s normal landing policy.
Changelog
Fixed a crash when releasing browser views.