Merge pull request #308 from MBombeck/release/v1.16.2

v1.16.2 — cap the due-context fallback
This commit is contained in:
Marc-André Bombeck authored and GitHub committed 2026-06-10 21:47:53 +02:00
commit 5bfafa6f51
2 files changed
+44

No files matched your search

@@ -459,3 +459,26 @@ describe("reconstructDoseHistory", () => {
expect(times).toEqual([...times].sort((a, b) => a - b));
});
});
describe("suggestNearestSlot fallback cap", () => {
it("omits due-context when the nearest anchor sits days away", async () => {
const { suggestNearestSlot } = await import("../dose-history");
const mk = (iso: string) => {
const at = new Date(iso);
return {
at,
timeOfDay: "09:00",
onTimeStart: new Date(at.getTime() - 60 * 60_000),
onTimeEnd: new Date(at.getTime() + 60 * 60_000),
overdueEnd: new Date(at.getTime() + 4 * 60 * 60_000),
} as never;
};
// First minted slot is 2026-05-31 (startsOn); the take predates it by 6 days.
const bands = [mk("2026-05-31T07:00:00Z"), mk("2026-06-01T07:00:00Z")];
const far = suggestNearestSlot(new Date("2026-05-25T09:26:00Z"), bands, () => false);
expect(far).toBeNull();
// A take inside the suggestion zone still gets its context.
const near = suggestNearestSlot(new Date("2026-05-31T08:30:00Z"), bands, () => false);
expect(near?.timeOfDay).toBe("09:00");
});
});
@@ -314,6 +314,27 @@ export function suggestNearestSlot(
}
}
// The nearest-anchor fallback only makes sense as due-context when the
// anchor sits within one cadence step of the take. A take recorded before
// the schedule's first minted slot (e.g. history predating startsOn) would
// otherwise pair with a slot days or weeks away and the history row would
// read "due 09:00 (-1904 h)" — meaningless. Cap the fallback at the band's
// own capture reach plus one inter-slot gap; beyond that, no due-context.
if (bestAny && bestSuggest === null) {
const b = bestAny.band;
const reach = b.overdueEnd.getTime() - b.onTimeStart.getTime();
const idx = sorted.indexOf(b);
const gapMs =
sorted.length > 1
? Math.abs(
(sorted[Math.min(idx + 1, sorted.length - 1)].at.getTime() ||
b.at.getTime()) -
(sorted[Math.max(idx - 1, 0)].at.getTime() || b.at.getTime()),
) / Math.max(1, Math.min(idx + 1, sorted.length - 1) - Math.max(idx - 1, 0))
: reach;
if (bestAny.dist > reach + gapMs) return null;
}
const pick = bestSuggest ?? bestAny;
if (!pick) return null;
return {