From de882960add408837d27750cf17587148c3fb687 Mon Sep 17 00:00:00 2001 From: Yuxin Qiao <104957188+Yuxin-Qiao@users.noreply.github.com> Date: Thu, 20 Aug 2026 18:13:49 +0800 Subject: [PATCH] fix(spend): widen ownership and revision coverage sameSourceOwnership only compared 4 fields, so switching bucketTimeZoneIdentifier / openCodexUsageLogsEnabled / hideNativeCodexCostWhenOpenCodexPresent / hiddenSourceIDs / preferredCurrencyCode during an in-flight forceRefresh was coalesced as same-owner and left daily/hourly/chartDomain stale. Compare the display-affecting fields explicitly. snapshotRevision only hashed daily[]. An OpenCodex hourly-only change or project/session update did not bump sourceRevisions, so ForcedOutcome.incorporating treated it as not newer and discarded the confirmedNonempty input. Hash hourly / projects / sessions as well. --- .../CodexBar/SpendDashboardController.swift | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Sources/CodexBar/SpendDashboardController.swift b/Sources/CodexBar/SpendDashboardController.swift index c6f454e665..436f68a8f7 100644 --- a/Sources/CodexBar/SpendDashboardController.swift +++ b/Sources/CodexBar/SpendDashboardController.swift @@ -686,6 +686,26 @@ enum SpendDashboardSource { encoder.append(breakdown.priorityTokens) } } + encoder.append(snapshot.hourly.count) + for entry in snapshot.hourly { + encoder.append(entry.hour.timeIntervalSinceReferenceDate) + encoder.append(entry.totalTokens) + encoder.append(entry.costUSD) + } + encoder.append(snapshot.projects.count) + encoder.append(snapshot.sessions.count) + for project in snapshot.projects { + encoder.append(project.name) + encoder.append(project.path ?? "") + encoder.append(project.totalTokens) + encoder.append(project.totalCostUSD) + encoder.append(project.daily.count) + } + for session in snapshot.sessions { + encoder.append(session.sessionID) + encoder.append(session.totalTokens) + encoder.append(session.costUSD) + } return encoder.finalize() } @@ -1540,7 +1560,12 @@ final class SpendDashboardController { lhs.costUsageEnabled == rhs.costUsageEnabled && lhs.providerIDs == rhs.providerIDs && lhs.codexAccountIdentities == rhs.codexAccountIdentities && - lhs.sourceOwnershipFingerprints == rhs.sourceOwnershipFingerprints + lhs.sourceOwnershipFingerprints == rhs.sourceOwnershipFingerprints && + lhs.bucketTimeZoneIdentifier == rhs.bucketTimeZoneIdentifier && + lhs.openCodexUsageLogsEnabled == rhs.openCodexUsageLogsEnabled && + lhs.hideNativeCodexCostWhenOpenCodexPresent == rhs.hideNativeCodexCostWhenOpenCodexPresent && + lhs.hiddenSourceIDs == rhs.hiddenSourceIDs && + lhs.preferredCurrencyCode == rhs.preferredCurrencyCode } private static func invalidatedSourceIDs(