Commit Graph
2 Commits
Author SHA1 Message Date
Marc-André Bombeck 0849d59ecf test(cycle): pin the fixture clock to the suite's clock
Two cycle suites compute their relative day keys with local Date arithmetic
under TZ=UTC while the routes resolve today in the user's zone, falling back
to Europe/Berlin when the fixture sets none. For two hours every evening the
two clocks disagree about the date and every day-of-cycle assertion is off
by one — the suite was red from 22:00 UTC to midnight and green the rest of
the day. The fixtures now carry an explicit UTC timezone so the test's clock
and the route's clock are the same clock at any hour. Reproduced red at
23:07 UTC, green with the pin at 23:19 UTC.
2026-08-08 01:02:08 +02:00
Marc-André Bombeck 3596720905 fix(cycle): let a period logged as flow count as a period
MenstrualCycle rows were only ever written by the one-tap period
boundary. Every other way of recording bleeding wrote a day-log and
stopped there: the log sheet's flow chips, the bulk drain, an Apple
Health export full of menstrual-flow samples. The engine reads cycle
rows, so a person could log every period they had and the module still
held that none had happened. No cycle length, no forecast, an empty
chart, and an offer to log a first period. An Apple Health user who
never found the one-tap button had the whole module dead.

A first bleeding day now opens the cycle it belongs to, through one
shared seam in the day-log write helper, so the single capture, the
bulk drain and the Apple Health fold all infer a start the same way and
the one-tap boundary's own write lands on a date that already has a
cycle, which the rule declines.

The rule is conservative, because a false start corrupts every length
derived after it. Spotting does not open a cycle: it is as often the
tail or the herald of a period as its first day. Bleeding flagged as
between periods does not, since that flag exists to say so. Neither does
a bleeding day within the hard physiological minimum of an existing
start, which is what keeps day three of a period, or a forgotten day
two, from starting a cycle of its own without needing a contiguity
check. A genuinely short cycle is not inferred from flow alone; the
one-tap start still opens one on any date.

An edit is symmetric. Setting a flow opens the cycle; clearing it, or
flagging it as between periods, removes the cycle that day opened and
re-opens the one before it. A cycle that an edit can create and not undo
leaves the record holding a period nothing on the day says happened.

- the period route's start arm and the flow inference now share one
  boundary helper, so a start means the same thing however it is entered
- back-dating re-anchors both neighbours, as the one-tap start does
- integration: eleven cases through the real routes, of which five are
  about what must NOT open a cycle
2026-08-07 15:50:23 +02:00