The Insights screen can contradict itself: it reports a large, steady change at the top of the card and "no significant change" at the bottom of the same card, and it labels a perfectly smooth trend as volatile.
Reproducing it
No hardware needed — the project's own demo generator produces it:
About → Developer Tools → Scenario: Trend Progress, Time Range: Last 6 months → Generate Demo Data → Insights
What the card then shows, simultaneously:
|
|
| Start / Now |
100.21 kg → 78.10 kg (−22.11 kg) |
| Rate |
−3.74 kg / month |
| Chips |
short-term ↘ long-term ↘ volatile plateau 180 days |
| Summary |
"No significant change for 180 days — your body may be adapting." |
A 22 kg loss over six months is being described as a 180-day plateau.
Cause: both come from using the raw standard deviation on a trending series
1. Plateau — MeasurementInsightsUseCase.kt:178-193
val threshold = stdDev * 0.5f
for (i in dataPoints.indices.reversed().drop(1)) {
if (abs(dataPoints[i + 1].second - dataPoints[i].second) > threshold) break
plateauStartIndex = i
}
The step between two consecutive measurements is compared against half the standard deviation of the entire series. On a trending series those quantities have completely different scales:
series 181 points, 100.16 -> 78.05 kg
stdDev 6.42 kg
threshold 3.21 kg (stdDev * 0.5)
largest consecutive step 0.22 kg
steps above threshold 0
=> loop never breaks, the whole series is reported as a plateau
The relationship is inverted: the stronger the trend, the larger the stdDev, the higher the threshold, and the longer the reported plateau. The cleanest weight-loss curve produces the longest "no change" message.
2. Volatility — MeasurementInsightsUseCase.kt:142-151
val stdDev = stdDev(rawValues, mean)
val relStdDev = if (mean != 0f) stdDev / mean else 0f
On a trending series the raw stdDev measures the span of the trend, not variability around it:
relStdDev 6.42 / 89.15 = 7.20%
thresholds STABLE < 1%, MODERATE < 3%
=> HIGH ("volatile"), for a visually smooth line
Any successful weight-loss run of more than ~3% of body weight is therefore labelled volatile — the users most likely to open Insights.
Suggested direction
Both need the trend removed before measuring spread:
- Plateau: derive the threshold from local step sizes (e.g. stdDev of consecutive differences) rather than from the spread of the whole series — or test the plateau window directly, i.e. "total change within the candidate window is below X".
- Volatility: measure residuals rather than raw values — stdDev of consecutive differences, or residuals from a linear fit.
A shared detrendedStdDev() helper would cover both.
I'd be glad to send a PR with a fix and unit tests in MeasurementInsightsUseCaseTest.kt — but this changes the behaviour of an algorithm you designed, so I'd rather ask first whether you want it, and in which direction.
Minor, same screen
The plateau chip truncates: it renders as 180 günlük durağ in Turkish (180 days plateau fits in English). Probably worth letting that chip wrap or shortening the string.
The Insights screen can contradict itself: it reports a large, steady change at the top of the card and "no significant change" at the bottom of the same card, and it labels a perfectly smooth trend as volatile.
Reproducing it
No hardware needed — the project's own demo generator produces it:
About → Developer Tools → Scenario:
Trend Progress, Time Range:Last 6 months→ Generate Demo Data → InsightsWhat the card then shows, simultaneously:
short-term ↘long-term ↘volatileplateau 180 daysA 22 kg loss over six months is being described as a 180-day plateau.
Cause: both come from using the raw standard deviation on a trending series
1. Plateau —
MeasurementInsightsUseCase.kt:178-193The step between two consecutive measurements is compared against half the standard deviation of the entire series. On a trending series those quantities have completely different scales:
The relationship is inverted: the stronger the trend, the larger the stdDev, the higher the threshold, and the longer the reported plateau. The cleanest weight-loss curve produces the longest "no change" message.
2. Volatility —
MeasurementInsightsUseCase.kt:142-151On a trending series the raw stdDev measures the span of the trend, not variability around it:
Any successful weight-loss run of more than ~3% of body weight is therefore labelled volatile — the users most likely to open Insights.
Suggested direction
Both need the trend removed before measuring spread:
A shared
detrendedStdDev()helper would cover both.I'd be glad to send a PR with a fix and unit tests in
MeasurementInsightsUseCaseTest.kt— but this changes the behaviour of an algorithm you designed, so I'd rather ask first whether you want it, and in which direction.Minor, same screen
The plateau chip truncates: it renders as
180 günlük durağin Turkish (180 days plateaufits in English). Probably worth letting that chip wrap or shortening the string.