Wiki › Implementation › Program Progression
How the app decides which workout is "today" and how week numbers advance.
The app does not map workouts to calendar days. Instead, it walks through the program sequentially based on how many sessions you've completed.
flowchart LR
S0["Session 0<br/>Routine A"] --> S1["Session 1<br/>Routine B"]
S1 --> S2["Session 2<br/>Routine C"]
S2 --> S3["Session 3<br/>Routine A (wk 2)"]
S3 --> Sdot["..."]
Sdot --> S36["Session 36<br/>wraps to index 0"]
classDef step fill:#1f6f6f,stroke:#0f3a3a,color:#ffffff;
class S0,S1,S2,S3,Sdot,S36 step;
todaysWorkout = allWorkouts[completedSessionCount % allWorkouts.length]
allWorkouts= every workout across all 12 weeks (36 total for Strength Foundation)completedSessionCount= finished sessions for the active program- After session 36, it wraps back to workout index 0
This means you can train on any day of the week — the app always gives you the next workout in sequence.
currentWeekNumber = floor(completedSessionCount / daysPerWeek) + 1;
// capped at durationWeeksFor a 3-day/week program: sessions 0–2 → week 1, sessions 3–5 → week 2, etc.
currentWorkoutLetter = char(65 + (completedSessionCount % daysPerWeek));
// 0→A, 1→B, 2→CPosition within the current week cycle, independent of which workout template is next.
flowchart TD
Open[Open Today page] --> Done{Session for<br/>today + active program?}
Done -->|yes| Complete[Show 'complete for today']
Done -->|no| Active{sessionStore.isActive?}
Active -->|yes| Overlay[Session overlay open]
Active -->|no| Start[Show routine card + Start button]
classDef start fill:#3b3f8c,stroke:#23264f,color:#ffffff;
classDef decision fill:#9a6a1f,stroke:#5c3f12,color:#ffffff;
classDef done fill:#2f7d4f,stroke:#1a472d,color:#ffffff;
class Open start;
class Done,Active decision;
class Complete,Overlay,Start done;
If a Session exists for today's date + active program, the Today page shows a "complete for today" state instead of the start button. You cannot log two sessions on the same day.
The Program page shows week 1 workout templates (canonical A/B/C cards), not the specific week's instance.
Status badges are computed by finding each template's position in the flattened allWorkouts array (all 36 workouts) and comparing against todaysWorkout's index:
| Badge | Condition |
|---|---|
| Today | Template ID matches todaysWorkout.id |
| Done | Template's index in allWorkouts is before today's index |
| Scheduled | Template's index is after today's index |
Workout edits via saveWorkoutExercises() propagate to all weeks by matching workout name (not ID).
- Skipping a day doesn't shift the schedule — next open still shows the next workout
- No "rest day" or "skipped" status in progression logic
- Calendar shows actual session dates, not projected schedule
The logic above is unchanged — it just runs per Discipline. The Discipline model shipped in v1.4.0 with two Disciplines (Strength and Belly Dance) each tracking their own active program; Belly Dance was removed in v1.10.0 (US-051), and v1.10.0's One Workout Section (US-052) also made exactly one plan active at a time, full stop — setActiveProgram() is now always exclusive rather than appending per-Discipline. With one Discipline shipped today, this is one active program computing its own todaysRoutine, currentWeek, and streak. Two deliberate non-goals, locked in US-015:
- Count-driven, never calendar-driven. No day-of-week scheduling — the user logs the active plan's next routine whenever they show up, and it's recommended by count (A → B → C).
- No load periodization on course programs. Weeks are not auto-progressed; weight carries forward via the per-item last-used prefill and is adjusted manually. A plan with a goal (v1.9.0, merged into Workout in v1.10.0) is a separate program type with a wave-loading generator — see v1.9.0 and US-052.
When a plan has a goal, routine rotation and week numbering use the same count-driven formulas as plain plans (via a generated backing Program). What differs is prescribed load: the focus exercise follows 4-week wave blocks (final peak snaps to the goal weight × reps); supporting exercises bump by weightIncrement each week. Block repeat rewinds targets via countOffset and extends the backing program without erasing session history. Since only one plan is ever active, a plan with a goal is automatically the sole active Strength plan while it runs — activating a different plan pauses it. Spec: US-033 (wave generator), US-052 (merge into Workout).
- How It Works — Plain-language mental model
- Data Model — Program, Routine, Session types
- Program Management — Editing requirements
- State Management —
programStorederived values