Commit Graph
3 Commits
Author SHA1 Message Date
Marc-André Bombeck df67b60420 fix(cycle): raise the hard cycle-length ceiling so long cycles survive
The hard upper bound clamped cycle lengths at 45 days, force-excluding any
longer cycle as an outlier before the robust statistics ran. Cycles over 35
days are oligomenorrhea but still real, and adolescent and perimenopausal users
routinely run to fifty days or more — exactly the irregular users for whom an
honest estimate matters most. Clamping their genuine cycles biased the estimate
toward the population middle.

Raise the ceiling to 60 days. It stays a coarse gross-error backstop; the
median-plus-MAD fence and the missed-log heuristic still do the real outlier
work, and because the fence is computed before exclusion, raising the ceiling
does not move it — it only stops the hard rule from pre-empting the fence on
long but consistent cycles.
2026-06-14 14:43:57 +02:00
Marc-André BombeckandClaude Opus 4.8 15bece9849 fix(cycle): scope symptothermal to the current cycle, unify luteal clamp, widen fertile + crosstab coverage
The symptothermal layer ran against the full day-log history: detectTempShift
returned the earliest 3-over-6 rise and detectMucusPeak the last egg-white day
across all cycles, so a multi-cycle user with historical BBT confirmed a
months-old ovulation and the predicted next-period start landed in the past.
Scope the temperature/mucus detectors to the current cycle window
[lastConfirmedStart − BBT_WINDOW, today], take the latest qualifying temp shift,
and reject any confirmation whose next-start is already in the past.

Resolve luteal length through a single clamp. The predictor clamped to [10,16]
while the calendar adapter and phase mapper used the raw stored value, so the
ovulation dot and the OVULATORY band could land on different days for an
out-of-clamp profile. Export resolveLuteal/clampLuteal, route buildCalendar,
buildPhaseCycles and phaseForDay through it, and tighten the profile-prefs Zod
bound to the same [10,16] range.

Show the fertile window for AVOID_PREGNANCY as well as TRYING_TO_CONCEIVE per the
algorithm spec; it stays hidden for GENERAL_HEALTH/PERIMENOPAUSE.

Bound the day-log reads on the calendar and insights routes to the rendered span
plus the symptothermal lookback instead of the full history; cycle-length stats
already run off the MenstrualCycle rows.

Add mood and blood glucose as phase-crosstab outcome metrics under the same FDR
and per-group day floors. Mood is read from MoodEntry and injected as a synthetic
channel since it is not a measurement row.

Add multi-cycle, luteal-clamp and fertile-window fixtures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 12:06:48 +02:00
Marc-André BombeckandClaude Opus 4.8 ea0d7681e2 feat(cycle): add deterministic prediction + phase engine
Pure, DB-free cycle prediction and phase-assignment engine under
src/lib/cycle/. The math mirrors the offline-parity spec exactly so the
server and the native client compute byte-identical forecasts from the
same canonical inputs.

- day-math: noon-UTC anchored YYYY-MM-DD arithmetic (dayDiff, addDays,
  round-half-up) that stays stable across timezones and DST transitions.
- prediction: robust median+MAD cycle-length estimator over a trailing
  12-cycle window with 3-MAD outlier exclusion and missed-log detection;
  period-length estimator; one-sigma Low/High date band widened by
  logging sparsity; luteal back-calculated ovulation + six-day fertile
  window; the CALENDAR -> symptothermal -> temperature-trend method
  ladder (sensiplan 3-over-6 + mucus-peak re-implemented from the
  published method) with documented blend precedence; a multiplicative
  confidence scalar shrinking with length variance, low cycle count, and
  sparse logging.
- phase: day-of-cycle -> MENSTRUAL/FOLLICULAR/OVULATORY/LUTEAL boundary
  mapping with menstrual precedence on overlap.
- 48 unit tests covering the regular, irregular, cold-start, outlier,
  symptothermal, temperature-trend, and every phase-boundary case; all
  assertions are timezone-independent and pin exact dates + confidence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 08:29:52 +02:00