You tell AutoHomi when you last changed the HVAC filter. From that single date, the next reminder falls out automatically. Filter changed April 1, and the next reminder fires 90 days later. Filter changed April 20 instead, and the next reminder slides to the 90-day mark from there. The math is small. The behavior it produces is the entire point of the product.
A fixed cycle is convenient when nobody ever slips. The day you miss by one, the next reminder is off by one. The day you slip two weeks because you were on vacation, the next reminder is also off two weeks. Drift compounds. If you check the filter "every 90 days" the way it sounds on paper, the actual interval drifts further from the recommendation every cycle. AutoHomi anchors the next reminder to when you actually finished the job, so the schedule stays honest regardless of how late the previous one ran.
Each maintenance row in the template library carries either a frequency in days or a calendar anchor. A 90-day HVAC filter carries `every_n_days`. A fall gutter cleaning carries `month_day` paired with a specific date. Inside `applyTemplate`, an `every_n_days` row lands at `lastDoneAt + frequencyDays`, which is 90 days from the date you reported. A `month_day` row lands at `nextCalendarOccurrence(lastDoneAt, monthDay, tz)`, scheduled at 09:00 local in your timezone. Same function handles both cadences. The template stays the source of truth. The user-facing date keeps recomputing against the new anchor.
The dashboard banner and the step-2 wizard both POST a batch of `{templateKey, lastServicedAt}` answers to `/api/maintenance/last-serviced`. Inside the route, each touched row is re-anchored through `applyTemplate(..., { lastDoneAt })` so the new date drives the new `dueAt`. Every per-channel dispatch flag (`emailSentAt`, `smsSentAt`, `pushSentAt`) is reset to null so the previous cycle cannot short-circuit the cron against the fresh one. `nextReminderAt` and `nextMileageReminderAt` are recomputed from the new anchor. For mileage-anchored auto rows with a vehicle on file, the mileage prong is rewired to that vehicle's current odometer so the OR-prong stays coherent.
Take a single-family home. The HVAC filter cadence defaults to 90 days; multi-family, condo, townhouse, and manufactured homes default to 60 days because the smaller footprint and shared-system use patterns argue for the tighter pace. You log the change on April 1. The `dueAt` lands 90 days out, and the helper nudges that candidate forward into an upcoming peak window so the reminder fires ahead of heavy runtime rather than after. Then the actual filter swap runs late, on April 20. You tap Update Last Serviced with April 20 as the date, and `dueAt` slides to the completion date plus 90 days, not the original April 1 plus 90 days. Without the re-anchor, your next reminder would still have fired at the original due date even though no new filter was in place to remind you about. The single-slip absorption is why the system is built around a last-serviced anchor instead of a rigid 60–90 day sticker on the calendar.
Anchoring to the date the work actually ran keeps the reminder schedule honest across slipping cycles, calendar chores, and the odd road trip. The whole product is a thin read-and-write layer over the same idea. Tell us when. We will tell you when next.