autoDetectWorkouts: bypass motion gate on genuine HR onset (edge#170) - #32
Conversation
edge#170: a real 1h50m cycling session (avg ~117W, HR 100-140, peak 157) produced no suggested workout at all. The motion-confirmation gate (motionConfirmMean = 0.15 mean L2 gravity-delta/s) is tuned for arm-swing activities (walking/running) and unconditionally drops any window where the wrist stays still — which is exactly what happens on a handlebar/oar during cycling/rowing, regardless of how strong the HR signal is. A raw %HRR bypass (skip motion above some higher HR-reserve fraction) was considered and rejected: it re-opens the exact false positive the gate exists to catch — any sustained elevation (fever, anxiety, heat) would qualify, since level alone doesn't distinguish "started exercising" from "already elevated." Instead, bypass on HR ONSET kinetics: mean bpm over the first 3 min of the candidate span must rise by >=25 bpm versus the 3 min immediately before it. Real aerobic effort shows a fast phase-II HR kinetic response right as exertion begins (Whipp & Wasserman 1972); a slow-drifting plateau (fever/heat/anxiety climbing over many minutes, or data that starts mid-elevation with no visible onset) shows no such rise and stays gated. No usable pre-window -> abstain (never fabricate an onset that isn't there).
|
Warning Review limit reached
Next review available in: 52 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe workout detector now evaluates HR onset when motion confirmation fails. It compares pre-span and initial-span mean BPM values and accepts only spans with sufficient HR rise and complete sample coverage. Tests cover accepted and rejected low-motion cases. ChangesWorkout HR-onset detection
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/src/onehz/workout/auto_detect.dart`:
- Around line 297-298: Update the earlyMean calculation in the auto-detection
flow to pass the exclusive upper bound for exactly the first three minutes,
removing the extra one-second increment from the _meanBpmInRange call. Preserve
the existing end and onsetWindowS bounds while ensuring its lo <= ts < hi range
covers only 180 seconds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 4886f6ce-b2d4-478b-9251-8404ccc4f8fc
📒 Files selected for processing (2)
lib/src/onehz/workout/auto_detect.darttest/onehz/workout_test.dart
| final earlyMean = _meanBpmInRange( | ||
| ts, bpm, start, math.min(end, start + onsetWindowS) + 1); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use exactly the first three minutes for earlyMean.
_meanBpmInRange uses lo <= ts < hi. Line 298 adds one second, so it evaluates 181 seconds. This can change a result at the 25 BPM boundary.
Proposed fix
final earlyMean = _meanBpmInRange(
- ts, bpm, start, math.min(end, start + onsetWindowS) + 1);
+ ts, bpm, start, start + onsetWindowS);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/src/onehz/workout/auto_detect.dart` around lines 297 - 298, Update the
earlyMean calculation in the auto-detection flow to pass the exclusive upper
bound for exactly the first three minutes, removing the extra one-second
increment from the _meanBpmInRange call. Preserve the existing end and
onsetWindowS bounds while ensuring its lo <= ts < hi range covers only 180
seconds.
CodeRabbit caught this on #32: _meanBpmInRange uses a half-open [lo, hi) range. preMean correctly used [start-onsetLookbackS, start) (180 samples), but earlyMean used [start, min(end, start+onsetWindowS)+1) — the trailing +1 made it 181 samples when the span outlasts the onset window, asymmetric with the pre-window it's compared against. Fixed to [start, min(end+1, start+onsetWindowS)): matches the 180-sample width when the span is long enough, while still including the span's last sample (end, inclusive elsewhere in this file) when the span itself is shorter than onsetWindowS.
Summary
edge#170: a real 1h50m cycling session (avg ~117W, HR 100-140, peak 157 bpm) produced no suggested workout —AutoWorkoutDetector's motion-confirmation gate (motionConfirmMean = 0.15) is tuned for arm-swing activities and unconditionally drops any window where the wrist stays still, which is exactly what happens gripping a handlebar/oar during cycling/rowing, regardless of HR signal strength.Test plan
dart test— 386 passed (was 383; +3 net from new/restructured workout tests), no failures.Summary by CodeRabbit
New Features
Bug Fixes