Estimate ovulation and the fertile window, with uncertainty inherited from the forecast #21

Closed
opened 2026-08-18 14:44:41 -05:00 by null · 0 comments
Owner

CycleStatus and CalendarMarks both already take a fertile window and an ovulation date, and both currently receive null. Nothing estimates them.

PRODUCT_PLAN.md §17: for a calendar-only V1, estimate ovulation relative to the predicted next period and the user's available cycle history. Do not imply that a fixed day-14 rule is biologically exact. The luteal-phase assumption is configurable/estimated, with the uncertainty reflected rather than hidden.

What to build in domain/prediction:

  • ovulation = predicted start − luteal phase, defaulting to a sensible constant and overridable
  • fertile window from ovulation, accounting for sperm survival before it and the egg's short viability after
  • uncertainty that inherits from the forecast. This is the part that is easy to get wrong: ovulation is derived from a predicted date, so it cannot be more certain than that prediction. A user whose next period is a nine-day window does not have a one-day ovulation estimate, and showing her one would be the app inventing precision.

The trap: an ovulation date computed from a forecast that has been re-conditioned by "Not yet" moves with it. That is correct and worth a test, because the alternative — pinning ovulation to the original forecast — silently contradicts the period estimate on the same screen.

No new data is collected. §17 lists LH tests, basal body temperature and cervical mucus as future inputs that are not required for V1.

Verify: ./gradlew :domain:prediction:test covers ovulation falling a luteal phase before the predicted start, a fertile window that contains ovulation and ends no later than a day after it, a window at least as wide as the forecast's own uncertainty, and no estimate at all when there is no forecast.

`CycleStatus` and `CalendarMarks` both already take a fertile window and an ovulation date, and both currently receive null. Nothing estimates them. PRODUCT_PLAN.md §17: for a calendar-only V1, estimate ovulation relative to the **predicted next period** and the user's available cycle history. Do not imply that a fixed day-14 rule is biologically exact. The luteal-phase assumption is configurable/estimated, with the uncertainty reflected rather than hidden. What to build in `domain/prediction`: - ovulation = predicted start − luteal phase, defaulting to a sensible constant and overridable - fertile window from ovulation, accounting for sperm survival before it and the egg's short viability after - **uncertainty that inherits from the forecast.** This is the part that is easy to get wrong: ovulation is derived from a predicted date, so it cannot be more certain than that prediction. A user whose next period is a nine-day window does not have a one-day ovulation estimate, and showing her one would be the app inventing precision. The trap: an ovulation date computed from a forecast that has been re-conditioned by "Not yet" moves with it. That is correct and worth a test, because the alternative — pinning ovulation to the original forecast — silently contradicts the period estimate on the same screen. No new data is collected. §17 lists LH tests, basal body temperature and cervical mucus as future inputs that are **not required for V1**. Verify: `./gradlew :domain:prediction:test` covers ovulation falling a luteal phase before the predicted start, a fertile window that contains ovulation and ends no later than a day after it, a window at least as wide as the forecast's own uncertainty, and no estimate at all when there is no forecast.
null added this to the Batch 04 — Fertility milestone 2026-08-18 14:44:41 -05:00
null closed this issue 2026-08-18 14:56:09 -05:00
Sign in to join this conversation.
No Label
P0
P1
P2
release-blocker
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: null/Privacy-Period-Tracker#21
No description provided.