Skip to content

[Data-integrity] daily streak processor preserves streak when user misses exactly one day #886

Description

@vedant7007

Description

while reading through the daily streak processor i noticed the daysDiff === 1 branch is a no-op labeled "Contributed yesterday, hasn't contributed today yet" — but at a midnight cron daysDiff === 1 actually means the user missed all of yesterday. the streak stays intact and can even keep growing while the user contributes only every other day.

The Problem

src/inngest/karma.ts lines 215-297. cron trigger is "0 0 * * *" (daily midnight), so now ≈ todayMidnight. inside the loop:

const todayMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
const activeTimestamp = new Date(user.lastContributionAt).getTime();
const daysDiff = Math.floor((todayMidnight - activeTimestamp) / oneDayMs);

if (daysDiff === 0) {
  // award streak_bonus, increment currentStreak
} else if (daysDiff >= 2) {
  // reset currentStreak to 0
} else if (daysDiff === 1) {
  // Valid trailing active window path (Contributed yesterday, hasn't contributed today yet)
  skippedNoOp++;
}

trace: user contributes Monday 12:00 PM. Tuesday 00:00 cron: daysDiff = floor(12h / 24h) = 0 -> awards streak, streak=6. user does nothing on Tuesday. Wednesday 00:00 cron: daysDiff = floor(36h / 24h) = 1 -> skippedNoOp -> streak stays 6. Wednesday 08:00 the user contributes. Thursday 00:00 cron: daysDiff = floor(16h / 24h) = 0 -> awards streak, streak=7.

user grew streak from 5 to 7 while contributing only Monday and Wednesday — missing all of Tuesday. the comment on the daysDiff === 1 branch is inverted: contributing yesterday actually yields daysDiff === 0 at a midnight cron because yesterday's timestamp (e.g. Tue 08:00) is within the last 24h relative to today-midnight.

concrete failure: this corrupts the currentStreak field that gates streak_days_* badges in checkAndAwardBadges and leaderboards. every "streak" milestone can be earned while contributing every other day. only 3+ full-day gaps correctly reset the counter.

Proposed Fix

  • treat any daysDiff >= 1 as a streak break (or equivalently, daysDiff !== 0). since the midnight cron guarantees daysDiff === 0 is the only "contributed within the last full day" case, collapse the two else-if branches into one reset branch
  • update the comment to reflect actual semantics
  • mirror the same-day ledger idempotency guard already used for the karmaTransactionsTable insert so the reset write is applied at most once per gap

I would like to work on this under GSSoC 26. please assign!

for labels i think gssoc + type:bug + level:advanced fits here.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions