Skip to content

Keep codex account file reads off the menu-build path#1401

Merged
steipete merged 2 commits into
steipete:mainfrom
ProspectOre:perf/menu-account-snapshot-stale
Jun 11, 2026
Merged

Keep codex account file reads off the menu-build path#1401
steipete merged 2 commits into
steipete:mainfrom
ProspectOre:perf/menu-account-snapshot-stale

Conversation

@ProspectOre

@ProspectOre ProspectOre commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Keep Codex account discovery, auth.json reads, JWT parsing, and auth fingerprint hashing off the menu-build and menu-tracking paths.

Problem

Sampling the provider switch path showed synchronous account reconciliation below:

populateMenu
  -> codexAccountMenuDisplay
  -> codexVisibleAccountProjection
  -> codexAccountReconciliationSnapshot
  -> DefaultCodexAccountReconciler.loadSnapshot
  -> auth.json read / JWT parse / fingerprint hashing

The same work was also reachable through the menu identity signature. Once the short-lived reconciliation cache expired, opening or rebuilding the menu could perform file and crypto work on the main actor.

Change

  • Render account rows from a separate immutable menu projection cache. Menu reads are optional and side-effect free; a cold cache never performs synchronous discovery.
  • Prewarm and revalidate that projection asynchronously at startup and on menu open. The blocking snapshot loader runs off the main actor.
  • Coalesce revalidation into one controller-owned task and cancel it during shutdown.
  • Apply background results only when the persisted active source and reconciliation generation still match.
  • Preserve stale menu data during ordinary refresh invalidations, but clear it for active-source and managed-account structural changes.
  • Prevent per-account override loads from replacing the persisted account menu projection.
  • Keep menu identity calculation on already-cached account data instead of routing through UsageStore.accountInfo.
  • Select a displayed account through the source captured by its rendered row, avoiding a reconciliation read in the click callback.
  • Do not rebuild an actively tracked menu when background account data changes; mark menus stale for the next safe rebuild.

Validation

  • swift test --filter 'CodexAccountMenuDisplaySnapshotTests|CodexAccountReconciliationTests|StatusMenuCodexSwitcherTests' (55 tests, 3 suites, pass)
  • make check (SwiftFormat clean; SwiftLint 0 violations)
  • git diff --check
  • Structured Codex autoreview: clean after fixing override-cache poisoning and managed-account invalidation findings

Runtime Evidence

The original field samples captured repeated DefaultCodexAccountReconciler.loadSnapshot and loadLiveSystemAccount frames below populateMenu. The rewritten path has no reconciliation call from menu projection reads or menu identity calculation; tests use a blocking loader probe to verify menu open remains non-blocking and the loader executes off the main thread.

Part of #1387.

@ProspectOre

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 10, 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.

@ProspectOre

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-run

@clawsweeper

clawsweeper Bot commented Jun 10, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

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

populateMenu -> codexAccountMenuDisplay loaded the codex account
reconciliation snapshot synchronously whenever the 2s freshness cache
had lapsed, paying auth.json reads, JWT parsing, and SHA256
fingerprinting on the main thread inside menu open and tracking. Menu
display now tolerates a stale cached snapshot and revalidates the cache
off the menu-build path; account changes land on the next rebuild.

Copy link
Copy Markdown
Contributor

Local validation on 53cd6151a8f1a371fd7a5fa5fa90349e9b51ddb7:

git diff --check HEAD^ HEAD
HOME=$PWD/.home CLANG_MODULE_CACHE_PATH=$PWD/.module-cache swift test --filter 'CodexAccountMenuDisplaySnapshotTests|CodexAccountReconciliationTests'

Result:

  • git diff --check: passed
  • CodexAccountMenuDisplaySnapshotTests|CodexAccountReconciliationTests: passed, 29 tests in 2 suites
  • Environment: /Applications/Xcode.app/Contents/Developer, Apple Swift 6.3.2

The new menu-display snapshot coverage passed, including menu display snapshot tolerates stale cache and revalidates off the menu path, which is the key behavior this PR needs for keeping Codex account reconciliation file reads out of populateMenu / menu tracking.

Scope note: I did not run a packaged app UI sample here; this is focused local regression validation for the menu-account snapshot path.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor

Checked the current merge conflict against origin/main (02c94032). After deepening the shallow checkout, git merge --no-commit --no-ff origin/main reports only one unresolved file: CHANGELOG.md.

The conflict is limited to the 0.32.6 — Unreleased / Fixed list: this PR’s menu-build-path changelog line needs to be kept alongside newer main entries for #1409, #1406, and #1410. I did not see source-level unresolved conflicts in this merge check.

So the rebase looks low-risk: keep this PR’s changelog item under 0.32.6 — Unreleased / Fixed together with the new main entries, then rerun the focused tests/CI. I reset the local merge state after inspection.

@steipete steipete force-pushed the perf/menu-account-snapshot-stale branch from 53cd615 to f1c1b6c Compare June 11, 2026 03:08
@clawsweeper

clawsweeper Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed June 10, 2026, 11:13 PM ET / 03:13 UTC.

Summary
The PR renders Codex account menu state from an immutable cache, revalidates it asynchronously, and removes synchronous reconciliation from menu construction, identity calculation, and account selection callbacks.

Reproducibility: yes. from current-main source and the reported field samples: once the short reconciliation cache expires, Codex menu construction can synchronously enter account discovery, file reading, JWT parsing, and fingerprinting. A live reproduction was not performed during this read-only review.

Review metrics: 2 noteworthy metrics.

  • Changed surface: 11 files; +529/-17. The focused performance fix introduces a coordinated cache lifecycle across settings and menu-controller paths.
  • Focused validation: 55 tests across 3 suites reported passing. The selected suites cover reconciliation, projection caching, and Codex account-switcher behavior.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof from a real setup is added.

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

Rank-up moves:

  • Post redacted Instruments, sample, terminal output, or runtime logs from the packaged app showing menu opening and provider switching no longer call loadSnapshot or loadLiveSystemAccount.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR provides focused blocking-loader tests but explicitly lacks after-fix packaged-app runtime evidence; post redacted diagnostics and update the PR body to trigger a fresh review, or ask a maintainer to comment @clawsweeper re-review.

Mantis proof suggestion
A native menu interaction paired with visible diagnostic output would directly verify the claimed main-thread performance boundary. 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: capture CodexBar menu open/provider switching with redacted diagnostics proving `loadSnapshot` and `loadLiveSystemAccount` stay off the menu-tracking path.

Risk before merge

  • [P1] The contributor has not supplied after-fix evidence from a packaged CodexBar build, so source structure and test probes do not directly demonstrate that the observed production menu stall is gone.

Maintainer options:

  1. Decide the mitigation before merge
    Keep the guarded asynchronous projection design and verify a packaged build with redacted sampling that menu opening and provider switching avoid reconciliation while account add, removal, and active-source changes appear on the next safe menu rebuild.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] The remaining action is contributor runtime proof followed by normal maintainer merge review; no concrete code defect warrants an automated repair job.

Security
Cleared: No concrete security or supply-chain concern was found; the patch adds no dependencies, permissions, downloads, secret exposure, or new artifact-execution paths.

Review details

Best possible solution:

Keep the guarded asynchronous projection design and verify a packaged build with redacted sampling that menu opening and provider switching avoid reconciliation while account add, removal, and active-source changes appear on the next safe menu rebuild.

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

Yes from current-main source and the reported field samples: once the short reconciliation cache expires, Codex menu construction can synchronously enter account discovery, file reading, JWT parsing, and fingerprinting. A live reproduction was not performed during this read-only review.

Is this the best way to solve the issue?

Yes. Separating a side-effect-free menu projection from the authoritative reconciliation snapshot directly removes blocking work from the menu path while preserving the existing account model and non-menu reconciliation behavior.

AGENTS.md: found and applied where relevant.

Codex review notes: reasoning high; reviewed against 02c94032c79a.

Label changes

Label changes:

  • add P2: This is a normal-priority provider-specific responsiveness bug with a bounded menu and multi-account blast radius.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR provides focused blocking-loader tests but explicitly lacks after-fix packaged-app runtime evidence; post redacted diagnostics and update the PR body to trigger a fresh review, or ask a maintainer to comment @clawsweeper re-review.

Label justifications:

  • P2: This is a normal-priority provider-specific responsiveness bug with a bounded menu and multi-account blast radius.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR provides focused blocking-loader tests but explicitly lacks after-fix packaged-app runtime evidence; post redacted diagnostics and update the PR body to trigger a fresh review, or ask a maintainer to comment @clawsweeper re-review.
Evidence reviewed

Acceptance criteria:

  • [P1] swift test --filter 'CodexAccountMenuDisplaySnapshotTests|CodexAccountReconciliationTests|StatusMenuCodexSwitcherTests'.
  • [P1] make check.
  • [P1] git diff --check.
  • [P1] Run a packaged CodexBar build and capture redacted sampling while repeatedly opening the Codex menu and switching providers.

What I checked:

Likely related people:

  • steipete: Current-main blame ties the reconciliation cache, visible-account projection, and menu-display integration to the v0.32.5 commit; steipete also authored the latest PR-head commit refining this path. (role: introduced current behavior and recent area contributor; confidence: high; commits: 920997c6a365, f1c1b6cdf3ea; files: Sources/CodexBar/Providers/Codex/CodexSettingsStore.swift, Sources/CodexBar/StatusItemController+AccountMenuDisplay.swift, Sources/CodexBar/StatusItemController+MenuTracking.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: 🦪 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. P2 Normal priority bug or improvement with limited blast radius. labels Jun 11, 2026
@clawsweeper

clawsweeper Bot commented Jun 11, 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.

@ProspectOre

Copy link
Copy Markdown
Contributor Author

Runtime proof — packaged app at current head f1c1b6c, after-fix

Per the review ask: redacted sample captures from a packaged build showing menu opening and provider switching no longer call loadSnapshot or loadLiveSystemAccount.

Setup

  • Packaged from this PR's head f1c1b6cdf3eadaa8cfd0ba15aa99c78bfe3c08bc via CODEXBAR_SIGNING=adhoc Scripts/package_app.sh release
  • Running as the only CodexBar instance; real day-to-day config: merged icons, codex + claude enabled, cost usage ON, multiple Codex accounts present in auth.json
Analysis of sampling CodexBar (pid 82016) every 1 millisecond
Identifier:      com.steipete.codexbar
Version:         0.32.6 (81)
Code Type:       ARM64
OS Version:      macOS 26.5.1 (25F80)
Analysis Tool:   /usr/bin/sample

Method

  1. Menu opening — 12 rounds: AX click of the status item to open the menu, 3 s dwell (longer than the 2 s reconciliation cache interval, so every open hits the stale-projection path), close. Each round captured with sample CodexBar 6 1 (1 ms granularity). 11 of 12 capture files were written (round 3's sample failed to produce a file; not a frame anomaly).
  2. Provider switching — one sample CodexBar 20 1 capture while AX-clicking the in-menu provider switcher segments inside an open tracking session: Codex → Claude → Overview → Codex → Claude → Codex, 2.5 s apart.

Result: zero reconciler frames in every capture

$ grep -c "loadSnapshot\|loadLiveSystemAccount" s01..s12.txt switch01.txt
s01.txt:0   s02.txt:0   s04.txt:0   s05.txt:0   s06.txt:0   s07.txt:0
s08.txt:0   s09.txt:0   s10.txt:0   s11.txt:0   s12.txt:0   switch01.txt:0

Every capture demonstrably contains live menu tracking (so the zeros are not from sampling a closed menu):

$ grep -c "NSMenuTrackingSession" s01..s12.txt switch01.txt
s01.txt:10  s02.txt:2   s04.txt:2   s05.txt:30  s06.txt:13  s07.txt:15
s08.txt:5   s09.txt:16  s10.txt:2   s11.txt:2   s12.txt:2   switch01.txt:22

Representative main-thread stack from s05.txt — 3,685 of ~6,000 samples are inside the menu-tracking event loop, with no reconciliation/JWT/fingerprint frames anywhere below it:

3685 +[NSStatusBarButtonCell popUpStatusBarMenu:ofItem:ofBar:inRect:ofView:withEvent:]  (in AppKit)
  3685 -[NSCocoaMenuImpl popUpMenu:atLocation:width:forView:withSelectedItem:withFont:withFlags:withOptions:]  (in AppKit)
    3685 -[NSCocoaMenuImpl popUpMenu:atLocation:width:view:selectedItemIndexProvider:font:positioningRect:flags:options:]  (in AppKit)
      3685 _NSPopUpMenu  (in AppKit)
        3685 +[NSContextMenuImpl presentPopup:fromView:withContext:animated:]  (in AppKit)
          3685 -[NSContextMenuTrackingSession startMonitoringEvents:]  (in AppKit)
            3685 -[NSMenuTrackingSession startMonitoringEvents:]  (in AppKit)
              3618 -[NSMenuTrackingSession startRunningMenuEventLoop:]  (in AppKit)
                3618 -[NSApplication(NSEventRouting) nextEventMatchingMask:untilDate:inMode:dequeue:]  (in AppKit)

The switching capture (switch01.txt) additionally shows StatusItemController.menuWillOpen / menuDidClose and provider-switcher activity during the session — and still zero loadSnapshot / loadLiveSystemAccount frames:

1 @objc StatusItemController.menuWillOpen(_:)  (in CodexBar)
1 StatusItemController.menuDidClose(_:)  (in CodexBar)  StatusItemController+Menu.swift:147
7 closure #1 in ProviderSwitcherShortcutEventMonitor.init(events:peekGate:callback:)  (in CodexBar)  StatusItemController+ProviderSwitcher.swift:94

Account data still renders correctly in the menu (accounts list, cost rows, switcher all populated), so the cached-projection path is serving real data while revalidation stays off the menu path.

@ProspectOre

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

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

Re-review progress:

@steipete steipete merged commit febf562 into steipete:main Jun 11, 2026
4 checks passed
@steipete

Copy link
Copy Markdown
Owner

Landed in febf562741e56ab4b9dd58a271a839586cf6344e.

The final rewrite keeps Codex account reconciliation and menu identity reads off menu tracking, adds source/generation guards around background revalidation, and covers override-cache and managed-account invalidation regressions.

Proof: 55 focused tests passed, make check passed, structured autoreview was clean, and all exact-head CI checks passed. Thanks @ProspectOre!

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. 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants