- invoice_overdue scan: monthly bucket → d7/d15/d30 milestones resolved via
resolveSchedule (dated config, rule 3); each fires once via the idempotency key
- F7 catch-up guard: scan gaps and the monthly→dN cutover enqueue only the
highest crossed milestone, never the whole ladder
- ctx.daysOverdue populated in reminderContext (A7: anchored on doc_date) so the
'N day(s) past due' template text renders; injectable today for determinism
- daysBetweenIso helper; 5 tests; suite green
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>