Commit Graph
4 Commits
Author SHA1 Message Date
Marc-André Bombeck 609d248252 feat(cycle): choose cervix as the symptothermal secondary symptom
Let a user pick the Sensiplan double-check's second indicator — cervical
mucus (default) or cervix observation — mirroring the published NFP
standard where the temperature primary sign pairs with one chosen
secondary sign.

- `CycleProfile.secondarySymptom` (MUCUS default | CERVIX) drives which
  second sign the engine confirms against. Casual users keep temperature
  + mucus untouched; the choice lives behind the advanced cycle settings.
- `CycleDayLog` gains the three standard cervix signs — position
  (LOW/HIGH), firmness (FIRM/SOFT), opening/os (CLOSED/OPEN) — as nullable
  columns (migration 0157), wired through Zod, the day-log upsert, the
  DTO, the sync feed, backup/restore and OpenAPI. Additive and
  back-compatible: existing profiles backfill MUCUS, existing day-logs
  keep NULL cervix signs, iOS inherits the fields.
- The engine's `confirmSymptothermal` takes the secondary-symptom choice;
  on CERVIX it confirms against `detectCervixPeak` (last fertile-cervix
  day followed by three closed days — the cervix-closure rule) instead of
  the mucus peak. The double-check still requires BOTH the temperature
  shift AND the secondary peak: a single cervix sign never confirms, and
  the still-learning gate and conservative posture are unchanged.

Engine derived clean-room from the Arbeitsgruppe-NFP / Raith-Paula &
Frank-Herrmann standard.
2026-06-14 15:23:26 +02:00
Marc-André Bombeck 4bfe9f11d7 feat(cycle): exclude disturbed temperatures from the symptothermal engine
A fever, illness, alcohol or a late/short-sleep reading can fake or mask
a temperature rise. Add a per-day `temperatureExcluded` flag on
CycleDayLog (migration 0156): when set, the engine drops the reading from
the cover-line baseline AND the shift evaluation, so a spike neither
fabricates nor masks a rise (Sensiplan: the cover line is drawn over the
last six unbracketed values).

Wired end to end — schema + Prisma, the engine's `detectTempShift`
filter, the Zod input/patch schemas, the day-log upsert (partial merge +
change detection), the read DTO, the calendar/insights/coach selects, the
backup round-trip, and the OpenAPI contract (a new boolean on the day-log
and calendar-day DTOs for the iOS client). The log-day sheet gains a
"mark reading as disturbed" toggle that appears once a temperature is
entered, translated across all six locales.

Citation: de.wikipedia "Symptothermale Methode"; myNFP "Temperaturkurve
auswerten" — Störfaktor bracketing.
2026-06-14 15:04:25 +02:00
Marc-André Bombeck f5798e20d8 fix(cycle): gate the calendar grid to a learning state during cold start
The predictions panel suppressed its fertile-window and ovulation rows while
fewer than three cycles were observed, but the month calendar did not — so a
brand-new user who logged a single period saw the grid paint a confident
fertile-window shading, a predicted-ovulation dot, and full phase bands off a
population 28/14 prior at ~0.20 confidence. Calendar-based fertility estimation
is unreliable below several recorded cycles, so the grid was asserting days it
could not know.

Thread the same still-learning gate into the calendar build: while fewer than
three cycles are observed, suppress the fertile window, the ovulation dot, and
the phase band (the predicted-period bar stays, mirroring the panel). Surface a
top-level stillLearning flag on the calendar response so the client renders a
calm "learning your cycle" banner over the grid instead of a bare, falsely
confident calendar. The flag is additive and back-compatible; older clients
ignore it and degrade to the previous render.

Also pin the observed-cycle count to a single source — the engine's
post-exclusion value — so the response never carries two divergent counts.
2026-06-14 14:43:50 +02:00
Marc-André Bombeck 35e8f076a2 refactor(openapi,queries): feature-located registries behind thin indexes
The five-thousand-line route registry splits into thirteen domain
modules and the query-key factory into twelve feature files — each
behind an index that preserves exact registration order, proven by a
byte-identical generated contract. The factory lint rule learns the new
home directory.
2026-06-11 00:10:00 +02:00