fix(bg-model): cap fuel-rate recommendations#195
Merged
Conversation
…ep; add spread guard PR #193 fixed the BG drop display unit but left the model recommending absurd fuel rates (e.g. 90 g/h Easy from a 62 g/h baseline). Two research-grounded changes: 1. **Spread guard:** regression requires tested fuel rates to differ by ≥ FUEL_STEP_GH (10 g/h). Below that, the slope fits noise across a narrow window and solving for "ideal drop" extrapolates absurd targets. Real example from Per's data: 4 g/h spread (60-64), drop nearly identical across groups, regression returned raw target 143 g/h capped to 90. Falls through to extrapolation, which moves one step at a time. 2. **Step cap:** `MAX_FUEL_MULTIPLIER = 1.5` (50% jump per cycle) replaced with `current + FUEL_STEP_GH` (10 g/h max increment per cycle). The 50% multiplier had no clinical basis and violated incremental CHO titration guidance — sudden CHO increases cause GI distress (Costa et al. 2023 systematic review). The 10 g/h step matches the standard sports-nutrition gut-training increment (Jeukendrup & Killer 2010) and the "Rule of 15" magnitude from the Riddell et al. 2017 T1D exercise consensus. `MAX_FUEL_ABSOLUTE = 90` kept — it's the gut absorption ceiling for an untrained gut (single-source SGLT1 limit ~60 g/h, 60-90 with glucose-fructose blends and gut adaptation; >90 only with elite-level adaptation). End-to-end verified against 67 real activities: - Easy: 90 → 65 g/h (regression skipped, extrapolation result) - Long: 56 → 56 g/h (regression still triggers, real signal across 12 g/h spread) - Interval: 60 → 60 g/h (default, drop doesn't meet MIN_DROP_TO_SUGGEST) IDEAS.md: added "Phase-Aware Fuel Modeling" — investigation while implementing this fix surfaced that the model's bigger problem is averaging drop rate across both pre-fuel (minutes 0-25, no fuel landed yet) and post-fuel (minutes 25+, fuel kicking in) phases. Easy runs are dominated by pre-fuel kinetics that more fuel cannot fix. Worth a separate focused PR. TODO.md: queued post-run hyper investigation — needs runBGContext data which the debug script disabled to skip Scout. Tests: 1431 → 1431 pass (existing regression test updated for the new cap value 66 → 55; two new tests cover the spread guard and step cap). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
psjostrom
commented
May 14, 2026
psjostrom
commented
May 14, 2026
psjostrom
commented
May 14, 2026
psjostrom
commented
May 14, 2026
psjostrom
commented
May 14, 2026
Owner
Author
psjostrom
left a comment
There was a problem hiding this comment.
Math is solid — traced the spread guard, step cap, and spike-penalty interaction against the new test cases, all check out. End-to-end verification against 67 activities is a strong signal.
Four polish notes inline: two comment-rot issues (suggestions provided) and two non-blocking test-coverage gaps (cap-vs-penalty pinning, spread-guard boundary at exactly 10 g/h).
… tests Address review feedback on PR #195: - Drop `MAX_FUEL_MULTIPLIER` reference from `capFuel` comment — the constant no longer exists in the codebase, so the reference would rot on merge. - Drop the trailing "Falls through to the extrapolation path" sentence from the spread-guard comment — it narrates the other branch and would go stale if extrapolation semantics change. - Add boundary test: spread of exactly `FUEL_STEP_GH` (10 g/h) qualifies for regression. Guards against a future `>=` → `>` flip. - Add cap-vs-penalty ordering test: with raw target above the step cap, spike penalty subtracts from raw before the cap clamps (penalty-then-cap), not the other way around.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bug 3 from the original screenshots — model recommending 90 g/h Easy when current is 62. Two research-grounded changes to
lib/bgModel.ts:Spread guard — regression now requires tested fuel rates to differ by ≥ 10 g/h (one CHO titration step). Below that, the slope fits noise across a narrow window and solving for the model's "ideal drop" extrapolates absurd targets. Real example from production data: 4 g/h fuel spread (60-64), drop nearly identical across groups, regression returned raw target 143 g/h capped to 90. After fix, falls through to extrapolation which moves one step at a time → 65 g/h.
Step cap —
MAX_FUEL_MULTIPLIER = 1.5(50% jump per cycle) replaced withcurrent + 10 g/h(one CHO step max increment). The 50% multiplier had no clinical basis and violated incremental CHO titration guidance — sudden CHO increases cause GI distress (Costa et al. 2023 systematic review). The 10 g/h step matches the standard sports-nutrition gut-training increment (Jeukendrup & Killer 2010) and the "Rule of 15" magnitude from the Riddell et al. 2017 T1D exercise consensus.MAX_FUEL_ABSOLUTE = 90kept — it's the gut absorption ceiling for an untrained gut.End-to-end verified against real activity data (67 runs)
MIN_DROP_TO_SUGGESTSide discovery while debugging — phase-aware modeling
Per-time-bucket analysis of the same 67 activities surfaced something the current model can't see: drop rate has two distinct phases. CHO ingested at minute 10 enters the bloodstream around minute 25, and the data reflects this clearly:
Easy runs (median 44 min) spend ~70% of their time in pre-fuel territory; Long runs (median 76 min) spend ~40%. So the average drop rate Easy reports is dominated by pre-fuel kinetics that no amount of fuel during the run will fix. The current model's "Easy needs more fuel" diagnosis is directionally wrong.
The fix in this PR limits the damage of that misdiagnosis (incremental titration). The full fix is phase-aware modeling, queued in IDEAS.md as the next focused PR after this lands.
Also noted in TODO.md: the post-run hyper part of the timing hypothesis remains untested (only 1 spike sample because the investigation script disabled
runBGContextto skip Scout; needs a follow-up with full BG context).Test plan
🤖 Generated with Claude Code