Codex early weekly reset remains stale at 0–1% after #3177; 2% publishes normally
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 52/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- swift
- Domain
- backend, testing-qa
Research direction
Start with Sources/CodexBar/Providers/Codex/CodexWeeklyResetConfirmation.swift, especially makeDelayedCandidate and its prerequisites, then inspect UsageStore+CodexWeeklyResetCandidatePersistence.swift for account-scoped guards. Build a focused regression through the real admission path and file-backed account-snapshot store, including repeated exact low values and a 2% control. Done means diagnostics identify the rejected stage and the valid post-reset state is eventually persisted without accepting rebound or changed-account evidence.
Written by the indexing model from the issue text.
Description
Summary
After an early backend weekly reset, CodexBar continued showing and persisting the pre-reset weekly value of 53% used / 47% left while repeated direct reads from the packaged OAuth helper reported 0–1% used / 99–100% left for the same account, with exact confidence and a materially newer weekly reset boundary.
During the captured stale interval:
- the account-snapshot file was rewritten at two automatic-refresh checkpoints without advancing the public weekly snapshot;
weeklyResetCandidatewas absent in both persisted checkpoint records; and- manual Refresh left the snapshot timestamp, contents, and visible value unchanged.
A subsequent live inspection at 15:04:47 local time captured the persisted app state at 2% used / 98% left with the newer reset boundary. That later value no longer matched the guarded 0–1% reset shape: above 1%, initialDecision returns .publishInitial, so 2% is eligible for the ordinary publication path without delayed reset confirmation.
The live stale-publication symptom is established. The exact #3177 candidate-creation, compatibility, or persistence predicate that failed is not established by the available evidence. The original 59 ms reset-credit-expiry diagnosis is withdrawn.
Verified release context
#3177 was merged as commit 0a1aa53598c94003a87bcdcca4af88b0ad508421. That commit is an ancestor of the official v0.55.1 tag. This is therefore not a request to ship the already-merged #3177 change; it is a report about the reset shape that change was intended to recover.
Observed evidence
| Observation | Time, local | Public/persisted weekly state | Direct OAuth-helper state | Delayed candidate |
|---|---|---|---|---|
| First captured mismatch | 14:02 | 53% used / 47% left; snapshot still reported an older Sep 1 boundary | 0% used / 100% left; newer Sep 3 boundary; exact | not inspected at this instant |
| Automatic-refresh checkpoint | 14:04:49 | account file rewritten; public snapshot still 53% | — | absent in persisted record |
| Second direct helper read | 14:05 | public snapshot still 53% | 1% used / 99% left; same newer boundary; exact | not inspected at this instant |
| Automatic-refresh checkpoint | 14:09:49 | account file rewritten again; public snapshot still 53% | — | absent in persisted record |
| Manual Refresh | 14:12 | after 12 seconds, file timestamp and contents were unchanged; UI still showed 47% left | — | absent in unchanged record |
| Final direct helper control | 14:13 | persisted public snapshot still 53% with Sep 1 boundary | 1% used / 99% left with Sep 3 boundary; exact | absent in persisted comparison |
| Later accepted app state | 15:04:47 | 2% used / 98% left with Sep 3 boundary | — | none persisted after publication |
The directly captured stale interval runs from the first fresh helper observation at 14:02 through the failed manual refresh at 14:12. Sixty-two minutes elapsed between the 14:02 helper observation and the later 15:04 accepted state, but there is no retained sample for every intervening refresh. This report therefore does not claim that continuous staleness for the full 62 minutes is proven.
The direct packaged-helper reads are provider controls. They establish that OAuth authentication and upstream usage retrieval worked, but they do not establish the exact in-memory payload received by each automatic app refresh.
Why the 2% state is a useful control
In the shipped #3177 logic, the reset threshold is 1%. Confirmation is required only when the previous weekly usage is above 1% and the initial fresh usage is at most 1%; otherwise the initial snapshot publishes normally:
This gives a deterministic interpretation of the later observation:
- Fresh values at 0–1% match the guarded reset shape and require confirmation.
- A fresh value at 2% does not match that shape and can publish through
.publishInitial. - The later 2% publication is therefore not evidence that #3177's delayed-candidate recovery succeeded.
- It explains how the visible value could clear without a one-hour quota recalculation or a successful delayed confirmation.
At the two captured automatic-refresh checkpoints, the persisted public snapshot was still 53% and no delayed candidate was present. The evidence does not distinguish among these possibilities:
makeDelayedCandidatereturnednilbecause one of its strict prerequisites failed;- a candidate was produced but rejected by the account-scoped persistence guard;
- a candidate was created and discarded between the sampled file writes; or
- the automatic app refresh received evidence different from the direct helper controls.
Relevant seams:
Correction to the original 59 ms diagnosis
The original comparison normalized the persisted credit expiry to Unix seconds with a fractional component and compared it with the packaged CLI's whole-second ISO-8601 rendering.
That crossed serialization formats:
- the CLI explicitly sets
JSONEncoder.dateEncodingStrategy = .iso8601; - the account-snapshot store uses a default
JSONEncoder, whose numericDaterepresentation preserves fractional precision.
Source references:
Those outputs are compatible with the same in-memory Date. The available CLI output does not prove that the previous, initial, and confirmation observations differed by 59 ms inside the app. Exact expiry equality may still be a brittle seam, but it is not established as the cause of this incident, and this report does not request a Date-comparison change.
Expected behavior
When #3177's bounded sequence receives sufficient consistent exact OAuth evidence for the same account, source, plan, reset boundary, and unchanged positive reset-credit inventory, CodexBar should eventually publish the 0–1% post-reset weekly state. The user should not need to consume enough quota to produce a value above the 1% confirmation threshold.
Actual behavior
- Direct exact OAuth-helper reads returned the fresh 0–1% state and newer reset boundary.
- At two automatic-cadence file writes, the public snapshot remained at 53% and no delayed candidate was persisted.
- Manual Refresh did not advance the snapshot, boundary, update time, or visible value.
- A later 2% state published after the fresh value was above the guarded threshold.
Live reproduction shape
The backend reset is externally timed, so the complete trigger is not locally deterministic.
- Begin with a persisted exact OAuth weekly snapshot above 1% used and an old future reset boundary.
- Have the backend perform an early weekly reset without consuming the available reset credit.
- Use the packaged OAuth helper as a provider control and observe repeated exact 0–1% values with a materially advanced, consistent reset boundary for the same account.
- Observe the app through multiple automatic refresh intervals and invoke manual Refresh.
- Inspect the persisted account record after each checkpoint for the public weekly state and
weeklyResetCandidate. - Separately retain a later observation above 1% as a control for the ordinary publication path.
Requested investigation and safe fix boundary
Please identify the failed candidate stage before changing reset-admission policy. A focused regression should drive the complete sequence through the real admission and file-persistence seams while recording reason-coded outcomes:
- previous exact OAuth state above 1% with the old future boundary;
- repeated exact OAuth states at 0–1% with a consistent advanced boundary;
- stable account, plan, and source;
- the same observed credit ID, reset type, status, and available count across the sequence;
- raw in-memory expiry values captured from the same test observations rather than inferred across encoders;
- at least one refresh after the delayed minimum age;
- a real file-backed account-snapshot store; and
- a 2% control observation proving the ordinary publication path.
Diagnostics should distinguish:
- which
makeDelayedCandidateprerequisite failed, if creation returnednil; - whether account-scoped candidate persistence rejected the refresh guard; and
- which compatibility or age check discarded an existing candidate.
Existing safeguards should remain for single transient low samples, account/source/plan changes, non-exact data, non-monotonic timestamps, inconsistent boundaries, missing credit evidence, changed credit identity/status/count, and stale or expired candidates.
A counterexample that must remain rejected is a single 0–1% observation followed by a rebound, or a low sequence whose account, source, plan, reset boundary, or credit identity changes between observations.
Related work
- #2790: original early-reset stale-quota report.
- #2897: explicitly-zero-credit subcase.
- #3179: unchanged-positive-credit follow-up.
- #3177: merged delayed-candidate recovery intended to address this reset shape; its merge commit is included in v0.55.1.
- #3194: similar successful-fetch/stale-publication symptom; this report does not claim every instance shares one cause.
- #3200: automatic-refresh persistence coverage uses 31%, 42%, and 56%, all above the reset-confirmation threshold.
Evidence status
The UI screenshots, persisted percentages and reset boundaries, helper results, file-write times, candidate absence at the two captured checkpoints, and failed manual-refresh result remain valid observations.
Any earlier evidence text attributing the incident to a 59 ms expiry mismatch is superseded. The corrected evidence preserves the raw observations but classifies the exact failed predicate as unknown.
- Dominant language
- Swift
- Stars
- 21.6k
- Forks
- 2k
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 385
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from steipete/CodexBar
-
clawsweeper:needs-live-repro clawsweeper:needs-maintainer-review clawsweeper:no-new-fix-pr impact:ux-friction issue-rating: 🐚 platinum hermit P2
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
clawsweeper:needs-maintainer-review clawsweeper:needs-product-decision clawsweeper:no-new-fix-pr clawsweeper:source-repro impact:ux-friction issue-rating: 🦞 diamond lobster P2
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
clawsweeper:needs-maintainer-review clawsweeper:needs-product-decision clawsweeper:no-new-fix-pr impact:ux-friction issue-rating: 🌊 off-meta tidepool P3
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
clawsweeper:needs-maintainer-review clawsweeper:needs-product-decision clawsweeper:needs-security-review clawsweeper:no-new-fix-pr impact:auth-provider impact:security issue-rating: 🌊 off-meta tidepool P2
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
impact:ux-friction issue-rating: 🦪 silver shellfish P2
Difficulty 3/5 1-2 days Newbie friendliness 65/100
All issues in steipete/CodexBar
Similar issues
-
tvOS
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
skiptools/skip-fuse-ui#147 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
OneBusAway/onebusaway-ios#1438 · 1 reaction ·