Skip to content

Stop unrelated defaults writes and pane geometry changes from re-evaluating chrome-heavy views - #14058

Merged
teamleaderleo merged 4 commits into
mainfrom
fix/dotted-appstorage-invalidation
Sep 24, 2026
Merged

teamleaderleo merged 4 commits into
mainfrom
fix/dotted-appstorage-invalidation

Conversation

@austinywang

@austinywang austinywang commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Every UserDefaults write re-evaluated the window root (ContentView) and the vertical tabs sidebar. Every pane geometry change re-evaluated every view observing the workspace. testMinimalModeToggleDoesNotReevaluateChromeHeavyBodies catches both. Toggling minimal mode writes one defaults key, and on main that re-evaluates ContentView / WorkspaceContentView / VerticalTabsSidebar 1 / 2 / 1 times; the test expects 0 / 0 / 0. With this change the test passes. Its _printChanges output on main (run 35903570794, ccdbf30):

ContentView: _titlebarLeftControlsLeadingInset, _titlebarLeftControlsTopInset, _titlebarTrafficLightTabBarInset, _titlebarTrafficLightTitlebarLeadingInset, _selectedLeftSidebarProviderId changed.
WorkspaceContentView: @self changed.
VerticalTabsSidebar: _selection, _selectedExtensionSidebarProviderId, _titlebarLeftControlsLeadingInset, _titlebarLeftControlsTopInset changed.
WorkspaceContentView: _workspace changed.

Dotted @AppStorage keys. A view that reads an @AppStorage property whose key contains a . re-evaluates its body on every write to the defaults suite. A view with a flat key re-evaluates only when its own key changes. The standalone experiment in #13930 shows this. Apart from _selection, a binding that ContentView passes down, every property in the ContentView and VerticalTabsSidebar lines above reads one of five dotted keys: four titlebarDebug.* insets and cmuxExtensionSidebar.providerId.

This PR renames them to flat keys (titlebarDebugLeftControlsLeadingInset, …, cmuxExtensionSidebarProviderId). At launch, values saved under the old keys move to the new ones. A value already stored under a new key wins, and the old keys are removed. Nothing else in the repository reads the old keys.

Workspace.tmuxLayoutSnapshot was @Published (since 656786f). No view body reads it. The window pane overlay reads it from event handlers, including the .workspacePaneGeometryDidChange notification that the geometry callback posts right after assigning it. Publishing it fired objectWillChange on every geometry change, including divider drags, so every view observing the workspace re-evaluated. paneLayoutVersion is gated so that divider drags do not do this. The snapshot is now a plain stored property.

The two causes are independent. Each change removes only its own _printChanges lines, and the test passes only with both. These runs used macOS 26; counts are ContentView / WorkspaceContentView / VerticalTabsSidebar.

Run Code Counts _printChanges lines in the test
35837956172 base 41f6862 1 / 2 / 1 all four
35837959917 flat keys only 0 / 1 / 0 WorkspaceContentView: _workspace changed.
35837963691 unpublished snapshot only 1 / 1 / 1 the first three
35834741595 both (1445961) 0 / 0 / 0, passed none

The test already failed in the PR that added it: run 28413864324 of #7076 logged 6 / 8 / 10. Until #12053, the app-host shard classifier checked only XCTest summaries, so Swift Testing failures did not fail the shard.

Downgrade: the migration removes the old keys. An older build run afterwards shows the default sidebar view and the default titlebar insets.

Not changed here: more than 30 other @AppStorage keys still contain dots, including user settings such as terminal.textBoxMaxLines, fileExplorer.style and sessionIndex.compactView. Views that read them still re-evaluate on every defaults write. This PR does not include the repo-wide audit that #13930 suggests.

Fixes #13930. Refs #13879. This covers one of the five tests that #14006 lists as not covered.

Testing

Nothing was built or run locally. Hosted runs:

Run OS Commit Result
test-depot 35834662983 macOS 15 1445961 The minimal-mode test and both new tests passed. The other six failures fail at the same lines on main in run 35828352181: the sidebar visibility toggle, equalize, three config reloads and the font barrier.
E2E 35834741595 macOS 26 1445961 The minimal-mode test and legacyDottedSelectionKeyMigratesToFlatKey passed. visibilityToggleKeepsAppKitTableContainerMounted failed, as it does on main (#13931 is the open fix).
E2E 35834746026 macOS 26 1445961 WindowDragHandleHitTests passed, including the titlebar migration test.
E2E 35936168156 macOS 26 8686898 (this PR merged with main 1ba90a1) queued

The E2E workflow ends red on every run. Its cleanup step exits 1 after the tests with "refusing to inspect app hosts outside runner temp". The results above come from the test log.

New tests: WindowDragHandleHitTests.testTitlebarChromeSettingsMigrateDottedKeysFromBeforeIssue13930 and SidebarProviderMenuRegressionTests.legacyDottedSelectionKeyMigratesToFlatKey() cover the migration. They include the case where a newer value wins over a leftover legacy value.

Localization audit: no user-facing strings changed. The renamed keys are UserDefaults storage keys.

Checklist

  • I tested the change locally (hosted runs only, above)
  • I added or updated tests for behavior changes
  • For iOS connectivity, auth, lifecycle, workspace or terminal changes, I updated the deterministic soak coverage or explained why existing coverage still applies, and recorded the affected workload result (n/a: macOS app only)
  • I updated docs/changelog if needed (none needed)
  • I requested bot reviews after my latest commit (copy/paste block above or equivalent)
  • All code review bot comments are resolved
  • All human review comments are resolved

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Preserved existing sidebar selections and titlebar settings when updating the app, including cases where newer settings were already saved.
    • Improved responsiveness while dragging workspace dividers by reducing unnecessary screen updates.
    • Resolved a tracked failure affecting minimal-mode titlebar updates.

testMinimalModeToggleDoesNotReevaluateChromeHeavyBodies (#13930) fails
on main: switching the presentation mode re-evaluates ContentView,
WorkspaceContentView, and VerticalTabsSidebar. The test's
_printChanges output in main CI names two causes.

Every @AppStorage property whose key contains "." reported a change
after a write to a different key, while the flat-key properties in the
same views did not. ContentView and VerticalTabsSidebar held five such
keys (four titlebar insets and the sidebar view selection), so any
UserDefaults write re-evaluated the window root and the sidebar.
Rename them to flat keys and move values saved under the old keys at
launch. A value already stored under the new key wins.

Workspace.tmuxLayoutSnapshot was @published, but its only reader is
the window pane overlay, which reads it from the
.workspacePaneGeometryDidChange handler posted right after the
assignment. Publishing it re-evaluated every view observing the
workspace on each geometry change, which paneLayoutVersion is gated to
avoid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: ff99a019-cb08-4faf-89fc-8551a63ed27a

📥 Commits

Reviewing files that changed from the base of the PR and between 1f90e91 and 720f321.

📒 Files selected for processing (2)
  • cmuxTests/AppDelegateEqualizeSplitsShortcutTests.swift
  • scripts/ci/app-host-known-failures.json
💤 Files with no reviewable changes (1)
  • scripts/ci/app-host-known-failures.json

Included review availability: Your plan provides up to 10 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Sidebar and titlebar settings now use flat UserDefaults keys, with startup migrations from legacy dotted keys. Workspace layout snapshots no longer use @Published; geometry updates use a notification.

Changes

View invalidation changes

Layer / File(s) Summary
Flat defaults keys and migration
Sources/ContentView.swift, Sources/WindowDragHandleView.swift, Sources/cmuxApp.swift, cmuxTests/SidebarProviderMenuRegressionTests.swift, cmuxTests/WindowAndDragTests.swift
Sidebar and titlebar settings use flat keys. Startup migration preserves existing flat-key values, removes legacy keys, and copies legacy values only when the corresponding flat key is unset. Regression tests cover migration and key precedence.
Workspace geometry updates
Sources/Workspace.swift, cmuxTests/AppDelegateEqualizeSplitsShortcutTests.swift, scripts/ci/app-host-known-failures.json
tmuxLayoutSnapshot is no longer published. The geometry-change handler communicates updates through the workspace geometry notification. A test comment changes “published” to “recorded,” and a test is removed from the known-failures list.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 720f3

Saved sidebar and titlebar settings are migrated, geometry updates still refresh their consumers, and the regression test now gates app-host CI. No concrete merge-blocking risk remains beyond normal checks.

🚥 Pre-merge checks | ✅ 24 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (24 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary changes: preventing unrelated UserDefaults writes and pane geometry changes from re-evaluating chrome-heavy views.
Description check ✅ Passed The description is mostly complete. It includes a detailed summary, testing results, known failures, migration behavior, added tests, and checklist status. It does not include the requested Demo Video…
Linked Issues check ✅ Passed The PR meets the coding requirements in issue #13930. It changes the five affected dotted storage keys to flat keys and adds launch-time migrations. The migration preserves an existing flat-key value …
Out of Scope Changes check ✅ Passed The changed production code, launch migration calls, regression tests, and CI registry update support issue #13930. The AppDelegateEqualizeSplitsShortcutTests comment change documents the `tmuxLayou…
Cmux Cloud Persistent Session And Early Input ✅ Passed PASS: The pull request does not change Cloud terminal creation, persistent cmux-tui transport, manual renderer admission, attachment input routing, or Cloud auth/revision/lease handling. The authorita…
Cmux Swift Actor Isolation ✅ Passed The production diff adds only flat UserDefaults keys, synchronous migration helpers, and removes @Published from the existing Workspace.tmuxLayoutSnapshot property. It adds no implicit MainActor model…
Cmux Swift Blocking Runtime ✅ Passed The production diff adds only synchronous UserDefaults migration logic and removes @Published from Workspace.tmuxLayoutSnapshot. It does not add semaphores, blocking waits, sleeps, delayed dispatch,…
Cmux Browser Automation Off-Main ✅ Passed PASS. The authoritative diff changes only defaults migration, workspace geometry observation, related tests, and the known-failures registry. It does not modify Sources/TerminalController.swift, `Co…
Cmux Expensive Synchronous Load ✅ Passed The production diff adds only finite UserDefaults key migrations and removes @Published from Workspace.tmuxLayoutSnapshot. It adds no RestorableAgentSessionIndex.load(), agent-store/transcript/t…
Cmux Cache Substitution Correctness ✅ Passed PASS. The diff does not replace a fresh authoritative read with a cached value in a persistence, history, undo, or snapshot path. It only removes @Published from Workspace.tmuxLayoutSnapshot; the …
Cmux No Hacky Sleeps ✅ Passed PASS: The pull request changes only Swift source/tests and a JSON known-failures registry. It introduces no TypeScript, JavaScript, shell, or non-Swift build/runtime changes, and the diff adds no cove…
Cmux Algorithmic Complexity ✅ Passed PASS — The production diff adds only two UserDefaults migrations and removes one publication. MinimalModeTitlebarDebugSettings.migrateLegacyKeysIfNeeded iterates a fixed four-key list, which is a ti…
Cmux Swift Concurrency ✅ Passed The pull request does not introduce or expand a flagged legacy async pattern. The new migration methods are synchronous UserDefaults operations. The diff adds no DispatchQueue, DispatchGroup, custom q…
Cmux Swift @Concurrent ✅ Passed The PR does not introduce or materially change Swift concurrency behavior. The changed Swift lines add only synchronous UserDefaults migration methods, flat key constants, synchronous launch calls, an…
Cmux Swift Package Boundaries ✅ Passed The PR does not introduce a package-boundary violation. The changed code is confined to app-target types for titlebar/sidebar UI settings and workspace geometry observation: it renames @AppStorage k…
Cmux Swiftpm Lockfiles ✅ Passed The pull request changes only Swift source, tests, and a CI known-failures JSON file. It changes no Package.swift, Package.resolved, Xcode project package references, .gitignore, workflow, or dependen…
Cmux Swift Logging ✅ Passed PASS: The pull request adds no production logging. The changed runtime lines only rename defaults keys, migrate values, add migration calls, and remove @Published; no print, debugPrint, dump, …
Cmux User-Facing Error Privacy ✅ Passed The pull request adds no user-facing errors, alerts, command output, API error bodies, or recovery copy. Production changes only rename UserDefaults keys, migrate legacy values, and stop publishing pa…
Cmux Full Internationalization ✅ Passed PASS. The production diff changes UserDefaults keys, migration logic, and workspace observation behavior. Added string literals are configuration keys or legacy keys, not user-facing text. Added comme…
Cmux Swiftui State Layout ✅ Passed The diff does not introduce any prohibited SwiftUI state or layout pattern. It removes @Published from the existing Workspace.tmuxLayoutSnapshot; it does not add a new ObservableObject or `@Publ…
Cmux Architecture Rethink ✅ Passed The diff does not introduce a prohibited architecture pattern. It removes @Published from the workspace-owned geometry snapshot and keeps the existing geometry notification path, whose producer and …
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR does not add or materially change a standalone cmux-owned window. The production diff changes UserDefaults key migration and removes @Published from Workspace.tmuxLayoutSnapshot. Existing w…
Cmux Source Artifacts ✅ Passed All eight changed paths are intentional source, test, or CI configuration files. The diff adds migration logic, workspace behavior, regression tests, and removes one obsolete known-failure entry. No l…
Cmux No Test Or Debug Seam In Production Source ✅ Passed The production diff adds only defaults-key migration methods, renamed key constants, and removes @Published from Workspace.tmuxLayoutSnapshot. The new migration methods are called by cmuxApp at …
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@teamleaderleo

Copy link
Copy Markdown
Collaborator

An independent rebuild of this fix reached the same two causes: the dotted @AppStorage keys (#13930), and tmuxLayoutSnapshot being @Published. It fails alone at main (run 35933844935). The key rename alone gave 0/1/0 (run 35934148962), and a stack-trace run found splitTabBar(_:didChangeGeometry:) as the remaining trigger (run 35942433848). The duplicate #14096 was closed in favour of this PR.

This PR's red shards contain no failure of its own: the icon test (#14062), sendPrintsQueuedMarkerWhenDeliveryIsQueued / generatedBashBootstrap… subprocess timeouts (#14056), and the uncatalogued AgentHookDeliveryQueueTests best-effort capacity failure, which I am looking at now. When you next push, please also remove WorkspaceContentViewVisibilityTests/testMinimalModeToggleDoesNotReevaluateChromeHeavyBodies() from scripts/ci/app-host-known-failures.json (from #14074), so the fix is enforced.

— Ibex g1 🌿

@cursor

cursor Bot commented Sep 24, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

…asses

The known-failures catalog listed it under #13930, which this PR fixes, so a
regression would fail the test without failing CI. Also drop "published"
from a helper comment now that tmuxLayoutSnapshot is a plain property.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo enabled auto-merge (squash) September 24, 2026 14:10
@teamleaderleo
teamleaderleo merged commit 06ec6cb into main Sep 24, 2026
54 checks passed
rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 24, 2026
cd7a4cf Prepare iOS 1.0.6 beta compatibility release (manaflow-ai#14112)
2d9b4e8 test: skip dead persistent-SSH restore tests and fix relay-less legacy fixtures after manaflow-ai#14216 (manaflow-ai#14222)
df44058 ci: run focused cmuxTests against products CI already compiled (manaflow-ai#14229)
06ec6cb Stop unrelated defaults writes and pane geometry changes from re-evaluating chrome-heavy views (manaflow-ai#14058)
185d99e chore(cli): remove dead persistent SSH PTY startup path (manaflow-ai#14231)
dddffea ci: take the build-fleet host lock for nightly mini builds (manaflow-ai#14233)
f2106e5 test(cli): expect the client-side workspace ref resolution manaflow-ai#13964 added (manaflow-ai#14230)
59fa5b9 docs(ci): fix persistent-compile pilot runbook drift (manaflow-ai#14206)

# Conflicts:
#	.github/workflows/app-host-test-rerun.yml
#	.github/workflows/nightly-mini-build.yml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ContentView and the sidebar re-render on unrelated UserDefaults writes (dotted @AppStorage keys)

2 participants