diff --git a/domain/prediction/src/main/kotlin/dev/privacyllc/period/domain/prediction/PersonalPredictionEngine.kt b/domain/prediction/src/main/kotlin/dev/privacyllc/period/domain/prediction/PersonalPredictionEngine.kt index e4c7efd..4181d0d 100644 --- a/domain/prediction/src/main/kotlin/dev/privacyllc/period/domain/prediction/PersonalPredictionEngine.kt +++ b/domain/prediction/src/main/kotlin/dev/privacyllc/period/domain/prediction/PersonalPredictionEngine.kt @@ -421,6 +421,32 @@ class PersonalPredictionEngine : PredictionEngine { const val NOT_YET_MAX_PENALTY = 0.30 const val MINIMUM_FOR_MEDIUM = 2 const val HIGH_THRESHOLD = 0.55 - const val MEDIUM_THRESHOLD = 0.44 + /** + * Raised from 0.30 when [EXPECTED_ERROR_PER_SCALE] landed, and not as a + * separate opinion — the two are one change. + * + * The old accuracy term deflated every score, including scores that had + * earned better, so the thresholds sat where they had to sit to let a + * stable user through at all. Once accuracy stopped punishing errors the + * window already admits, every calibrated forecast scored near its own + * agreement and 0.30 no longer separated anything: the §51 variable user + * — cycles from 25 to 37 — landed at 0.32 and began reading Medium + * beside an eleven-day window. + * + * Measured at 400 seeds, not guessed. The ordering agreement produces is + * already right (stable ~0.66, drifting ~0.52, variable ~0.32); only the + * line was in the wrong place. 0.40 puts it in the gap: stable reads High + * 88% of the time late in her history, variable reads Low 93%, and + * drifting — tracked to within a day and a quarter — keeps Medium. 0.44 + * was measured too and rejected: it pushes drifting back toward Low at + * 21%, telling a woman the app has lost the thread of a cycle it is + * following well. + * + * Tightening AGREEMENT_SENSITIVITY instead was tried across 1.0, 1.2 and + * 1.4 and rejected: agreement multiplies into every score, so every value + * that quietened the variable user also took High from the stable one, + * and 1.0 broke §51 outright. + */ + const val MEDIUM_THRESHOLD = 0.40 } }