fix: Monthly usage window respects subscription anchor, fix setManualSpentTargets#69
Conversation
…SpentTargets window mismatch The monthly cost aggregation was using calendar month boundaries after my previous change, but the OpenCode Go billing is anchor-based (resets on the subscription day/hour). The auto-anchor from the earliest SQLite row was also lost, causing the window to shift when crossing calendar boundaries and making the percentage jump backward. Root cause of the setManualSpentTargets bug: getSummary() computed the monthly cost for the CURRENT window (no anchor). After storing the anchor, the window changed, so tracked + baseline no longer equaled the target. Changes: - buildMonthlyWindow(nowMs, baseline, earliestMs?) with 3-tier priority: 1. User-configured anchor from baseline (set via Set Spent Targets) 2. Auto-anchor from earliest SQLite row (restores original behavior) 3. Calendar month fallback (for tracked-only path) - buildSummaryFromRows passes earliest SQLite timestamp for auto-anchor - buildSummaryFromTracked uses baseline anchor or calendar month fallback - setManualSpentTargets computes trackedMonthly using the PROSPECTIVE window (with the new anchor), reading SQLite costs directly instead of relying on getSummary() which would use the old window - CHANGELOG updated
|
Nice one @Wallacy , clean fix. I checked out the branch locally: compile passes, 86/86 tests pass. Root cause analysis is solid, especially bug 2 (window shifts after the anchor gets stored). The 3-tier fallback in Two minor notes, neither a merge blocker:
Merging as-is. If you want to add tests or extract the helper, happy to take a follow-up PR. Thanks for the fix 🙏 |
fix: Monthly usage window respects subscription anchor, fix setManualSpentTargets — fixes monthly cost drift
Goal
Fix the monthly usage percentage jumping backward (e.g. 30% → 8% → 46%) due
to two bugs introduced in the SQLite wiring (PR #60).
Root cause
Bug 1 — Auto-anchor lost.
buildSummaryFromRowsoriginally derived themonthly window from the earliest SQLite row's day-of-month (auto-anchor),
matching the actual billing start. My previous change replaced this with
calendar month boundaries, causing the window to shift when crossing the
anchor boundary and dropping accumulated costs.
Bug 2 — setManualSpentTargets window mismatch. When the user set a monthly
target with a new anchor day,
getSummary()computed the monthly cost usingthe CURRENT window (no anchor). After storing the anchor, the window changed,
so
tracked + baselineno longer equaled the target — the displayed valuediverged from what the user entered.
What was implemented
buildMonthlyWindow(nowMs, baseline, earliestMs?)with 3-tier priority:buildSummaryFromRowspasses the earliest SQLite timestamp for auto-anchorsetManualSpentTargetscomputestrackedMonthlyusing the PROSPECTIVEwindow (with the new anchor), reading SQLite costs directly instead of
relying on
getSummary()which uses the old windowTesting
86/86 unit tests pass. Anchor simulation:
Notes
Users who previously set manual targets with an anchor may want to clear and
re-set them after applying this fix, since the tracked monthly cost is now
computed against the correct window. The auto-anchor should work out of the
box — no manual config needed if the earliest SQLite row matches the
subscription start date.