Repository navigation
Assisted-bodyweight volume encoding changes at the upgrade boundary, breaking trend comparisons #67
Description
Summary
WorkoutSet.calculateVolume() (in workout-logger/lib/models/models.dart) now computes volume differently depending on whether assistWeight is set:
- Sets logged before this feature existed (or before a user's exercise got reclassified as bodyweight-assisted):
assistWeight == null→ volume =weight × reps. - Sets logged after:
assistWeightis populated → volume =(bodyweight − assist + extra) × reps.
Both encodings coexist permanently in stored data. Anything that compares volume/weight across sessions spanning the upgrade boundary sees a step change that has nothing to do with actual training progress.
The most concrete downstream break is in workout-logger/lib/services/ml_service.dart (recommendSets, around lines 398-410): the deload-detection logic compares w0/v0 from the most recent session against w1/v1 from the session before it. If one of those two sessions used the old encoding and the other the new one, the comparison is comparing incompatible numbers — a normal progression can be misclassified as a deload (or the reverse), which changes the weight/rep recommendation the AI coach and the in-app suggestion card show the user.
How to reproduce (user-visible impact)
- Have at least one existing logged set for a bodyweight-assisted exercise (
pull_ups,chin_ups,dips, orpush_ups) from before this app version — i.e.assistWeightis null on that historical set, so its stored volume is plainweight × reps. - Update to this app version and log a new set for the same exercise. This new set gets
assistWeight/bodyWeightAtLogpopulated, so its volume uses the new formula. - Open the exercise's progress/volume chart (Analytics tab) — there will be a visible discontinuity in the volume trend line exactly at the upgrade point, unrelated to any real change in training.
- Alternatively: with exactly two past sessions logged for the exercise (one pre-upgrade, one post-upgrade), open the workout screen for that exercise and check the AI set recommendation / "recovering from deload" messaging. Because
ml_service.dartcompares the two sessions' effective load directly, the recommendation can flip into deload-recovery mode (or fail to) based on the encoding mismatch alone, not the user's actual last two sessions.
Why this isn't a quick fix
Resolving it requires picking a real compatibility strategy up front, not a local code change:
- Backfill
assistWeight/bodyWeightAtLogonto historical sets during/after the SQLite migration (needs a defensible default assist value — there isn't one, since old sets never recorded it), or - Record an explicit encoding version per set and teach every volume/trend consumer (
calculateVolume,ml_service.dart's deload comparison, any future analytics) to normalize across versions.
Either path is a deliberate data-modeling decision, not something to guess at while resolving PR review comments. Flagging as a known limitation to design properly.
Pointers
workout-logger/lib/models/models.dartlines 155-173 (calculateVolume,effectiveWeight)workout-logger/lib/services/ml_service.dartlines 398-410 (deload-recency comparison)- Originally flagged by CodeRabbit on PR Migrate storage from Hive to SQLite + add coach SQL query tool #66: Migrate storage from Hive to SQLite + add coach SQL query tool #66 (comment)
Activity
fix: address second round of CodeRabbit findings on PR #66
Migrate storage from Hive to SQLite + add coach SQL query tool (#66)
feat: recovery-aware recommendation engine + per-model Gemini thinkin…