fix: BG response shows mmol/hr, coach derives longest run from activity data#193
Merged
Conversation
…tivity data
Bug 1: BGResponsePanel headline showed "-0.0 mmol/L /min" for stable
runs. Per-window slope averaging cancels positive/negative windows,
rounding to -0.0. Switch headline to medianRate × 60 with "mmol/hr"
label and per-hour color thresholds. Per-bucket displays unchanged.
Bug 2: Coach AI prompt hardcoded "Longest distance: 10km" — actual
longest is 14km (W11 Long, Apr 22). Derive from completed events;
omit the sentence when no data.
Bug 3 ("adapt-plan recommends 90/75 g/h up from 62") deferred — likely
downstream of Bug 1 feeding the model bad signal. Watch one cycle
before tightening MAX_FUEL_MULTIPLIER or EXTRAPOLATION_FACTOR.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR #192 ballooned a 3-bug fix into a 97-file rewrite that's been abandoned. Salvaged the conceptually-good ideas as future-work entries without bringing the implementation: IDEAS.md (Next): - Timezone-Correct Planned Events — processPlannedEvents parses start_date_local with parseISO; wrong UTC instant for non-UTC users on Vercel. Self-contained ~30-line PR. - BG-Context Status Banner — surface upstream BG fetch failures (Scout outage, missing creds) instead of silently rendering empty predictions. IDEAS.md (Parked): - Server-Owned runBGContext via Scout Batch — drop the persisted column, compute on every read by batching one Scout request. - Personal Hypo Floor — per-runner BG threshold below which hypos cluster, combined with Riddell 2017 consensus. - Pump-During-Runs Setting — replace hardcoded "All runs are pump-off" assertion in coach + run-analysis prompts. TODO.md (Tech Debt): - activity_streams.glucose is a derived cache (same anti-pattern as the deferred run_bg_context derivation; cross-references the IDEAS.md Parked entry). The IDEAS.md component-name updates from #192 (BGResponsePanel → DuringPatternCards/AfterPatternCards/TomorrowCard) are intentionally not salvaged — those components don't exist on main. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
68 tasks
…ket displays The headline switch in 174e6ab fixed the "-0.0 mmol/L /min" lie but left every other display in BGResponsePanel.tsx on per-minute units with the pre-existing rateColor() thresholds (-0.5, -1.5). Those translate to -30 and -90 mmol/hr — physiologically unreachable — so the per-activity breakdown, StartingBGSection, EntrySlopeSection, and TimeDecaySection rendered every dot green regardless of the actual drop. Single boundary fix: convert per-min to per-hour at the display layer via a new `perHour(perMin)` helper. rateColor() thresholds drop to -1 / -3 mmol/hr, matching the headline's logic (which is now collapsed into the same function). Five call sites converted; SuggestionCard text label updated to "mmol/hr". No data change — model still stores rates per-minute. The lie was at the display boundary, the fix is at the display boundary. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…Chart The previous commit (9868957) fixed BGResponsePanel but left two siblings on per-min units with broken thresholds: - BGCompact (Overview tab top — the card user actually sees first) had copy-pasted rateColor/rateLabel with the same -0.5/-1.5 per-min thresholds. Headline showed "-0.0 Stable" with green dots universally. - BGScatterChart axis labeled "mmol/L /min", tooltip said "mmol/L/5m" (already inconsistent with itself), crash reference line at CRASH_DROP_RATE = -1.5 per-min = -90 mmol/hr — off-screen forever. Extracted perHour / rateColor / rateLabel into lib/bgRateDisplay.ts so the next sibling component can't drift again. All three consumers (BGResponsePanel, BGCompact, BGScatterChart) import from there. BGCompact headline switches to medianRate × 60 (matches BGResponsePanel logic). CRASH_DROP_RATE retuned from -1.5 (per-min) to -3 (mmol/hr); only consumer is BGScatterChart. BGCompact tests updated to use realistic per-min fixtures (e.g. medianRate -0.04 → displays "-2.4 mmol/hr"). Assertions still check user-visible text ("Stable", "Moderate", "Fast drop") — no implementation-detail testing. Bug 3 (adapt-plan recommending 90/75 g/h) is the remaining piece, still deferred per option D — likely downstream of these display lies feeding the model's regression. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5 tasks
psjostrom
added a commit
that referenced
this pull request
May 14, 2026
* fix(bg-model): cap fuel-rate recommendations to clinical titration step; 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> * chore(bg-model): trim rotting comments, add boundary + cap-vs-penalty 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. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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
Two surgical bug fixes plus salvaged ideas/TODO from the abandoned PR #192.
Bug 1 —
BGResponsePanelshowed-0.0 mmol/L /minfor stable runs. Per-window slope averaging cancels positive/negative windows; rounding to 1 decimal kills the sign and rounds to zero. Switched the headline tomedianRate × 60with anmmol/hrlabel and per-hour color thresholds. Per-bucket displays unchanged.Bug 2 — Coach AI prompt hardcoded "Longest distance: 10km". Actual longest is 14km (W11 Long, Apr 22). Now derived from completed events; sentence omitted when no data.
Bug 3 — adapt-plan recommends 90/75 g/h up from 62. Deferred. Likely downstream of Bug 1 feeding the regression bad signal. Watch one cycle before tightening
MAX_FUEL_MULTIPLIERorEXTRAPOLATION_FACTOR.Salvage from #192
PR #192 is abandoned. Conceptually-good ideas preserved as future-work entries without bringing the implementation:
runBGContextvia Scout Batch; Personal Hypo Floor; Pump-During-Runs Settingactivity_streams.glucoseis a derived cache (cross-references the Parked entry)Test plan
npx tsc --noEmitpassesnpx vitest run— 1429/1429 passnpx eslinton touched files clean-0.0)🤖 Generated with Claude Code