[Insights] Achievement progress not calculated for partial completion #296

Closed
opened 2026-01-25 16:55:05 +00:00 by jack · 2 comments
Owner

Problem

The "In Progress" tab for achievements shows no items because the progress is never calculated for partially completed achievements.

Root Cause

In packages/backend/src/services/insights-service.ts, the checkAchievements() method only updates progress when the threshold is already reached:

// Line 259-261
if (totalObservations >= 10) {  // ❌ Progress only updated if threshold reached
  const progress = await this.updateProgressIfNeeded('observations-10', totalObservations / 10);

This means:

  • User has 5 observations
  • observations-10 requires 10 observations
  • Condition 5 >= 10 is false
  • Progress is never updated → stays at 0
  • Achievement doesn't appear in "In Progress" tab

Expected Behavior

Progress should be calculated for all achievements with thresholds, regardless of completion:

// ✅ Always calculate progress for threshold-based achievements
const progress = totalObservations / 10;
await this.updateProgressIfNeeded('observations-10', progress);

User with 5 observations should see:

  • observations-10: 50% progress (5/10)
  • observations-100: 5% progress (5/100)
  • etc.

Affected Achievements

All threshold-based achievements are affected:

  • Observation milestones (10, 100, 500, 1000, 5000, 10000)
  • Session milestones (10, 50, 100, 500, 1000)
  • Token milestones (100k, 1m, 10m, 50m, 100m, 500m)
  • Bug fix milestones (10, 50, 100, 500)
  • Discovery milestones (10, 50, 100, 500)
  • Streak milestones (3, 7, 14, 30, 60, 100, 365)
  • Project milestones (3, 10, 25, 50)
  • And more...

Solution

Refactor checkAchievements() to always calculate and store progress for all threshold-based achievements, not just when the threshold is met.

  • Issue #282 - Achievement filter tabs (closed, but progress feature incomplete)
## Problem The "In Progress" tab for achievements shows no items because the progress is never calculated for partially completed achievements. ## Root Cause In `packages/backend/src/services/insights-service.ts`, the `checkAchievements()` method only updates progress when the threshold is **already reached**: ```typescript // Line 259-261 if (totalObservations >= 10) { // ❌ Progress only updated if threshold reached const progress = await this.updateProgressIfNeeded('observations-10', totalObservations / 10); ``` This means: - User has 5 observations - `observations-10` requires 10 observations - Condition `5 >= 10` is **false** - Progress is **never updated** → stays at 0 - Achievement doesn't appear in "In Progress" tab ## Expected Behavior Progress should be calculated for **all** achievements with thresholds, regardless of completion: ```typescript // ✅ Always calculate progress for threshold-based achievements const progress = totalObservations / 10; await this.updateProgressIfNeeded('observations-10', progress); ``` User with 5 observations should see: - `observations-10`: 50% progress (5/10) - `observations-100`: 5% progress (5/100) - etc. ## Affected Achievements All threshold-based achievements are affected: - Observation milestones (10, 100, 500, 1000, 5000, 10000) - Session milestones (10, 50, 100, 500, 1000) - Token milestones (100k, 1m, 10m, 50m, 100m, 500m) - Bug fix milestones (10, 50, 100, 500) - Discovery milestones (10, 50, 100, 500) - Streak milestones (3, 7, 14, 30, 60, 100, 365) - Project milestones (3, 10, 25, 50) - And more... ## Solution Refactor `checkAchievements()` to always calculate and store progress for all threshold-based achievements, not just when the threshold is met. ## Related - Issue #282 - Achievement filter tabs (closed, but progress feature incomplete)
Author
Owner

Additional Problem: Manual Trigger Required

Currently the user must click "Check Achievements" to trigger any progress calculation. This should happen automatically when relevant events occur:

Event Achievements to Update
New observation created Observation milestones, type-specific (bugfix, discovery, etc.)
Session completed Session milestones
New project detected Project milestones
Daily first activity Streak achievements
Technology detected Polyglot achievements

Proposed Solution

  1. Event-driven updates: Hook into existing SSE events or create achievement-specific triggers
  2. Batch processing: Update relevant achievements after each session ends (via session-end hook)
  3. Lazy calculation: Calculate progress on-demand in getAchievements() instead of storing it

Option 3 might be simplest - calculate current progress from live data instead of relying on stored values:

async getAchievements(): Promise<AchievementProgress[]> {
  const stats = await this.calculateCurrentStats();
  
  return ACHIEVEMENTS.map(def => {
    const progress = this.calculateProgress(def, stats);
    return {
      definition: def,
      progress,
      unlocked: progress >= 1,
      // ...
    };
  });
}

This removes the need for:

  • Manual "Check Achievements" button
  • Stored progress values in DB
  • Event-based triggers
## Additional Problem: Manual Trigger Required Currently the user must click "Check Achievements" to trigger any progress calculation. This should happen **automatically** when relevant events occur: | Event | Achievements to Update | |-------|----------------------| | New observation created | Observation milestones, type-specific (bugfix, discovery, etc.) | | Session completed | Session milestones | | New project detected | Project milestones | | Daily first activity | Streak achievements | | Technology detected | Polyglot achievements | ## Proposed Solution 1. **Event-driven updates**: Hook into existing SSE events or create achievement-specific triggers 2. **Batch processing**: Update relevant achievements after each session ends (via `session-end` hook) 3. **Lazy calculation**: Calculate progress on-demand in `getAchievements()` instead of storing it Option 3 might be simplest - calculate current progress from live data instead of relying on stored values: ```typescript async getAchievements(): Promise<AchievementProgress[]> { const stats = await this.calculateCurrentStats(); return ACHIEVEMENTS.map(def => { const progress = this.calculateProgress(def, stats); return { definition: def, progress, unlocked: progress >= 1, // ... }; }); } ``` This removes the need for: - Manual "Check Achievements" button - Stored progress values in DB - Event-based triggers
Author
Owner

Fixed in commit 665c3ca.

The checkAchievements() method now always calculates progress for all threshold-based achievements, regardless of whether the threshold is met.

Changes:

  • Removed conditional threshold checks before updating progress
  • Added trackProgress() helper that always calls updateProgressIfNeeded(achievementId, current/threshold)
  • Added coverage for all 50+ threshold-based achievements:
    • Observation milestones (10, 100, 500, 1K, 5K, 10K)
    • Session milestones (10, 50, 100, 500, 1K)
    • Token milestones (100K, 1M, 10M, 50M, 100M, 500M)
    • Decision milestones (10, 50, 100)
    • Bug fix milestones (10, 50, 100, 500)
    • Discovery milestones (10, 50, 100, 500)
    • Language milestones (3, 5, 10)
    • Project milestones (3, 10, 25, 50)
    • Streak milestones (3, 7, 14, 30, 60, 100, 365)
  • Query observation counts by type (bugfix, decision, discovery) instead of just total
  • Fixed duplicate bugfixCount/bugFixCount parameter

Now users will see proper progress percentages in the "In Progress" tab.

Fixed in commit 665c3ca. The `checkAchievements()` method now always calculates progress for all threshold-based achievements, regardless of whether the threshold is met. **Changes:** - Removed conditional threshold checks before updating progress - Added `trackProgress()` helper that always calls `updateProgressIfNeeded(achievementId, current/threshold)` - Added coverage for all 50+ threshold-based achievements: - Observation milestones (10, 100, 500, 1K, 5K, 10K) - Session milestones (10, 50, 100, 500, 1K) - Token milestones (100K, 1M, 10M, 50M, 100M, 500M) - Decision milestones (10, 50, 100) - Bug fix milestones (10, 50, 100, 500) - Discovery milestones (10, 50, 100, 500) - Language milestones (3, 5, 10) - Project milestones (3, 10, 25, 50) - Streak milestones (3, 7, 14, 30, 60, 100, 365) - Query observation counts by type (`bugfix`, `decision`, `discovery`) instead of just total - Fixed duplicate `bugfixCount`/`bugFixCount` parameter Now users will see proper progress percentages in the "In Progress" tab.
jack closed this issue 2026-01-25 17:07:13 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
customable/claude-mem#296
No description provided.