docs/research/shared-spend-source-publications.md
Research date: 2026-08-18
CodexBar should expose one app-scoped, main-actor publication of immutable spend-source inputs and states. Async producers refresh that publication outside the menu-rendering path. The Overview menu and Usage & Spend dashboard synchronously project the same publication at different densities.
The publication must preserve source identity and truth state:
Every async producer captures identity before suspension and validates cancellation, generation, provider configuration, and ownership again before publication. Cancellation is only a performance tool; identity validation is the correctness boundary.
Independent dashboard scans also capture the regular token publication revision before suspension. Successful scans (including confirmed empty) acknowledge only that revision; failed attempts are tracked separately to prevent automatic retry loops. A newer regular publication triggers a targeted 365-day scan, with one coalesced follow-up when data arrives during a scan. Regular trigger revisions remain separate from authoritative dashboard data revisions, and short menu snapshots cannot substitute for dashboard history.
This fixes an independently reproduced dashboard freshness gap investigated alongside #3209 and #3176. The screenshot in #3209 is the regular provider-menu cost chart and submenu; its Claude root cause remains unproved. This dashboard fix does not establish a resolution of that report, #3194's Codex quota/persistence issue, or #3207's separate scanner fairness work.
The existing implementation already contains most of the required primitives:
TokenSnapshotPublication carries snapshot, publication revision, provider-config revision, and scope signature.UsageStore exposes synchronous current-config validation for provider publications.SpendDashboardController already has immutable request/result values, generation checks, and source-ownership reconciliation.codex:<account-id> source identifiers and cache identities.The architectural gap is that the richer 365-day, multi-account Codex, and OpenCodex result set remains private to a preferences-pane-owned controller. The Overview therefore reads a narrower provider-global cache and cannot achieve source parity.
Menu construction must remain synchronous and cache-only. It must not perform a network request, filesystem scan, or wait for an async refresh. A publication change can invalidate the next menu build, but structural or height-changing mutations should be deferred while AppKit is tracking an open menu.