Keep booster and measurement reminders anchored on the last satisfaction

The course rule that stops an old intake from anchoring a rolling
cadence read a reminder's anchor date as a course start, so a booster
fourteen years past on a ten-year cadence came due later today instead
of staying overdue. Measurement and booster reminders now opt out with
rollingAnchor: "lastIntake"; the rule stays on medications.
This commit is contained in:
Marc-André Bombeck committed 2026-10-04 00:33:34 +02:00
1 parent 2c2151ae70
commit b930ab80ff
2 files changed
+16 -1

No files matched your search

@@ -166,6 +166,9 @@ export function buildReminderRecurrence(
},
timeZone: tz,
lastIntakeAt: reminder.lastSatisfiedAt,
// The anchor date is not a course start: the last satisfaction always
// anchors the next due, however long ago it was.
rollingAnchor: "lastIntake",
};
return { schedule, ctx };
+13 -1
View File
@@ -148,6 +148,16 @@ export interface RecurrenceContext {
* resolvers, the dedup pass) must change together.
*/
lastIntakeAt: Date | null;
/**
* v1.40 (#1024) — `"lastIntake"` keeps the rolling anchor on the last
* intake whatever `startsOn` says. A medication's `startsOn` is the start
* of its current course, so an intake whose next dose falls before it
* belongs to an earlier course and does not anchor; a measurement or
* booster reminder's `startsOn` is only its anchor date, and a dose
* fourteen years old on a ten-year cadence must stay overdue. Absent = the
* medication rule.
*/
rollingAnchor?: "lastIntake";
}
export interface Occurrence {
@@ -513,7 +523,9 @@ function rollingDoseAnchor(
? civilDayOfDate(ctx.medication.startsOn)
: null;
return fromIntake !== null &&
(startDay === null || fromIntake.getTime() >= startDay.getTime())
(ctx.rollingAnchor === "lastIntake" ||
startDay === null ||
fromIntake.getTime() >= startDay.getTime())
? fromIntake
: anchorCivilDay(ctx);
}