Skip to main content

MoTrends

"The Pattern Finder" — "I see your trajectory"

Status: ⚠️ Partial (included in progression API)

MoTrends analyzes long-term training data to identify trends in strength, volume, and performance.


Purpose

  • Track strength progression over time
  • Identify exercises improving or plateauing
  • Calculate volume trends
  • Detect RPE creep (workouts getting harder)

Current Implementation

Trend analysis is embedded in the /api/progression endpoint.

Code Location

/app/api/progression/route.ts

Trend Types

Strength Trend

interface StrengthTrend {
exerciseId: string;
exerciseName: string;

// Current vs historical
currentWeight: number;
startWeight: number;
weightChange: number;
percentChange: number;

// Estimated 1RM
current1RM: number;
peak1RM: number;

// Trend direction
trend: 'improving' | 'plateaued' | 'declining';
sessionsAtCurrentWeight: number;
}

Volume Trend

interface VolumeTrend {
period: '7d' | '14d' | '30d';

// Weekly averages
currentWeeklyVolume: number;
previousWeeklyVolume: number;
volumeChange: number;

// By muscle group
volumeByMuscle: {
muscle: string;
volume: number;
change: number;
}[];
}

RPE Trend

interface RPETrend {
// Average RPE over time
currentAvgRPE: number;
previousAvgRPE: number;
rpeChange: number;

// RPE creep detection
isCreeping: boolean; // RPE trending up without weight increase
creepRate: number; // RPE increase per week
}

Calculations

Plateau Detection

function isPlateaued(history: SetHistory[]): boolean {
const recentSessions = history.slice(0, 4);

// Same weight for 4+ sessions
const weights = recentSessions.map(s => s.weight);
const allSameWeight = new Set(weights).size === 1;

// Not hitting target reps
const avgReps = average(recentSessions.map(s => s.reps));
const targetNotMet = avgReps < targetReps;

return allSameWeight && targetNotMet;
}

RPE Creep Detection

function detectRPECreep(history: SetHistory[]): boolean {
const recent = history.slice(0, 5);
const older = history.slice(5, 10);

const recentAvgRPE = average(recent.map(s => s.rpe));
const olderAvgRPE = average(older.map(s => s.rpe));

// RPE increased by 0.5+ without weight increase
const rpeIncreased = recentAvgRPE - olderAvgRPE >= 0.5;
const weightSame = recent[0].weight === older[0].weight;

return rpeIncreased && weightSame;
}

API Response

Part of /api/progression:

{
"exercises": {
"readyToProgress": [...],
"plateaued": [
{
"exerciseId": "uuid",
"name": "Barbell Squat",
"stuckAt": 225,
"sessionCount": 5,
"suggestions": [
"Try 5x5 instead of 4x8",
"Add pause reps",
"Consider a deload"
]
}
]
}
}

Integration Points

Receives from:

  • MoStrength (set history)
  • MoSession (session data)

Provides to:

  • MoProgress (plateau detection)
  • MoFatigue (RPE trends)
  • Progress page (charts)

Future Enhancements

  • Dedicated trends API endpoint
  • Muscle group volume balance
  • Strength standards comparison
  • Progress velocity predictions