[BUG] Weekly usage bar exhausted in August with 30–45% LOWER token consumption than June (Max 20x)
Plan: Max 20x · Client: Claude Code CLI 2.1.235 (native install), macOS · Promo: the +50% weekly-limit promotion (May–Aug 2026) was active in both periods compared below.
Summary
Weekly usage, measured from local transcripts (~/.claude/projects, deduplicated by requestId, all sessions and subagents included), was 30–45% higher in June than in August — yet June weeks ended at roughly 75–80% of the weekly bar while August weeks hit 100% mid-week.
| Week (Mon) | Requests | cache_read tokens | output tokens | Weekly bar at end of week |
|------------|---------:|------------------:|--------------:|---------------------------|
| Jun 1 | 27,839 | 4.71B | 9.8M | ~75–80% |
| Jun 29 | 25,666 | 5.14B | 7.0M | ~75–80% |
| Aug 3 | 11,954 | 3.30B | 5.7M | 100% (maxed) |
| Aug 10 | 18,662 | 3.56B | 8.7M | 100% (maxed) |
Model mix (share of input-side tokens): June was more Opus-heavy (Opus 4.8 ≈ 71% in the Jun 1 week) than August (Opus 4.8 + Opus 5 ≈ 54% in the Aug 10 week). Fable 5: 0% in the Jun 1 week, ≈12% in the Jun 29 week, ≈39% in the Aug 3 week, ≈12% in the Aug 10 week. The cleanest like-for-like pair is therefore Jun 29 vs Aug 10: same Fable share, same Sonnet range, June 38% more requests and 45% more cache_read — June did not max, August did.
Usage pattern (interactive Claude Code plus subagent orchestration, same projects, same workflow) was unchanged across the period.
Expected
Same or lower token consumption → same or lower percentage of the weekly bar.
Actual
Lower consumption → weekly bar exhausted, with no change on my side.
Weekly-bar values are as observed in /usage at the end of each week; Claude Code does not log them locally. Server-side metering for this account over the same weeks will confirm or refute the comparison.
Per-day breakdown
Same source (local transcripts, deduplicated by requestId, main sessions + subagents). Model columns are the share of input-side tokens (cache_read + cache_write + input) per model family; the remainder is Haiku/other. Days are local time (America/Los_Angeles).
| Date | Requests | cache_read | cache_write | input | output | Opus % | Sonnet % | Fable % |
|------|---------:|-----------:|------------:|------:|-------:|-------:|---------:|--------:|
| Week of Jun 1 | | | | | | | | |
| Jun 1 Mon | 3,726 | 774.8M | 17.9M | 476k | 1.4M | 58 | 41 | 0 |
| Jun 2 Tue | 4,415 | 817.5M | 23.3M | 555k | 2.0M | 71 | 29 | 0 |
| Jun 3 Wed | 2,911 | 521.0M | 13.6M | 325k | 1.4M | 91 | 9 | 0 |
| Jun 4 Thu | 4,826 | 728.8M | 31.5M | 5.9M | 1.9M | 84 | 16 | 0 |
| Jun 5 Fri | 6,740 | 1.19B | 22.1M | 1.3M | 1.4M | 78 | 22 | 0 |
| Jun 6 Sat | 3,879 | 455.4M | 15.1M | 288k | 1.2M | 46 | 53 | 0 |
| Jun 7 Sun | 1,342 | 220.4M | 10.6M | 163k | 694k | 74 | 26 | 0 |
| Week of Jun 29 | | | | | | | | |
| Jun 29 Mon | 2,709 | 549.1M | 21.7M | 697k | 1.1M | 80 | 20 | 0 |
| Jun 30 Tue | 3,400 | 527.7M | 11.5M | 631k | 838k | 71 | 29 | 0 |
| Jul 1 Wed | 3,270 | 482.7M | 20.3M | 782k | 766k | 56 | 35 | 9 |
| Jul 2 Thu | 3,878 | 966.5M | 15.0M | 342k | 740k | 30 | 63 | 7 |
| Jul 3 Fri | 6,227 | 1.26B | 25.9M | 1.1M | 1.2M | 44 | 41 | 15 |
| Jul 4 Sat | 4,695 | 940.0M | 31.9M | 455k | 1.4M | 43 | 46 | 11 |
| Jul 5 Sun | 1,487 | 411.8M | 15.6M | 325k | 964k | 30 | 18 | 52 |
| Week of Aug 3 | | | | | | | | |
| Aug 3 Mon | 1,185 | 402.5M | 12.5M | 4k | 893k | 40 | 0 | 58 |
| Aug 4 Tue | 398 | 105.8M | 3.3M | 1k | 258k | 42 | 0 | 58 |
| Aug 5 Wed | 1,680 | 435.7M | 11.5M | 19k | 994k | 54 | 12 | 33 |
| Aug 6 Thu | 3,484 | 871.3M | 32.1M | 61k | 1.4M | 42 | 34 | 23 |
| Aug 7 Fri | 3,330 | 969.2M | 20.9M | 32k | 1.2M | 41 | 12 | 47 |
| Aug 8 Sat | 1,578 | 448.6M | 16.6M | 8k | 821k | 66 | 1 | 33 |
| Aug 9 Sun | 299 | 61.9M | 3.7M | 2k | 126k | 75 | 0 | 25 |
| Week of Aug 10 | | | | | | | | |
| Aug 10 Mon | 2,356 | 654.2M | 20.3M | 21k | 1.2M | 57 | 8 | 35 |
| Aug 11 Tue | 2,804 | 563.4M | 18.7M | 23k | 1.4M | 61 | 28 | 11 |
| Aug 12 Wed | 3,785 | 563.2M | 24.5M | 28k | 1.2M | 66 | 29 | 5 |
| Aug 13 Thu | 4,806 | 739.7M | 17.8M | 69k | 1.8M | 42 | 58 | 0 |
| Aug 14 Fri | 2,772 | 488.1M | 17.0M | 52k | 1.5M | 43 | 57 | 0 |
| Aug 15 Sat | 1,735 | 402.4M | 10.8M | 27k | 1.1M | 61 | 30 | 9 |
| Aug 16 Sun | 404 | 144.1M | 5.1M | 2k | 449k | 63 | 0 | 37 |
One visible difference between the periods: uncached input tokens are ~10–100× lower in August (tens of k/day vs hundreds of k to M/day in June), i.e. prompt caching was more effective in August, not less — which should make August cheaper, not more expensive.
Measurement script
Standard library only. Reads ~/.claude/projects/**/*.jsonl, de-duplicates by requestId, prints aggregates only (no paths, session ids, or message text). The tables above were produced with:
python3 claude_usage_report.py --weeks 2026-06-01,2026-06-29,2026-08-03,2026-08-10
<details>
<summary>claude_usage_report.py</summary>
#!/usr/bin/env python3
"""Aggregate Claude Code token usage from local transcripts.
Reads ~/.claude/projects/**/*.jsonl, de-duplicates API responses by requestId,
and prints per-week and per-day totals plus model-family shares.
Output contains aggregate numbers only: no paths, session ids, or message text.
Usage:
python3 claude_usage_report.py # all weeks found
python3 claude_usage_report.py --since 2026-06-01
python3 claude_usage_report.py --weeks 2026-06-01,2026-06-29,2026-08-03,2026-08-10
python3 claude_usage_report.py --tz -7 # UTC offset hours for day bucketing (default: local)
"""
import argparse
import collections
import datetime as dt
import glob
import json
import os
import sys
def family(model: str) -> str:
m = (model or "").lower()
for f in ("opus", "sonnet", "fable", "haiku"):
if f in m:
return f
return "other"
def fmt(n: float) -> str:
if n >= 1e9:
return f"{n / 1e9:.2f}B"
if n >= 1e6:
return f"{n / 1e6:.1f}M"
if n >= 1e3:
return f"{n / 1e3:.0f}k"
return str(int(n))
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--root", default=os.path.expanduser("~/.claude/projects"))
ap.add_argument("--since", default=None, help="YYYY-MM-DD (local)")
ap.add_argument("--weeks", default=None, help="comma-separated Monday dates to include")
ap.add_argument("--tz", type=float, default=None, help="UTC offset in hours; default = system local")
args = ap.parse_args()
if args.tz is None:
tz = dt.datetime.now().astimezone().tzinfo
else:
tz = dt.timezone(dt.timedelta(hours=args.tz))
since = dt.date.fromisoformat(args.since) if args.since else None
weeks = set(args.weeks.split(",")) if args.weeks else None
seen = set()
day = collections.defaultdict(collections.Counter)
fam_tok = collections.defaultdict(collections.Counter)
files = 0
for path in glob.glob(os.path.join(args.root, "**", "*.jsonl"), recursive=True):
files += 1
try:
fh = open(path, errors="replace")
except OSError:
continue
with fh:
for line in fh:
if '"usage"' not in line:
continue
try:
rec = json.loads(line)
except json.JSONDecodeError:
continue
msg = rec.get("message") or {}
usage = msg.get("usage") or {}
if not usage:
continue
rid = rec.get("requestId") or msg.get("id")
if not rid or rid in seen:
continue
seen.add(rid)
ts = str(rec.get("timestamp", ""))
try:
d = dt.datetime.fromisoformat(ts.replace("Z", "+00:00")).astimezone(tz)
except ValueError:
continue
dd = d.date()
if since and dd < since:
continue
wk = (dd - dt.timedelta(days=dd.weekday())).isoformat()
if weeks and wk not in weeks:
continue
c = day[dd.isoformat()]
c["n"] += 1
c["in"] += usage.get("input_tokens", 0) or 0
c["out"] += usage.get("output_tokens", 0) or 0
c["cW"] += usage.get("cache_creation_input_tokens", 0) or 0
c["cR"] += usage.get("cache_read_input_tokens", 0) or 0
inp_side = (usage.get("cache_read_input_tokens", 0) or 0) \
+ (usage.get("cache_creation_input_tokens", 0) or 0) \
+ (usage.get("input_tokens", 0) or 0)
fam_tok[dd.isoformat()][family(msg.get("model"))] += inp_side
if not day:
print("no usage records found", file=sys.stderr)
return 1
print(f"files scanned: {files} · unique requests: {len(seen)}\n")
# weekly
wkagg = collections.defaultdict(collections.Counter)
for k, c in day.items():
d = dt.date.fromisoformat(k)
wk = (d - dt.timedelta(days=d.weekday())).isoformat()
for f, v in c.items():
wkagg[wk][f] += v
print("| Week (Mon) | Requests | cache_read | cache_write | input | output |")
print("|------------|---------:|-----------:|------------:|------:|-------:|")
for wk in sorted(wkagg):
c = wkagg[wk]
print(f"| {wk} | {c['n']:,} | {fmt(c['cR'])} | {fmt(c['cW'])} | {fmt(c['in'])} | {fmt(c['out'])} |")
# daily
print("\n| Date | Requests | cache_read | cache_write | input | output | Opus % | Sonnet % | Fable % |")
print("|------|---------:|-----------:|------------:|------:|-------:|-------:|---------:|--------:|")
cur = None
for k in sorted(day):
d = dt.date.fromisoformat(k)
wk = (d - dt.timedelta(days=d.weekday())).isoformat()
if wk != cur:
cur = wk
print(f"| **Week of {d.strftime('%b %-d')}** | | | | | | | | |")
c = day[k]
t = sum(fam_tok[k].values()) or 1
ft = fam_tok[k]
print(f"| {d.strftime('%b %-d %a')} | {c['n']:,} | {fmt(c['cR'])} | {fmt(c['cW'])} | {fmt(c['in'])} | {fmt(c['out'])} "
f"| {100 * ft['opus'] / t:.0f} | {100 * ft['sonnet'] / t:.0f} | {100 * ft['fable'] / t:.0f} |")
return 0
if __name__ == "__main__":
sys.exit(main())
</details>