'One month later' has no single answer on the calendar's ragged edge, so engines clamp (Jan 31 -> Feb 28) — reasonable, silent, and lossy: the clamped date forgets it was ever the 31st, so iterating month-hops walks anniversary dates permanently down to the 28th, while a single jump preserves them. Every subscription renewal and billing anchor lives on this edge.
SELECT strftime(d + INTERVAL 1 MONTH + INTERVAL 1 MONTH, '%Y-%m-%d') AS renewal FROM anchor;
SELECT strftime(d + INTERVAL 2 MONTH, '%Y-%m-%d') AS renewal FROM anchor;
The subscription anchored Jan 31 decayed to the 28th — adding a month clamps Jan 31 to Feb 28, and the clamp is lossy, so hopping month by month walks the date permanently down; a single plus-two-months from the anchor keeps Mar 31.
Compute each occurrence from the original anchor (LEAST(anchor_day, days_in_month)); spell month-end intent as month-end (last_day()).