Skip to content

fix(sidepanel): fix TODAY rollup lag — daily summary now updates on every turn#50

Merged
DevanshuNEU merged 4 commits into
OpenCodeIntel:mainfrom
DevanshuNEU:feat/lco-18-today-rollup-lag
Apr 22, 2026
Merged

fix(sidepanel): fix TODAY rollup lag — daily summary now updates on every turn#50
DevanshuNEU merged 4 commits into
OpenCodeIntel:mainfrom
DevanshuNEU:feat/lco-18-today-rollup-lag

Conversation

@DevanshuNEU

@DevanshuNEU DevanshuNEU commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Summary

The TODAY section in the side panel was lagging the active conversation by up to 30 minutes. Turn writes land under conv: keys; the storage listener only called loadToday() on daily: key changes, which only happened when the background alarm fired. This PR fixes that by recomputing the daily summary immediately on each turn, so TODAY and ACTIVE CONVERSATION always agree.

Type of Change

  • feat — New feature
  • fix — Bug fix
  • refactor — Code restructure, no behavior change
  • test — Tests only
  • chore — Build, CI, tooling, dependencies
  • docs — Documentation only

What Was Changed

  • entrypoints/sidepanel/hooks/useDashboardData.ts — In onStorageChanged, when hasConvChange fires, also call computeDailySummary(orgId, todayDateString()) fire-and-forget. This writes daily:, which the existing hasDailyChange arm catches and feeds to loadToday(). The chain completes in one storage round-trip instead of waiting for the alarm.

  • tests/unit/today-rollup.test.ts (new) — Four tests encoding the GET-18 product invariants: multi-conv day sums correctly (not Math.max), a new turn is reflected immediately, no double-counting, and null cost propagates as null rather than 0.

How to Test

  1. Load the unpacked extension from .output/chrome-mv3/ in Chrome
  2. Open claude.ai and start a conversation
  3. Open the Saar side panel
  4. Note TODAY turn and token counts
  5. Send a message and wait for the response to complete
  6. Without refreshing, confirm TODAY now matches ACTIVE CONVERSATION to the exact turn and token count
  7. Confirm the match happens within seconds, not after a 30-min wait

Checklist

  • Tests pass locally (bun run test)
  • TypeScript compiles clean (bun run compile)
  • Extension builds without errors (bun run build)
  • No secrets, hardcoded URLs, or sensitive tokens exposed
  • Comments are professional and clear — no emojis, no AI-generated filler
  • Commit messages follow conventional commits (feat:, fix:, refactor:, test:, chore:)

Related Issues

Closes GET-18

Notes for Reviewer

Path A (Math.max(today.totalTurns, activeConv.turnCount)) was considered and rejected: it breaks for multi-conversation days where the active conv has fewer turns than the daily total. Path B (recompute on turn) is correct in all cases because computeDailySummary always scans all conversations.

Known trade-off: computeDailySummary reads all org conv records on each turn, not just today's (the conv index stores IDs only, no date metadata). At 90-day retention scale this is a batch read of ~100-300 records, fire-and-forget, and imperceptible in practice. If power users hit this limit, the fix is a date-indexed secondary index in conversation-store.ts.

Hook integration test gap: the storage event wiring in useDashboardData.ts cannot be unit-tested without renderHook + Chrome API mocks (not in this repo's test setup). The four tests in today-rollup.test.ts prove the data contract at the storage layer. Manual verification covers the hook wiring.

Summary by CodeRabbit

  • Bug Fixes

    • Daily summary now refreshes immediately when conversations change, ensuring real-time accuracy of aggregated statistics.
  • Tests

    • Added comprehensive unit tests validating daily rollup calculations, including conversation counts, token usage, and cost estimations.

Turn writes land under conv: keys and did not trigger loadToday().
TODAY lagged the active conversation by up to the background alarm
interval (~30 min). When hasConvChange fires, now also call
computeDailySummary so the daily: key is fresh. The existing
hasDailyChange arm picks it up and calls loadToday() immediately.
Three tests encode the product-level invariants GET-18 depends on:
daily summary totalTurns >= any single conv contribution, fresh
computeDailySummary after a new turn reflects it immediately, and
no double-counting across two conversations in the same day.
Hook comment now names the exact trade-offs (dual writers, full-scan
cost). Test file rewritten: WHY comments per test, hook integration gap
documented, null-cost test added for the unknown-model case.
@vercel

vercel Bot commented Apr 22, 2026

Copy link
Copy Markdown

@DevanshuNEU is attempting to deploy a commit to the Dev's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Apr 22, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@DevanshuNEU has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 25 minutes and 19 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 25 minutes and 19 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4c697980-7132-4077-aa54-313a0750cde2

📥 Commits

Reviewing files that changed from the base of the PR and between 11c2f9e and a9a55bf.

📒 Files selected for processing (1)
  • entrypoints/sidepanel/hooks/useDashboardData.ts
📝 Walkthrough

Walkthrough

This pull request enhances the dashboard hook to automatically recompute daily summaries when conversations change, and introduces a comprehensive test suite validating daily rollup aggregation accuracy, cost handling, and freshness guarantees.

Changes

Cohort / File(s) Summary
Dashboard Hook Enhancement
entrypoints/sidepanel/hooks/useDashboardData.ts
Added eager trigger to computeDailySummary within the storage change listener's conversation change branch, ensuring daily totals recompute immediately when conversations are modified.
Daily Rollup Test Suite
tests/unit/today-rollup.test.ts
New test file validating computeDailySummary behavior: correct aggregation across multiple conversations, no double-counting, freshness after new turns, and proper null-handling for unknown model costs.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

Poem

🐰 Conversations dance and summaries gleam,
Daily totals flow like a Spring-time stream,
Tests now verify each hop and each turn,
No double-count tricks, just lessons we learn! 🌱✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main fix: resolving the TODAY rollup lag by making daily summary update on every turn, matching the primary behavior change in the changeset.
Description check ✅ Passed The description is comprehensive and well-structured, covering all required sections: a clear summary explaining the root cause and fix, explicit type of change selection, detailed explanation of both file changes, thorough testing instructions, completed checklist, related issue link, and notes addressing trade-offs and testing gaps.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
entrypoints/sidepanel/hooks/useDashboardData.ts (1)

314-328: ⚠️ Potential issue | 🟠 Major

Batch the daily recompute before writing storage.

This can start multiple overlapping full-org scans and daily: writes for one burst of turn/index changes. If an earlier scan finishes after a later one, it can overwrite the fresher summary; the empty catch also hides the failure path that would keep TODAY stale. Debounce the recompute for 200ms and log failures.

Proposed fix
     // Ref mirror of isClaudeTab so event listeners (closures) can read the current
     // value without capturing a stale boolean from the render cycle.
     const isClaudeTabRef = useRef(false);
+    const dailySummaryRecomputeTimerRef = useRef<ReturnType<typeof window.setTimeout> | null>(null);

@@
                     // Trade-off: reads all org conv records on each turn. Cost grows with
                     // history depth but is fire-and-forget and fine at 90-day scale. [GET-18]
                     const orgId = orgIdRef.current;
                     if (orgId) {
-                        void computeDailySummary(orgId, todayDateString()).catch(() => {});
+                        if (dailySummaryRecomputeTimerRef.current !== null) {
+                            window.clearTimeout(dailySummaryRecomputeTimerRef.current);
+                        }
+                        dailySummaryRecomputeTimerRef.current = window.setTimeout(() => {
+                            dailySummaryRecomputeTimerRef.current = null;
+                            if (orgIdRef.current !== orgId) return;
+                            void computeDailySummary(orgId, todayDateString()).catch((err) => {
+                                console.error('[Saar] Failed to recompute daily summary:', err);
+                            });
+                        }, 200);
                     }
                 }
@@
         return () => {
+            if (dailySummaryRecomputeTimerRef.current !== null) {
+                window.clearTimeout(dailySummaryRecomputeTimerRef.current);
+            }
             chrome.storage.onChanged.removeListener(onStorageChanged);
             chrome.tabs.onActivated.removeListener(onTabActivated);
             chrome.tabs.onUpdated.removeListener(onTabUpdated);

As per coding guidelines, entrypoints/**/*.ts: Implement 200ms batching on postMessage and storage writes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@entrypoints/sidepanel/hooks/useDashboardData.ts` around lines 314 - 328, The
code triggers computeDailySummary immediately on every conversation change,
which can start overlapping org scans and storage writes; debounce these
recomputes for 200ms so only the last call in a burst runs: add a persistent
timer/ref (e.g., recomputeTimerRef) near orgIdRef/tabIdRef, clear any existing
timer before scheduling a new setTimeout that calls computeDailySummary(orgId,
todayDateString()), and on the promise catch log the error via
processLogger/console (do not swallow it with an empty catch); ensure the timer
is cleared when appropriate (cleanup) to prevent leaks and keep the existing
loadConversations/loadActiveConversation behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/unit/today-rollup.test.ts`:
- Around line 56-71: The test uses Date.now() as completedAt for recordTurn but
calls computeDailySummary(todayDateString()) separately, which can flake across
midnight; capture the date string from the same now (e.g. compute a summaryDate
derived from now) and pass that into computeDailySummary so the summary date
matches the turns' completedAt timestamps; update all similar blocks that call
recordTurn(..., completedAt: now) then computeDailySummary(todayDateString()) to
instead derive and use the date from now.

---

Outside diff comments:
In `@entrypoints/sidepanel/hooks/useDashboardData.ts`:
- Around line 314-328: The code triggers computeDailySummary immediately on
every conversation change, which can start overlapping org scans and storage
writes; debounce these recomputes for 200ms so only the last call in a burst
runs: add a persistent timer/ref (e.g., recomputeTimerRef) near
orgIdRef/tabIdRef, clear any existing timer before scheduling a new setTimeout
that calls computeDailySummary(orgId, todayDateString()), and on the promise
catch log the error via processLogger/console (do not swallow it with an empty
catch); ensure the timer is cleared when appropriate (cleanup) to prevent leaks
and keep the existing loadConversations/loadActiveConversation behavior
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 13d29690-dd91-4b80-9048-85c721ccdd32

📥 Commits

Reviewing files that changed from the base of the PR and between aebd5ec and 11c2f9e.

📒 Files selected for processing (2)
  • entrypoints/sidepanel/hooks/useDashboardData.ts
  • tests/unit/today-rollup.test.ts

Comment thread tests/unit/today-rollup.test.ts
…[GET-18]

Silent catch made TODAY stay stale with zero indication when the
recompute failed. Consistent with every other error path in this hook.
@DevanshuNEU DevanshuNEU merged commit 8e527db into OpenCodeIntel:main Apr 22, 2026
5 of 6 checks passed
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