Skip to main content
← Back to list
01Issue
FeatureOpenSwamp Club
AssigneesNone

Score daily sign-in events with streak multiplier

Opened by stack72 · 4/9/2026· GitHub #230

Summary

Track daily sign-ins as scoring events with an escalating streak multiplier — consecutive daily logins earn progressively more points.

Motivation

Rewarding daily engagement encourages operatives to stay active in the swamp. A streak multiplier makes consistent daily presence increasingly valuable, creating a compelling reason to log in regularly. This is a retention mechanic that also feeds into leaderboard competition.

Design sketch

  • Event source: Auth middleware or session creation emits a daily sign-in event (at most once per calendar day per user)
  • Event shape: { event: "daily_sign_in", distinct_id: "...", properties: { username, date } }
  • Streak tracking: Track consecutive sign-in days per user. Each day signed in increments the streak; a missed day resets it.
  • Scoring: Base points × streak multiplier. The multiplier grows with consecutive days (e.g., day 1 = 1×, day 2 = 1.5×, day 7 = 3×, day 30 = 5×). Exact curve TBD.
  • Deduplication: One daily_sign_in per username per calendar day, idempotent on reprocessing
  • Storage: Streak state (current streak length, last sign-in date) stored per user — either in username_metrics or a dedicated collection

Open questions

  • Multiplier curve — linear, logarithmic, tiered thresholds?
  • Should there be a multiplier cap to prevent runaway scores?
  • Grace period for missed days (e.g., 1 day grace before streak resets)?
  • Should streak length be visible on the user's profile?
  • Relationship to activeDates already tracked in telemetry metrics — can we derive streaks from existing data?
  • Relationship to #227 (award events) and #229 (sign-up scoring)
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED

Open

4/9/2026, 4:43:30 PM

No activity in this phase yet.

03Sludge Pulse

stack72 commented 4/9/2026, 4:43:31 PM

Originally posted by @johnrwatson on 2026-03-12T23:34:22Z:

Implementation Plan: Score daily sign-in events with streak multiplier

Context

The app tracks CLI activity via telemetry and scores it in UserScore. There's no tracking of web sign-ins as a scoring signal. This feature adds a daily sign-in contribution to UserScore with a streak multiplier that rewards consecutive daily visits, creating a retention mechanic.

Key distinction: the existing computeStreak() in activity-score.ts operates on CLI telemetry activeDates. This feature tracks web presence separately via a new sign_in_dates field in username_metrics, flowing through the existing telemetry pipeline.

Design Decisions

  1. Ingest via telemetry pipeline — middleware emits daily_sign_in events through track(), flowing through the same telemetry service → StatsConsumer → event queue → processScoreBatch pipeline as CLI events.
  2. New sign_in_dates field in username_metrics — StatsConsumer tracks sign-in dates separately from active_dates (which tracks all event types). This gives the scoring layer a clean signal for web-only engagement.
  3. Middleware trigger with in-memory dedup — "daily sign-in" means first authenticated visit per calendar day. In-memory cache (username → date) prevents redundant track() calls; $addToSet at the DB level provides idempotency as a safety net.
  4. No grace period — miss a day, lose the streak. Keeps the daily login incentive sharp.
  5. No aggregate class — per DDD Design Gate, pure functions + record type. Streak is computed from the signInDates array (same pattern as computeStreak() from activeDates).
  6. Contribution key "community:daily-sign-in" — overwrites each time (not cumulative), so points reflect current streak level.
  7. "engagement" category — distinct from CLI "activity" category.
  8. Profile visibility deferred — streak display on user profiles will be a follow-up issue.

Multiplier Curve (tiered)

Streak Days Multiplier Points (10 base)
1 10
2–6 1.5× 15
7–13 20
14–29 30
30+ 5× (cap) 50

Data Flow

Middleware (track "daily_sign_in")
  → Telemetry service /ingest
  → StatsConsumer: $addToSet sign_in_dates on username_metrics
  → Queue producer: snapshot includes signInDates
  → Main app event queue watcher
  → processScoreBatch → refreshSignInScore
  → UserScore.contribute("community:daily-sign-in", ...)

Files to Create

1. lib/domain/scoring/sign-in-score.ts — Domain (pure functions)

  • computeSignInStreak(signInDates: string[], now: Date): number — consecutive sign-in days ending today (no grace period, unlike computeStreak in activity-score.ts)
  • computeSignInPoints(streak: number): number — tiered multiplier curve above

2. lib/app/refresh-sign-in-score.ts — App service

Follows the exact pattern of refresh-activity-score.ts:

  • Computes streak via computeSignInStreak(signInDates, new Date())
  • Computes points via computeSignInPoints(streak)
  • Updates UserScore with withRetry pattern: score.contribute("community:daily-sign-in", "community", "engagement", points)

3. tests/domain/sign-in-score_test.ts — Domain unit tests

  • computeSignInStreak: no dates (0), single date today (1), consecutive days (correct count), gap resets (1), dates not including today (0 — no grace period)
  • computeSignInPoints: boundary values at each tier (1, 2, 6, 7, 13, 14, 29, 30, 100)

Files to Modify

4. services/telemetry/lib/consumers/stats.ts — StatsConsumer + UserMetrics type

  • Add sign_in_dates?: string[] to the UserMetrics interface
  • In the per-username metrics update block, add conditional sign_in_dates tracking for daily_sign_in events via $addToSet

5. shared/queue-schema.ts — Queue contract

Add signInDates?: string[] to the metrics snapshot type (optional for backward compat).

6. services/telemetry/lib/queue-producer.ts — Snapshot builder

Include sign_in_dates from username_metrics doc in the snapshot.

7. lib/app/refresh-user-score.ts — Score refresh orchestrator

Add call to refreshSignInScore() alongside existing activity and extension score refreshes.

8. lib/app/process-score-batch.ts — Batch processor

Pass signInDates from snapshot through to score refresh.

9. routes/_middleware.ts — Event emission

Add after session resolution, before verification gate:

  • Module-level in-memory cache: Map<string, string> (username → last recorded UTC date, max 10k entries)
  • Fire-and-forget track("daily_sign_in", userId, { username }) — same pattern as existing track("page_view", ...)

🤖 Generated with Claude Code

Sign in to post a ripple.