Skip to content

perf(next): avoid idle observation recomputation - #30565

Merged
teamleaderleo merged 2 commits into
feat-cmux-nextfrom
lane/cpu-audit-observation
Nov 1, 2026
Merged

teamleaderleo merged 2 commits into
feat-cmux-nextfrom
lane/cpu-audit-observation

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

The NEXT main thread repeatedly rebuilt observation-derived structures while idle. Settings host-list reads rebuilt wallpaper page rows, and the restored New Tab computation walked the workspace graph even when only row status changed. This caches wallpaper page rows until the catalog changes and exposes an indexed tab snapshot whose observation dependency is only the tab-list revision.

Performance:

metric before after delta harness
wallpaper page-row projections across 100 repeated reads 100 1 -99% wallpaperPageRowsAreMemoizedUntilTheCatalogChanges
workspace/screen/pane/tab collection layers observed by 100 repeated row updates 100 full graph walks 0 full graph walks (100 indexed tab snapshots) removes row-status invalidation path RestoredNewTabPages.ids with DaemonStore.liveTabs

The wallpaper cache stops when the catalog value changes. liveTabs observes tabListRevision, which changes on tab/pane topology updates, so terminal status changes do not re-arm the structural observation. No timer or observer is introduced.

The other newTabPages callers remain intentionally uncached because they build one-off intent and layout payloads: SidebarBridge+Intents.swift:203, SidebarBridge.swift:191, and SidebarBridge.swift:237. The structural sidebar pass at SidebarBridge.swift:148 uses the same safe liveTabs path.

Cause B from the cpu-audit idle main-thread sample. Compile with cmux-ci compile-check --ref 6877edf1111c3b13d430a940a1d745f9681fa936 --app.

Agent session: agent session

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Nov 1, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Packages/macOS/CmuxNext/Tests/CmuxNextSettingsTests/AppleAerialsCatalogTests.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Tests/CmuxNextSettingsTests/AppleAerialsCatalogTests.swift:83">
P2: This test never checks invalidation. `BackdropCatalog(bundledOnly:)` always yields the same catalog, so a stale cache would still pass. Add a case where the catalog changes, such as a catalog built with a different `aerialNames` or `pendingVideoPaths` via `BackdropCatalog(systemDirectory:fileManager:aerials:)` on a temp folder, and assert `builds == 2` with the new rows.</violation>
</file>

<file name="Packages/macOS/CmuxNext/Sources/CmuxNextSettings/BackdropCatalog+Page.swift">

<violation number="1" location="Packages/macOS/CmuxNext/Sources/CmuxNextSettings/BackdropCatalog+Page.swift:40">
P3: The cache can serve stale localized wallpaper labels because its key contains no locale. Include the localization context in the cache key or invalidate the rows cache when the app locale changes.</violation>

<violation number="2" location="Packages/macOS/CmuxNext/Sources/CmuxNextSettings/BackdropCatalog+Page.swift:40">
P2: Cache hits still linearly compare the entire wallpaper catalog, so every host-list read retains catalog-sized idle work. Compare a monotonic catalog revision/generation instead of using full value equality.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

for _ in 0..<100 { _ = cache.rows(for: catalog) }

#expect(cache.rows(for: catalog) == catalog.pageRows)
#expect(cache.builds == 1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This test never checks invalidation. BackdropCatalog(bundledOnly:) always yields the same catalog, so a stale cache would still pass. Add a case where the catalog changes, such as a catalog built with a different aerialNames or pendingVideoPaths via BackdropCatalog(systemDirectory:fileManager:aerials:) on a temp folder, and assert builds == 2 with the new rows.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Tests/CmuxNextSettingsTests/AppleAerialsCatalogTests.swift, line 83:

<comment>This test never checks invalidation. `BackdropCatalog(bundledOnly:)` always yields the same catalog, so a stale cache would still pass. Add a case where the catalog changes, such as a catalog built with a different `aerialNames` or `pendingVideoPaths` via `BackdropCatalog(systemDirectory:fileManager:aerials:)` on a temp folder, and assert `builds == 2` with the new rows.</comment>

<file context>
@@ -72,3 +72,13 @@ struct AppleAerialsCatalogTests {
+    for _ in 0..<100 { _ = cache.rows(for: catalog) }
+
+    #expect(cache.rows(for: catalog) == catalog.pageRows)
+    #expect(cache.builds == 1)
+}
</file context>

public init() {}

public mutating func rows(for catalog: BackdropCatalog) -> JSONValue {
if self.catalog != catalog {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Cache hits still linearly compare the entire wallpaper catalog, so every host-list read retains catalog-sized idle work. Compare a monotonic catalog revision/generation instead of using full value equality.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextSettings/BackdropCatalog+Page.swift, line 40:

<comment>Cache hits still linearly compare the entire wallpaper catalog, so every host-list read retains catalog-sized idle work. Compare a monotonic catalog revision/generation instead of using full value equality.</comment>

<file context>
@@ -24,3 +24,24 @@ public extension BackdropCatalog {
+    public init() {}
+
+    public mutating func rows(for catalog: BackdropCatalog) -> JSONValue {
+        if self.catalog != catalog {
+            self.catalog = catalog
+            cached = catalog.pageRows
</file context>

public init() {}

public mutating func rows(for catalog: BackdropCatalog) -> JSONValue {
if self.catalog != catalog {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The cache can serve stale localized wallpaper labels because its key contains no locale. Include the localization context in the cache key or invalidate the rows cache when the app locale changes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Packages/macOS/CmuxNext/Sources/CmuxNextSettings/BackdropCatalog+Page.swift, line 40:

<comment>The cache can serve stale localized wallpaper labels because its key contains no locale. Include the localization context in the cache key or invalidate the rows cache when the app locale changes.</comment>

<file context>
@@ -24,3 +24,24 @@ public extension BackdropCatalog {
+    public init() {}
+
+    public mutating func rows(for catalog: BackdropCatalog) -> JSONValue {
+        if self.catalog != catalog {
+            self.catalog = catalog
+            cached = catalog.pageRows
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 existing issues remain and no new issues found across 6 files

Requires human review: Auto-approval blocked because this review re-detected 2 unresolved issues already reported by Cubic.

View guided diff | Turn on auto-fix | Re-trigger cubic

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent claude-opus-5-5 medium): SHIP at 6877edf. The stale SidebarBridge cache was removed; the remaining liveTabs and wallpaper projection caches are safe. Follow-ups are recorded in the PR body.

@teamleaderleo
teamleaderleo merged commit d39b5c2 into feat-cmux-next Nov 1, 2026
98 of 143 checks passed
@teamleaderleo
teamleaderleo deleted the lane/cpu-audit-observation branch November 1, 2026 09:14
@lawrencecchen

Copy link
Copy Markdown
Contributor

Tip note: this PR and #30565 together added +15 CmuxNextApp Swift lines, so the arch ratchet's app_lines row went red at feat tip 61ad0ed. It went green again at 6bd0a43 with no action, because #30610 moved 16 lines out of CmuxNextApp. Nothing to do here. Next time, put new code in its owning package (plans/cmux-next/code-quality.md SW14), not in CmuxNextApp.

@cursor

cursor Bot commented Nov 1, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 234b294: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr30565 -f ref=lane/cpu-audit-observation -f platform=macos -f backend_mode=local

Not verified: macOS jobs did not run: compile and app tests were skipped on 6877edf111; ci-status is green without them.

CI passes on 6877edf111 (run 41291362202 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

Shipped in:

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 6877edf1 Deployed Nov 1, 2026 by teamleaderleo via namespace autofix #11061
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants