Skip to content

perf: cut menu readiness signature cost on store changes - #1351

Merged
steipete merged 11 commits into
steipete:mainfrom
Yuxin-Qiao:perf/menu-readiness-signature
Jun 8, 2026
Merged

perf: cut menu readiness signature cost on store changes#1351
steipete merged 11 commits into
steipete:mainfrom
Yuxin-Qiao:perf/menu-readiness-signature

Conversation

@Yuxin-Qiao

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

Copy link
Copy Markdown
Contributor

Summary

Targets the popup-menu lag reported in #1321. Multiple users confirmed the lag appeared in the 0.32 series and that downgrading to 0.31.0 restores responsiveness, so this focuses on a 0.32-era main-thread regression that still exists on main.

The menu readiness signature (menuAdjunctReadinessSignature()) serializes every enabled provider's token snapshot plus its 30-day daily breakdown (and the OpenAI dashboard per-service breakdown) into a string on every store mutation, via observeStoreChanges. This runs on the main actor on each background refresh tick and scales with provider/account count.

Three changes:

  • Skip the signature entirely when no menu is open. observeStoreChanges always computed didMenuAdjunctReadinessChange() to pass as refreshOpenMenus, but invalidateMenus only consults that argument while a menu is open. When the menu is closed (the common case during background refresh) the result was computed and discarded.
  • Re-anchor the baseline when a root menu opens and actually shows current data (correctness for the skip). Because the baseline is no longer recomputed on every closed-menu store change, it can drift from live store data. Without re-anchoring, a menu reopened from new data followed by an open-menu change that reverts to the previous baseline value would compare equal and skip the rebuild, leaving stale content visible. The baseline is now resynced when a root menu opens and !menuNeedsRefresh(menu) after refreshMenuForOpenIfNeeded — i.e. the menu was rebuilt or was already fresh for the current menuContentVersion. If an in-flight provider refresh causes refreshMenuForOpenIfNeeded to preserve stale content, resync is skipped so the refresh-completion update is not masked. Nested submenu opens intentionally do not resync, so a pending parent refresh can't be masked. Thanks to @clawsweeper for catching both the original drift case and the in-flight stale-preservation case.
  • Make the signature cheaper. Replace String(format: "%.8f", …) (a hot per-value cost run for every daily/service value of every provider) with the raw Double bit pattern. The signature is only ever compared for equality against the previous signature, so the bit pattern is both exact (no rounding collisions) and far cheaper. This also helps while a menu is open, where the signature is recomputed on each tick.

Net effect: less main-thread work both at idle (closed) and during interaction (open), which is the responsiveness path that regressed in 0.32.

Proof

Real app — freshly built ad-hoc bundle of this branch, 7 providers enabled (codex, claude, cursor, gemini, antigravity, minimax, deepseek), refreshFrequency = oneMinute, menu closed. A 65 s sample of the running CodexBar process — spanning at least one background refresh tick — shows the menu signature/rebuild path never lands on the main thread:

$ sample CodexBar 65
# grep across ALL threads:
menuAdjunctReadinessSignature | formatDoubleForSignature | populateMenu
  | updateMenuContentPreservingSwitcher | sizeThatFits  -> 0 hits
observeStoreChanges | invalidateMenus                   -> 0 hits

main thread: 54126/54126 samples parked in App.main -> __CFRunLoopRun -> mach_msg
idle CPU: ~0.3%

So with a multi-provider setup and the menu closed, the readiness/rebuild work is no longer a sustained main-thread cost. (Honest caveat: sample is 1 ms-coarse and a single per-tick signature call is ~tens of µs, so this demonstrates the path is not a sustained closed-path cost rather than a precise per-call delta.)

Real app — menu open with 7 providers enabled (same ad-hoc bundle: codex, claude, cursor, gemini, antigravity, minimax, deepseek). Opened via osascript + System Events (click menu bar item "CodexBar"):

click-to-open (11 visible items): ~190 ms real
20 s sample while menu held open:
  menuAdjunctReadinessSignature | populateMenu | observeStoreChanges
    | invalidateMenus | updateMenuContentPreservingSwitcher | sizeThatFits  -> 0 hits
  main thread: 17133/17138 samples parked in mach_msg

So the open-menu interaction path is responsive on first open, and there is no sustained signature/rebuild churn while the menu stays open across background refresh ticks.

Runtime cost of the work skipped when closed, measured on the real menuAdjunctReadinessSignature() with a realistic multi-provider store (4 providers, each with a 30-day token daily breakdown, 5000 calls, warm):

menuAdjunctReadinessSignature(): 0.391s / 5000 = ~78 us/call

That full ~78 µs call ran on the main actor on every store mutation pre-fix, even with all menus closed (i.e. on each background refresh tick). It now runs only when a menu is actually open, and the cost grows with enabled providers/accounts.

Why the per-call cost itself also dropped — signature double-formatting micro-benchmark (swiftc -O, 60 doubles/iter × 200k iters):

old(%.8f):       10.799s
new(bitPattern):  1.166s
speedup: 9.3x

Regression tests are negative-validated.

reopening root menu resyncs readiness baseline so reverted store data still refreshes — fails when resync is disabled, passes with it:

# resync disabled:
✘ Expectation failed: controller.didMenuAdjunctReadinessChange()
# resync enabled:
✔ Test "reopening root menu resyncs readiness baseline …" passed

root open during in flight refresh preserves stale content and does not resync baseline — fails when baseline resync is unconditional on root open, passes when gated on !menuNeedsRefresh(menu):

# unconditional resync on root open:
✘ Expectation failed: controller.didMenuAdjunctReadinessChange()
# gated resync (only when menu is fresh):
✔ Test "root open during in flight refresh …" passed

Test plan

  • swift build
  • make check (SwiftFormat + SwiftLint, 0 violations, 1018 files)
  • swift test --filter StatusMenuTests — includes existing open-menu refresh coverage plus both baseline-resync regression tests (closed reopen revert + in-flight stale preservation)
  • Negative validation of both regression tests (each fails without its respective fix)
  • Runtime measurement of the skipped signature call + the formatting speedup (see Proof)
  • Real-app 65 s sample, 7 providers, menu closed — no main-thread signature/rebuild churn (see Proof)
  • Menu-open responsiveness with 7 enabled providers (real app): ~190 ms click-to-open, 11 items; 20 s open-menu sample shows 0 signature/rebuild hot-path hits, main thread parked (see Proof). Maintainer/reporter multi-account spot-check still welcome on merge but is not author-blocking (clawsweeper: ready for maintainer look).

Notes / follow-ups (not in this PR)

  • Chart submenus still removeAllItems + rebuild their NSHostingView when underlying data changes; gating that on a per-provider content fingerprint is a possible follow-up (overlaps active menu-card-height work).
  • The multi-account "freeze on open" reports appear largely mitigated on current main by the recent Codex token-scan budget commits.

Made with Cursor

The menu readiness signature serializes every enabled provider's token
snapshot and 30-day daily breakdown on each store mutation. Two fixes:

- Skip computing it entirely when no menu is open, since the result
  (refreshOpenMenus) is only consulted while a menu is open. This is the
  common case during background refresh ticks.
- Use the raw Double bit pattern instead of String(format: "%.8f", …),
  which is a hot per-value cost. The signature is only compared for
  equality, so the bit pattern is both exact and far cheaper.

Reduces main-thread work that regressed popup-menu responsiveness in the
0.32 series (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 8, 2026, 6:11 AM ET / 10:11 UTC.

Summary
The branch skips menu-readiness signature computation while no menus are open, switches signature double formatting to raw bit-pattern strings, re-anchors the baseline on fresh root-menu opens, and adds regression tests.

Reproducibility: yes. from source inspection and contributor proof, but I did not rerun it locally. Current main calls the readiness signature on each store observation, and the PR body shows a freshly built multi-provider app sampled with the menu closed and open.

Review metrics: 3 noteworthy metrics.

  • Diff surface: 4 files changed, +204/-2. The implementation is limited to menu refresh scheduling and focused regression coverage.
  • Regression tests: 2 added. The new tests cover the baseline drift and in-flight stale-content cases that would make the optimization unsafe.
  • Runtime proof: 2 real-app samples plus 2 negative validations. The posted proof covers both closed-menu idle behavior and open-menu behavior with multiple providers.

Merge readiness
Overall: 🦀 challenger crab
Proof: 🦀 challenger crab
Patch quality: 🦀 challenger crab
Result: ready for maintainer review.

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

Next step before merge

  • No ClawSweeper repair lane is needed; the remaining action is normal maintainer review and merge decision for a proof-backed performance PR.

Security
Cleared: The diff only changes Swift menu refresh logic and tests; no dependency, script, secret, entitlement, or supply-chain surface is introduced.

Review details

Best possible solution:

Land this focused performance patch after normal maintainer review, and keep broader reporter-specific performance follow-up under #1321 and the complementary #1352 path.

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

Yes, from source inspection and contributor proof, but I did not rerun it locally. Current main calls the readiness signature on each store observation, and the PR body shows a freshly built multi-provider app sampled with the menu closed and open.

Is this the best way to solve the issue?

Yes. The patch removes wasted closed-menu signature work while preserving open-menu refresh semantics through guarded baseline resync and targeted regression tests.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add rating: 🦀 challenger crab: Overall readiness is 🦀 challenger crab; proof is 🦀 challenger crab and patch quality is 🦀 challenger crab.
  • remove rating: 🦞 diamond lobster: Current PR rating is rating: 🦀 challenger crab, so this older rating label is no longer current.

Label justifications:

  • P2: This is a focused fix for a user-visible popup-menu performance regression with limited blast radius.
  • rating: 🦀 challenger crab: Overall readiness is 🦀 challenger crab; proof is 🦀 challenger crab and patch quality is 🦀 challenger crab.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes terminal-style real-app sampling, benchmark output, test output, and negative validation that directly cover the changed behavior after the fix.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes terminal-style real-app sampling, benchmark output, test output, and negative validation that directly cover the changed behavior after the fix.
Evidence reviewed

What I checked:

Likely related people:

  • Larry Hao(郝卓远): Recently changed merged-menu dismiss latency and touched the same menu scheduling/tracking files that this PR optimizes. (role: recent area contributor; confidence: high; commits: 65e39f4dcb3a, de55f4850b8d; files: Sources/CodexBar/StatusItemController+MenuRefreshScheduling.swift, Sources/CodexBar/StatusItemController+MenuTracking.swift, Sources/CodexBar/StatusItemController.swift)
  • hhh2210: Recent height-cache and menu fingerprint work overlaps the menu rebuild and refresh paths affected by this PR. (role: adjacent area contributor; confidence: medium; commits: 10239cc617cf, 7c083fab0c08; files: Sources/CodexBar/StatusItemController+Menu.swift, Sources/CodexBar/StatusItemController+MenuTracking.swift, Sources/CodexBar/StatusItemController+MenuCardHeightCache.swift)
  • Nicolas Hidemaru Ogoshi / 大越ニコラス秀丸: Recently changed closed-menu rebuild behavior during data refresh, which is directly adjacent to the stale-content baseline guard in this PR. (role: recent adjacent contributor; confidence: medium; commits: 4652e40682a8; files: Sources/CodexBar/StatusItemController+MenuTracking.swift, Tests/CodexBarTests/StatusMenuClosedPreparationTests.swift)
  • steipete: Current-main blame for the imported readiness signature and store observation code points to the repository's v0.32.4 baseline commit. (role: original/current implementation provenance; confidence: low; commits: 723734ef3422; files: Sources/CodexBar/StatusItemController.swift, Sources/CodexBar/StatusItemController+MenuRefreshScheduling.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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f351a7d6e6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +451 to +453
let refreshOpenMenus = self.openMenus.isEmpty
? false
: self.didMenuAdjunctReadinessChange()

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 Badge Keep the readiness baseline current while closed

When no menu is open this now skips didMenuAdjunctReadinessChange(), so lastMenuAdjunctReadinessSignature can remain at an older value even though invalidateMenus and a later menuWillOpen have rebuilt the closed/open menu with the newer data. If the first observed store change while that menu is open brings the signature back to the old value, this branch passes refreshOpenMenus: false; invalidateMenus then returns early for open menus, leaving the visible menu showing the intermediate closed-time snapshot. Updating the stored baseline when closed, or resetting it after rebuilding on open, avoids missing this revert case.

Useful? React with 👍 / 👎.

@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
Skipping the readiness signature while all menus are closed (the idle-cost
optimization) let the baseline drift from live store data. A menu reopened
from new data, followed by an open-menu change reverting to the previous
baseline value, was treated as unchanged and skipped the rebuild, leaving
stale content visible.

Re-anchor the baseline when a root menu opens (rebuilt from current data).
Only the root open re-anchors; nested submenu opens must not, to avoid
masking a pending parent refresh. Adds a focused regression test.

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

Copy link
Copy Markdown
Contributor Author

Pushed a follow-up commit addressing the review:

  • Correctness fix (the P1 baseline-staleness risk): the readiness baseline is now re-anchored when a root menu opens, so a closed→reopen→revert sequence can no longer compare equal against a stale baseline and skip a needed rebuild. Nested submenu opens deliberately do not resync, to avoid masking a pending parent refresh. Good catch.
  • Focused regression test added (reopening root menu resyncs readiness baseline so reverted store data still refreshes), and negative-validated: it fails with the resync removed and passes with it.
  • Proof added to the PR body: a 9.3x micro-benchmark for the per-tick double serialization this PR removes, plus the negative-validation output for the new test.

PR body updated with the above. @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 rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. 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. labels Jun 7, 2026
@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Added a direct runtime measurement to the PR body (mirroring what #1352 used): the real menuAdjunctReadinessSignature() costs ~78 µs/call with 4 providers × 30-day daily, and that full call ran on the main actor on every store mutation even with all menus closed — now skipped while closed. The per-call cost itself also drops via the 9.3x formatting change, and the baseline-resync correctness fix is negative-validated. @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 the merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. label Jun 7, 2026
@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Added real-app proof to the PR body: a freshly built ad-hoc bundle of this branch with 7 providers enabled and the menu closed, sampled for 65 s across a background refresh tick (refreshFrequency = oneMinute). The readiness/rebuild path (menuAdjunctReadinessSignature, populateMenu, observeStoreChanges, …) gets 0 hits on any thread, main thread stays parked in mach_msg, idle CPU ~0.3%. Plus the ~78 µs/call cost of the now-skipped signature and the 9.3x formatting speedup. Honest caveat noted: couldn't automate the menu-open capture locally (no Accessibility/Peekaboo). @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: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 7, 2026
…e#1351)

Only re-anchor the readiness baseline on root menu open when the menu was
actually rebuilt or is already fresh for the current menuContentVersion.
When refreshMenuForOpenIfNeeded preserves stale content during an in-flight
provider refresh, resyncing to live store data would mask the
refresh-completion update. Add a focused regression test (negative-validated).

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

Copy link
Copy Markdown
Contributor Author

Addressed the @clawsweeper P1 stale-preservation blocker:

  • Gate baseline resync on fresh visible content: root-menu resync now runs only when !menuNeedsRefresh(menu) after refreshMenuForOpenIfNeeded. If an in-flight provider refresh preserves stale content on open, the baseline is not re-anchored to live store data, so the refresh-completion update still registers as a readiness change and triggers a rebuild.
  • Focused regression test: root open during in flight refresh preserves stale content and does not resync baseline — negative-validated (fails with unconditional resync, passes with the gate).
  • Existing reopening root menu resyncs readiness baseline … test still passes.

make check clean; swift test --filter StatusMenuReadinessBaselineTests 2/2 pass.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 8, 2026

Copy link
Copy Markdown

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 8, 2026
@clawsweeper clawsweeper Bot removed the merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. label Jun 8, 2026
@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Checked off the last Test plan item — menu-open proof (7 providers, same ad-hoc bundle):

  • Open: `osascript` click on the CodexBar menu-bar item → 11 items visible, ~190 ms real time.
  • While open: held the menu open for 20 s and ran `sample CodexBar 20` — 0 hits on `menuAdjunctReadinessSignature`, `populateMenu`, `observeStoreChanges`, `invalidateMenus`, `updateMenuContentPreservingSwitcher`, `sizeThatFits`; main thread 17133/17138 samples parked in `mach_msg`.

PR body updated; all Test plan checkboxes are now complete. Clawsweeper already has this at diamond lobster / ready for maintainer look — the remaining step is maintainer merge.

@clawsweeper clawsweeper Bot added rating: 🦀 challenger crab Exceptional PR readiness: strong proof, clean patch, and convincing validation. and removed rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. labels Jun 8, 2026
@steipete
steipete merged commit 322c8f8 into steipete:main Jun 8, 2026
4 checks passed
@Yuxin-Qiao
Yuxin-Qiao deleted the perf/menu-readiness-signature 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: 🦀 challenger crab Exceptional PR readiness: strong proof, clean patch, and convincing validation. 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