This article explains the Facility view (the view's own heading reads Upkeep): what it covers, how an item's status is computed from its Next due date, what recording a vendor appointment does and does not change, how the daily nag behaves, and what the incl. market estimates marker on the cost strip means. It is the reference for the Guide link on the view.
What it covers
Maintenance, compliance, insurance, registration, licences and vendor items — business and personal (the van, the Lexus and the café alike). Items live in Supabase (maintenance_items; completions in maintenance_log) and are served by pepper-ui/backend/maintenance-data.js. The as of {date} stamp is the server's Chicago date, which every status below is measured against. Pressing / anywhere in the view focuses the search box, which searches every item regardless of the category tab or list/timeline mode.
Status — pure date math against Next due
| Status | Rule |
|---|---|
| Inactive | active is off |
| Not scheduled | no Next due date |
| Overdue | Next due is before today |
| Due soon | Next due is within the item's lead time (lead_time_days, default 14) |
| Not due yet | everything else |
Status says nothing about whether a vendor has been contacted. That is tracked separately, below. The relative label beside the date reads Nd overdue, today, in Nd up to 60 days, in N mo up to 550 days, then years.
Next due vs. Vendor appointment booked for
These are two different facts and the console keeps them apart on purpose.
| Next due | Vendor appointment booked for | |
|---|---|---|
| Means | When the work is due | The real date a vendor has been booked to do it |
| Drives status | Yes — overdue / due soon | No — an overdue item stays overdue while booked |
| Drives the nag | Yes | Pauses the Telegram nag until that date |
| Shown as | Date and relative label on the row | Booked {date}; Was booked {date} once the date has passed |
The norm: set the appointment only once you have actually contacted a vendor and have a real date. It is not a way to silence an item — booking does not erase the risk, it tells the daily nag you are on it until that date. If the appointment date passes without the item being marked done, the booking no longer excuses it and the nag resumes with a note that the appointment looks missed. Clear appointment removes the date without touching anything else.
Done is the only thing that moves an item forward: it logs the completion (date, cost, vendor), sets Last completed to today, rolls Next due forward by the item's interval in months (or clears it if the item has no interval), and clears any appointment — the booking was a means to this end, not a fact to keep.
The daily nag
scripts/maintenance-alerts.mjs, launchd job com.blfo.maintenance-alerts, daily 8 am CDT on the Mini. No model in the loop. It looks at active items whose Next due is within lead time or already past, and works two channels with different jobs:
- Todoist — the record. One task per occurrence, deduplicated on
alerted_next_due: created when the item enters due-soon, or immediately if it slipped straight to overdue. Due today; priority 4 when overdue, 3 when due soon. Todoist shows it red on its own; this channel does not repeat. - Telegram — the nag. Fires every run for as long as an item stays overdue: immediately for the high-stakes categories (compliance, licence, insurance, registration in the script's set), and once an item has been ignored 3 or more days for everything else. It holds off while a vendor appointment is booked for today or later, resumes if that date passes, and stops only when the item is marked done in the Facility tab.
Cost roll-up and incl. market estimates
- Annual cost per category = cost per occurrence × (12 ÷ interval months); an item with no interval counts its cost once. The strip and legend show each category's annualised share.
- Next 12 months = the plain cost of every item due within the next 365 days, not annualised.
- incl. market estimates appears whenever any item's
sourceisestimate: its cost is a market figure entered when the item was seeded, not an invoice. Replace it with the actual as invoices arrive — Done accepts the real cost and vendor, and Edit can correct the item's cost directly. [check: which items currently carrysource = 'estimate'— querymaintenance_items; the seed scripts in the repo mark their rowsclaude-code, so the estimate rows were set elsewhere]