diff --git a/Sources/CodexBar/Providers/Codex/CodexWeeklyResetConfirmation.swift b/Sources/CodexBar/Providers/Codex/CodexWeeklyResetConfirmation.swift index 23edba9924..4470c18c98 100644 --- a/Sources/CodexBar/Providers/Codex/CodexWeeklyResetConfirmation.swift +++ b/Sources/CodexBar/Providers/Codex/CodexWeeklyResetConfirmation.swift @@ -178,7 +178,13 @@ struct CodexWeeklyResetConfirmation: Sendable { return false } let previouslyAvailableCredits = previousCredits.availableCredits(at: previousCredits.updatedAt) - guard !previouslyAvailableCredits.isEmpty else { return false } + // An explicitly observed zero-credit inventory means there was no manual reset credit to + // consume. Requiring consumption proof here would deadlock: the two consistent observations + // (initial + confirmation) are the only signal a server-side early reset has, so trust them. + // A nil/unknown previous inventory stays conservative and keeps demanding consumption proof. + guard !previouslyAvailableCredits.isEmpty else { + return previousCredits.availableCount == 0 + } return previouslyAvailableCredits.contains { previousCredit in Self.inventoryConfirmsConsumption( previousCredit: previousCredit, diff --git a/Tests/CodexBarTests/CodexWeeklyResetConfirmationTests.swift b/Tests/CodexBarTests/CodexWeeklyResetConfirmationTests.swift index 6a3bda3e52..11fd5f79ba 100644 --- a/Tests/CodexBarTests/CodexWeeklyResetConfirmationTests.swift +++ b/Tests/CodexBarTests/CodexWeeklyResetConfirmationTests.swift @@ -348,6 +348,40 @@ struct CodexWeeklyResetConfirmationTests { == .publishConfirmation) } + @Test + func `zero available reset credits confirm a server-side early weekly reset by observation`() throws { + let formatter = ISO8601DateFormatter() + let previousCapturedAt = try #require(formatter.date(from: "2026-08-13T03:36:16Z")) + let previousReset = try #require(formatter.date(from: "2026-08-18T00:49:16Z")) + let initialCapturedAt = try #require(formatter.date(from: "2026-08-13T03:41:46Z")) + let initialReset = try #require(formatter.date(from: "2026-08-20T03:38:06Z")) + let previous = self.snapshot( + capturedAt: previousCapturedAt, + weeklyUsed: 98, + weeklyReset: previousReset, + resetCredits: self.emptyResetCredits(capturedAt: previousCapturedAt)) + let initial = self.snapshot( + capturedAt: initialCapturedAt, + weeklyUsed: 1, + weeklyReset: initialReset, + resetCredits: self.emptyResetCredits(capturedAt: initialCapturedAt)) + let confirmation = self.snapshot( + capturedAt: initialCapturedAt.addingTimeInterval(30), + weeklyUsed: 1, + weeklyReset: initialReset.addingTimeInterval(30), + resetCredits: self.emptyResetCredits(capturedAt: initialCapturedAt.addingTimeInterval(30))) + + #expect( + CodexWeeklyResetConfirmation.initialDecision(previous: previous, initial: initial) + == .requiresConfirmation) + #expect( + CodexWeeklyResetConfirmation.confirmationDecision( + previous: previous, + initial: initial, + confirmation: confirmation) + == .publishConfirmation) + } + @Test func `one missing reset credit inventory does not confirm an early manual weekly reset`() throws { let formatter = ISO8601DateFormatter()