Skip to content

Add weekly quota tracking with rolling 7-day window#183

Open
jonathanpberger wants to merge 1 commit intoMaciek-roboblog:mainfrom
jonathanpberger:feature/weekly-quota-tracking
Open

Add weekly quota tracking with rolling 7-day window#183
jonathanpberger wants to merge 1 commit intoMaciek-roboblog:mainfrom
jonathanpberger:feature/weekly-quota-tracking

Conversation

@jonathanpberger
Copy link

@jonathanpberger jonathanpberger commented Jan 21, 2026

Summary

This PR adds support for tracking weekly token usage against Claude's rolling 7-day quota limits. This helps users understand their quota consumption and avoid hitting limits.

Features

  • WeeklyUsage model - New data model for tracking weekly quota (tokens used, cost, burn rate, per-model breakdown)
  • Weekly token/cost limits per plan - Estimated limits for Pro (~500M), Max5 (~1.2B), Max20 (~2.3B)
  • Rolling 7-day aggregation - Separate from calendar week view, matching Claude's actual quota system
  • New --view weekly CLI option - Shows weekly statistics with quota panel
  • Weekly quota panel - Progress bar, remaining tokens, daily burn rate, and weekly projections
  • Per-model token breakdown - See which models are consuming quota

Usage

# View weekly quota status
claude-monitor --view weekly --plan max20

# The output includes:
# - Rolling 7-day quota progress bar
# - Tokens used vs limit
# - Daily burn rate and weekly projection
# - Per-model breakdown (opus, sonnet, haiku)

Why Rolling 7-Day?

Claude's weekly quota uses a rolling window where usage from exactly 7 days ago "expires," making room for new capacity. This is different from calendar weeks - there's no single "reset day."

This implementation tracks the rolling 7-day window to accurately represent your quota consumption.

Test plan

  • Run claude-monitor --view weekly --plan max20 and verify quota panel displays
  • Verify weekly table shows ISO week groupings
  • Test with different plans (pro, max5, max20, custom)
  • Verify burn rate and projection calculations are reasonable

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added weekly view mode for monitoring rolling 7-day usage and quota tracking alongside existing daily and monthly views.
    • Weekly quota visualization displays token/cost metrics, daily burn rate, per-model breakdown, and remaining quota.
    • Includes projected weekly totals and warning indicators for quota management.

✏️ Tip: You can customize this high-level summary in your review settings.

This adds support for tracking weekly token usage against Claude's
rolling 7-day quota limits. The feature includes:

- WeeklyUsage model for quota tracking (tokens, cost, burn rate)
- Weekly token/cost limits per plan (Pro, Max5, Max20, Custom)
- Rolling 7-day aggregation separate from calendar week view
- New --view weekly CLI option for weekly statistics
- Weekly quota panel with progress bar and projections
- Per-model token breakdown for the rolling week

The weekly quota uses a rolling window (matching Claude's actual
quota system) rather than fixed calendar weeks. This helps users
understand their quota consumption and avoid hitting limits.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

The PR adds comprehensive weekly view support to Claude Monitor. A new WeeklyUsage dataclass tracks rolling 7-day usage metrics, weekly limits are configured in PlanConfig, settings accept "weekly" as a view mode, aggregation logic computes weekly and rolling-week statistics, and UI components render weekly tables and quota panels with burn-rate metrics.

Changes

Cohort / File(s) Summary
Core Data Model
src/claude_monitor/core/models.py
Introduces WeeklyUsage dataclass with week boundaries, token/cost usage and limits, elapsed days, and computed properties (tokens_remaining, usage_percentage, daily_burn_rate, projected_weekly_total) for quota monitoring and forecasting.
Plan Configuration
src/claude_monitor/core/plans.py
Adds weekly_token_limit and weekly_cost_limit fields to PlanConfig, populates weekly limits in PLAN_LIMITS for PRO/MAX5/MAX20/CUSTOM plans, exports helper functions get_weekly_token_limit() and get_weekly_cost_limit().
Settings & Validation
src/claude_monitor/core/settings.py
Extends Settings.view field type and validation to accept "weekly" alongside existing "realtime", "daily", "monthly", and "session" modes.
Data Aggregation
src/claude_monitor/data/aggregator.py
Adds aggregate_weekly() method for ISO week grouping, introduces aggregate_rolling_week() for 7-day rolling usage computation with per-model token breakdown, extends aggregate_from_blocks() and aggregate() to support "weekly" aggregation mode.
CLI Integration
src/claude_monitor/cli/main.py
Routes "weekly" view mode to new aggregation path, computes weekly rolling quota usage by loading entries and applying limits, passes weekly_usage data structure to table display controller.
UI & Display
src/claude_monitor/ui/table_views.py
Introduces create_weekly_table() for ISO week period rendering, adds create_weekly_quota_panel() with progress bar and burn-rate metrics, extends display_aggregated_view() signature with weekly_usage parameter to display weekly quota visualization before summary.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI (main.py)
    participant Settings as Settings
    participant Aggregator as Data Aggregator
    participant Plans as Plan Limits
    participant UIDisplay as UI Display (table_views)
    participant Console as Console Output

    CLI->>Settings: Parse view_mode="weekly"
    Settings-->>CLI: Validate "weekly" mode
    CLI->>Aggregator: Call aggregate_rolling_week(entries, token_limit, cost_limit)
    Aggregator->>Aggregator: Compute rolling 7-day window from UTC now
    Aggregator->>Aggregator: Aggregate tokens/costs per model
    Aggregator->>Aggregator: Calculate elapsed days & burn rate
    Aggregator-->>CLI: Return WeeklyUsage object
    CLI->>Plans: Fetch get_weekly_token_limit() & get_weekly_cost_limit()
    Plans-->>CLI: Return weekly limits for plan
    CLI->>UIDisplay: Call display_aggregated_view(weekly_usage=WeeklyUsage, view_mode="weekly")
    UIDisplay->>UIDisplay: create_weekly_quota_panel(weekly_usage)
    UIDisplay->>UIDisplay: create_weekly_table(weekly_data)
    UIDisplay->>Console: Print quota panel with progress & metrics
    UIDisplay->>Console: Print weekly table with per-model breakdown
    UIDisplay-->>CLI: Display complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Add: Daily and monthly token usage views #99: Modifies CLI, settings, aggregator, and table views to add time-based aggregated views (daily/monthly); this PR extends the same code pathways with weekly aggregation support.
  • Daily monthly token stats #107: Introduces the foundational time-based aggregation framework; this PR extends it with weekly and rolling-week computation capabilities using the established patterns.

Poem

🐰 Weekly wonders now take flight,
Seven-day rolls, metrics bright!
From plans to tables, data flows,
A rolling view that swiftly grows! 📊✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main feature: adding weekly quota tracking with a rolling 7-day window, which aligns with the PR's primary objective and substantial changes across multiple files.
Docstring Coverage ✅ Passed Docstring coverage is 92.59% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/claude_monitor/data/aggregator.py`:
- Around line 260-267: The weekly filter currently includes entries with
timestamp == week_start, which keeps items that should have expired; in the
aggregation logic that computes weekly_entries (using variables now, week_start,
week_end and iterating entries with e.timestamp), change the boundary check to
exclude the exact seven-days-prior moment (use a strict greater-than for the
start: e.timestamp > week_start and keep the end condition as <= week_end) so
entries exactly at week_start are not counted.

In `@src/claude_monitor/ui/table_views.py`:
- Around line 348-361: The "quota exceeded" branch is unreachable because the
code checks `pct >= 80` before `pct >= 100` (and `usage_percentage` is capped at
100), so change the conditional order or logic: check the `pct >= 100` case
first (or use `pct > 100` if you remove the cap) before the `pct >= 80` branch
so the "🚨 Weekly quota exceeded" Text in table_views.py is reachable; update
the conditionals involving the `pct` variable accordingly.
🧹 Nitpick comments (1)
src/claude_monitor/cli/main.py (1)

404-425: Avoid double-loading usage entries in weekly view.
Weekly mode loads entries in aggregator.aggregate() and again via load_usage_entries. Consider loading once and reusing for both aggregate_weekly and aggregate_rolling_week to reduce I/O and keep timestamp normalization consistent.

♻️ Possible refactor
-        aggregated_data = aggregator.aggregate()
+        if view_mode == "weekly":
+            from claude_monitor.data.reader import load_usage_entries
+
+            entries, _ = load_usage_entries(data_path=str(data_path))
+            if entries:
+                for entry in entries:
+                    if entry.timestamp.tzinfo is None:
+                        entry.timestamp = aggregator.timezone_handler.ensure_timezone(
+                            entry.timestamp
+                        )
+                aggregated_data = aggregator.aggregate_weekly(entries)
+            else:
+                aggregated_data = []
+        else:
+            aggregated_data = aggregator.aggregate()

Comment on lines +260 to +267
now = datetime.now(timezone.utc)
week_start = now - timedelta(days=7)
week_end = now

# Filter entries to last 7 days
weekly_entries = [
e for e in entries if week_start <= e.timestamp <= week_end
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Rolling‑window boundary includes entries that should have expired.
The PR objective says usage from exactly seven days prior should expire, but the filter includes timestamp == week_start. This will overcount at the cutoff.

🔧 Proposed fix
-        weekly_entries = [
-            e for e in entries if week_start <= e.timestamp <= week_end
-        ]
+        weekly_entries = [
+            e for e in entries if week_start < e.timestamp <= week_end
+        ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
now = datetime.now(timezone.utc)
week_start = now - timedelta(days=7)
week_end = now
# Filter entries to last 7 days
weekly_entries = [
e for e in entries if week_start <= e.timestamp <= week_end
]
now = datetime.now(timezone.utc)
week_start = now - timedelta(days=7)
week_end = now
# Filter entries to last 7 days
weekly_entries = [
e for e in entries if week_start < e.timestamp <= week_end
]
🤖 Prompt for AI Agents
In `@src/claude_monitor/data/aggregator.py` around lines 260 - 267, The weekly
filter currently includes entries with timestamp == week_start, which keeps
items that should have expired; in the aggregation logic that computes
weekly_entries (using variables now, week_start, week_end and iterating entries
with e.timestamp), change the boundary check to exclude the exact
seven-days-prior moment (use a strict greater-than for the start: e.timestamp >
week_start and keep the end condition as <= week_end) so entries exactly at
week_start are not counted.

Comment on lines +348 to +361
if pct >= 80:
lines.append(
Text(
"⚠️ Approaching weekly limit! Consider using Haiku for lighter tasks.",
style="yellow",
)
)
elif pct >= 100:
lines.append(
Text(
"🚨 Weekly quota exceeded - expect rate limiting",
style="red bold",
)
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

“Quota exceeded” warning never triggers.
usage_percentage is capped at 100 and the pct >= 80 branch runs first, so the exceeded message is unreachable even when over the limit.

🔧 Proposed fix
-        if pct >= 80:
-            lines.append(
-                Text(
-                    "⚠️  Approaching weekly limit! Consider using Haiku for lighter tasks.",
-                    style="yellow",
-                )
-            )
-        elif pct >= 100:
-            lines.append(
-                Text(
-                    "🚨 Weekly quota exceeded - expect rate limiting",
-                    style="red bold",
-                )
-            )
+        if weekly_usage.token_limit > 0 and weekly_usage.tokens_used >= weekly_usage.token_limit:
+            lines.append(
+                Text(
+                    "🚨 Weekly quota exceeded - expect rate limiting",
+                    style="red bold",
+                )
+            )
+        elif pct >= 80:
+            lines.append(
+                Text(
+                    "⚠️  Approaching weekly limit! Consider using Haiku for lighter tasks.",
+                    style="yellow",
+                )
+            )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if pct >= 80:
lines.append(
Text(
"⚠️ Approaching weekly limit! Consider using Haiku for lighter tasks.",
style="yellow",
)
)
elif pct >= 100:
lines.append(
Text(
"🚨 Weekly quota exceeded - expect rate limiting",
style="red bold",
)
)
if weekly_usage.token_limit > 0 and weekly_usage.tokens_used >= weekly_usage.token_limit:
lines.append(
Text(
"🚨 Weekly quota exceeded - expect rate limiting",
style="red bold",
)
)
elif pct >= 80:
lines.append(
Text(
"⚠️ Approaching weekly limit! Consider using Haiku for lighter tasks.",
style="yellow",
)
)
🤖 Prompt for AI Agents
In `@src/claude_monitor/ui/table_views.py` around lines 348 - 361, The "quota
exceeded" branch is unreachable because the code checks `pct >= 80` before `pct
>= 100` (and `usage_percentage` is capped at 100), so change the conditional
order or logic: check the `pct >= 100` case first (or use `pct > 100` if you
remove the cap) before the `pct >= 80` branch so the "🚨 Weekly quota exceeded"
Text in table_views.py is reachable; update the conditionals involving the `pct`
variable accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant