Cursor/critical bug investigation 38cb - #577
Conversation
… (Issue #572) When a routine has two adjacent RoutineExercise entries that share the same physical exercise but have different programMode (e.g. 'Sumo Belt Squat 2x8 OldSchool' followed by 'Sumo Belt Squat 1x8 TUT'), the routine-advance logic was unconditionally sending a fresh 0x04 BLE CONFIG frame for the mode change. The Vitruvian firmware de-energises the cable on a mode change, which the user reported as 'the set deloads weight but the screen still shows the set weight'. This change introduces a 'same-exercise continuation' branch in ActiveSessionEngine.startNextSetOrExercise: * RoutineFlowManager.isSameExercise() matches adjacent entries by name, plus id when both ids are non-null (id check skipped when either side is null for legacy / unlinked exercise data). * When getNextStep advances to the next entry and isSameExercise() is true, the consumer: - keeps the on-screen programMode label aligned with the new entry while carrying forward the current programMode to the firmware (so the cable is NOT de-energised mid-movement); - applies the per-set weight from the new entry's setWeightsPerCableKg[nextSetIdx]; - skips the fresh startWorkout() (no fresh 0x04 CONFIG frame) and goes to SetReady so the user explicitly starts the TUT finisher, at which point the fresh CONFIG is sent at the right time; - does NOT re-seed rack defaults, does NOT reset the rep counter, does NOT re-initialise the warm-up phase (it is still the same physical exercise). * Genuinely different adjacent exercises, the superset branch, and same-entry set advance (preserves manual rest-screen weight/rep edits) are all unchanged. Acceptance criteria covered by new regression tests in DWSMRoutineFlowTest (section G. Same-exercise continuation): 1. (0, 0) -> (0, 1) stays inside Lunge 2x8, not jumping to Lunge TUT. 2. Full 'Legs' sequence: (0,1) -> (1,0) -> (2,0) -> (2,1) -> (3,0) -> (4,0) -> null. 3. No same-exercise adjacency (existing fixture) is unchanged. 4. Different ids with same name do NOT merge. 5. Null ids (legacy / unlinked data) are still detected as same exercise. 6. Superset branch interleaving is unchanged for same-exercise entries inside a superset. 7. isSameExercise is false for genuinely different adjacent exercises. No schema change. No BLE packet format change. No firmware change. No superset-branch change. No enterSetReady change. No new UI de-duplication at routine edit time. Fixes #572
…creen stall Issue #572 same-exercise continuation called enterSetReady while workoutState remained Resting after autoplay rest completion. ActiveWorkoutScreen only navigates to SetReady when workoutState is Idle, so users were stranded on the rest UI. Tapping Skip Rest re-invoked startNextSetOrExercise and skipped the deferred TUT finisher set entirely. Set workoutState to Idle before enterSetReady and add a regression test. Co-authored-by: Devil <9thLevelSoftware@users.noreply.github.com>
There was a problem hiding this comment.
Code Review
This pull request addresses navigation and double-advance issues during same-exercise continuations by ensuring the workout state transitions to Idle and restricting continuations to adjacent linear exercises. Comprehensive unit tests have been added to verify these fixes. The reviewer suggested a code improvement in ActiveSessionEngine.kt to simplify the boolean logic, leveraging Kotlin's smart-casting and avoiding an intermediate variable.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| val isAdjacentLinearExercise = | ||
| nextExIdx == coordinator._currentExerciseIndex.value + 1 && | ||
| currentExercise?.supersetId == null && | ||
| nextExercise.supersetId == null | ||
| val isSameExerciseContinuation = isChangingExercise && | ||
| currentExercise != null && | ||
| isAdjacentLinearExercise && | ||
| flowDelegate?.isSameExercise(currentExercise, nextExercise) == true |
There was a problem hiding this comment.
We can simplify this logic by combining isAdjacentLinearExercise directly into isSameExerciseContinuation. This avoids creating an unnecessary intermediate variable, leverages Kotlin's smart-casting for currentExercise (so we don't need the safe-call ?. operator on supersetId), and utilizes short-circuit evaluation to prevent redundant checks when currentExercise is null.
| val isAdjacentLinearExercise = | |
| nextExIdx == coordinator._currentExerciseIndex.value + 1 && | |
| currentExercise?.supersetId == null && | |
| nextExercise.supersetId == null | |
| val isSameExerciseContinuation = isChangingExercise && | |
| currentExercise != null && | |
| isAdjacentLinearExercise && | |
| flowDelegate?.isSameExercise(currentExercise, nextExercise) == true | |
| val isSameExerciseContinuation = isChangingExercise && | |
| currentExercise != null && | |
| nextExIdx == coordinator._currentExerciseIndex.value + 1 && | |
| currentExercise.supersetId == null && | |
| nextExercise.supersetId == null && | |
| flowDelegate?.isSameExercise(currentExercise, nextExercise) == true |
Code Review Roast 🔥Verdict: No Issues Found | Recommendation: Merge Oh wait, this PR is actually clean. I need to sit down. I had my flamethrower warmed up and everything. This PR fixes Issue #572 with surgical precision — adding adjacency checks to prevent same-exercise continuation from incorrectly triggering when (a) an intervening exercise was skipped, or (b) superset member transitions occur. The logic change in The only change I could even snark about is the new 📊 Overall: Like finding a unicorn in production — meticulously crafted, well-tested, and ready to ship. Files Reviewed (2 files)
Reviewed by laguna-m.1-20260312:free · 1,650,036 tokens |
No description provided.