Skip to content

perf: size hosted menu charts without a throwaway hosting controller - #1352

Merged
steipete merged 1 commit into
steipete:mainfrom
Yuxin-Qiao:perf/hosted-chart-measure
Jun 8, 2026
Merged

perf: size hosted menu charts without a throwaway hosting controller#1352
steipete merged 1 commit into
steipete:mainfrom
Yuxin-Qiao:perf/hosted-chart-measure

Conversation

@Yuxin-Qiao

@Yuxin-Qiao Yuxin-Qiao commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to the popup-menu lag reported in #1321, focused on the hosted chart submenus (cost/usage/credits history, storage breakdown, z.ai hourly, usage breakdown).

Every hosted chart item built two SwiftUI hierarchies on each (re)build:

  1. the MenuHostingView that is actually displayed, and
  2. a separate NSHostingController(rootView:) created solely to measure height via sizeThatFits.

The controller-measured height was always immediately overwritten by the subsequent refreshHostedSubviewHeights() fittingSize pass — which runs from both call paths (menuWillOpen after hydrate, and refreshHostedSubviewMenu). So the second SwiftUI tree was pure overhead on a popup-menu hot path that scales with provider/account count and re-runs whenever provider data changes while a chart submenu is open.

This change measures the live displayed view via fittingSize instead (the exact mechanism refreshHostedSubviewHeights already uses), extracted into a shared hostedSubviewFittingHeight(for:width:) helper. Final heights are identical; only the redundant hosting-controller hierarchy is removed.

Why it's safe:

  • The displayed view is always the source of truth for the final height (refreshHostedSubviewHeights re-measures it on every open/refresh today).
  • Chart content is still rebuilt from current data on every refresh — no caching, no fingerprints, so no risk of stale charts or clipped heights.
  • No change to when or whether menus rebuild, so the existing AppKit menu-tracking / retry behavior is untouched.

Proof

Runtime measurement of the removed work, on the real CostHistoryChartMenuView (30 daily entries, menu-card width 310, 400 builds, warm), comparing the two paths directly:

old (2 hierarchies: throwaway NSHostingController.sizeThatFits + live fittingSize): 2.080s total, 5.201 ms/build
new (1 hierarchy: live fittingSize only):                                           1.866s total, 4.664 ms/build
saved per submenu build: ~0.54 ms (10.3% less)

That ~0.5 ms is per chart-submenu build, on the main thread, and recurs on every open/refresh while a chart submenu is visible — multiplied across enabled providers/accounts, which is exactly the multi-provider setup the #1321/#1325 reporters describe.

Correctness: the new regression test asserts the height the append path assigns equals the authoritative refreshHostedSubviewHeights re-measure (and is non-trivial) across cost-history / usage-history / storage-breakdown — i.e. dropping the throwaway controller did not change sizing behavior.

(What I can't provide locally: an end-to-end popup recording on the reporters' exact multi-account machine. The change is a strict reduction of main-thread SwiftUI work on the documented hot path, with identical output.)

Test plan

  • swift build
  • make check (SwiftFormat + SwiftLint, 0 violations)
  • New test hosted chart items size to the displayed view without a throwaway controller — asserts the append-path height matches the authoritative refreshHostedSubviewHeights re-measure (and is non-trivial) across cost-history / usage-history / storage-breakdown for a seeded provider
  • StatusMenuHostedSubmenuRefreshTests, StatusMenuOpenRefreshTests, StatusMenuHeightCacheTests (41 tests) pass
  • Ad-hoc local bundle build + launch, app healthy
  • Runtime measurement of removed hierarchy (see Proof)

Notes

Complements #1351 (menu readiness signature cost). Touches the hosted-submenu sizing path that overlaps @hhh2210's recent menu-card height-cache work, so review from that area owner is welcome.

Made with Cursor

Each hosted chart/submenu item built TWO SwiftUI hierarchies on every
(re)build: the MenuHostingView that is actually displayed, plus a separate
NSHostingController created solely to measure height via sizeThatFits. That
controller's result was always immediately overwritten by the subsequent
refreshHostedSubviewHeights() fittingSize pass (run from both menuWillOpen
and refreshHostedSubviewMenu), so the second hierarchy was pure overhead on
a popup-menu hot path that scales with provider/account count.

Measure the live displayed view via fittingSize instead (the same mechanism
refreshHostedSubviewHeights already uses), via a shared
hostedSubviewFittingHeight helper. Final heights are unchanged; only the
redundant SwiftUI tree is removed. Adds a test asserting the append-path
height matches the authoritative re-measure across chart types/providers.

refs steipete#1321

Co-authored-by: Cursor <cursoragent@cursor.com>
@clawsweeper

clawsweeper Bot commented Jun 7, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed June 7, 2026, 12:25 PM ET / 16:25 UTC.

Summary
The PR replaces throwaway NSHostingController sizing for hosted chart submenus with live-view fittingSize measurement and adds a focused height-parity test.

Reproducibility: Source-reproducible: current main clearly builds a second hosting controller for hosted chart measurement, and #1321 has multiple user reports of 0.32.x menu lag. I did not reproduce the full multi-account lag locally.

Review metrics: 3 noteworthy metrics.

  • Hosted sizing paths: 6 append paths changed. Every hosted chart submenu append path now uses the shared live-view measurement helper.
  • Focused test coverage: 1 test added, 3 chart families covered. The new test exercises the stable sizing seam without depending on fragile live AppKit status-menu automation.
  • Runtime proof: 400 builds, 10.3% measured reduction. The PR body now includes copied timing output for the removed work on a real hosted chart view.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Mantis proof suggestion
A short native menu visual smoke could still help maintainers verify hosted chart sizing remains unchanged in the real app menu. A maintainer can ask Mantis to capture proof by posting a new PR comment that starts with the OpenClaw Mantis account mention, followed by:

visual task: verify hosted CodexBar chart submenus open with unchanged sizing after live-view fittingSize measurement replaces throwaway hosting-controller sizing.

Risk before merge

  • [P1] The PR removes one attributable main-thread cost, but it does not prove the broader multi-account menu lag reported in can someone fix the performance? #1321 is fully fixed.
  • [P1] I did not run tests during this read-only review, so validation is limited to source inspection, the PR-stated checks, and the copied runtime timing output.

Maintainer options:

  1. Decide the mitigation before merge
    Merge after normal maintainer review and passing checks as a narrow hot-path reduction that complements, but does not by itself close, the broader menu-lag work.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • No automated repair is needed; the remaining action is normal maintainer review, CI/check gating, and optional native-menu smoke proof.

Security
Cleared: The diff only changes local AppKit/SwiftUI menu sizing code and focused tests; no security or supply-chain concern was found.

Review details

Best possible solution:

Merge after normal maintainer review and passing checks as a narrow hot-path reduction that complements, but does not by itself close, the broader menu-lag work.

Do we have a high-confidence way to reproduce the issue?

Source-reproducible: current main clearly builds a second hosting controller for hosted chart measurement, and #1321 has multiple user reports of 0.32.x menu lag. I did not reproduce the full multi-account lag locally.

Is this the best way to solve the issue?

Likely yes: reusing the displayed view's existing fittingSize measurement is a narrow maintainable way to remove the redundant SwiftUI hierarchy. The remaining question is only how much this specific reduction helps the broader lag report.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against db184430bc4b.

Label changes

Label changes:

  • add P2: The PR targets a user-facing menu performance regression with limited UI blast radius and no data-loss, security, or crash-loop signal.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body now includes copied runtime timing output comparing old and new measurement paths on a real CostHistoryChartMenuView; this is sufficient real behavior proof for this narrow performance change.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body now includes copied runtime timing output comparing old and new measurement paths on a real CostHistoryChartMenuView; this is sufficient real behavior proof for this narrow performance change.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: The PR targets a user-facing menu performance regression with limited UI blast radius and no data-loss, security, or crash-loop signal.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body now includes copied runtime timing output comparing old and new measurement paths on a real CostHistoryChartMenuView; this is sufficient real behavior proof for this narrow performance change.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body now includes copied runtime timing output comparing old and new measurement paths on a real CostHistoryChartMenuView; this is sufficient real behavior proof for this narrow performance change.
Evidence reviewed

What I checked:

Likely related people:

  • Peter Steinberger: Blame attributes the current hosted chart throwaway sizing and live refresh implementation to the v0.32.4 base commit. (role: introduced behavior; confidence: high; commits: 723734ef3422; files: Sources/CodexBar/StatusItemController+HostedSubmenus.swift, Sources/CodexBar/StatusItemController+Menu.swift, Sources/CodexBar/StatusItemController+UsageHistoryMenu.swift)
  • hhh2210: Recent commits hardened menu height fingerprints and cached menu card heights, and the PR body explicitly calls out overlap with that area. (role: recent area contributor; confidence: high; commits: 10239cc617cf, 7c083fab0c08; files: Sources/CodexBar/MenuCardHeightFingerprint.swift, Sources/CodexBar/StatusItemController+MenuCardHeightCache.swift, Sources/CodexBar/StatusItemController+Menu.swift)
  • Larry Hao(郝卓远): Recent merged-menu latency work touched menu behavior adjacent to hosted submenu sizing and refresh scheduling. (role: recent adjacent contributor; confidence: medium; commits: 65e39f4dcb3a; files: Sources/CodexBar/StatusItemController+Menu.swift, Sources/CodexBar/StatusItemController+UsageHistoryMenu.swift)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 7, 2026
@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Added runtime proof to the PR body: a direct measurement on the real CostHistoryChartMenuView shows the removed throwaway NSHostingController cost ~0.54 ms/submenu build (10.3% less), which recurs per open/refresh and scales with enabled providers/accounts. Correctness is covered by the new height-parity regression test. @clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 7, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal priority bug or improvement with limited blast radius. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 7, 2026
@steipete
steipete merged commit c81a9f2 into steipete:main Jun 8, 2026
4 checks passed
@Yuxin-Qiao
Yuxin-Qiao deleted the perf/hosted-chart-measure branch June 25, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants