Expiry writes without broadcasting¶
expire_past_schedules is the one operation in items/services.py that changes Items and
emits nothing. Every other operation broadcasts on the "items" channel; this one is
deliberately silent, and the wishlist read no longer calls it at all.
ADR 0002 makes the broadcast mandatory because a mutation that doesn't emit leaves every
client stale. Expiry does not have that property. The calendar filters
scheduled_date >= today, so an Item dated yesterday is already hidden whether or not its
stored Status has been rewritten; the wishlist filters status = OPEN, which expiry can
only move Items away from, never into. No client's screen depends on the sweep having run.
The sweep's only caller is _scheduled_items_queryset, a read path. With the emit in place,
one person opening the calendar wrote rows, broadcast to every connected client, and made
each of them re-fetch a list that could not have changed — a round trip on both phones for a
change neither could see. The sweep now runs for stored-Status correctness only.
Consequences¶
- Expiry keeps the storage honest but is not a user-visible transition. Anything that displays Done Items — a history view, a "past events" tab — cannot assume the sweep is current, because it only runs when somebody opens the calendar.
- Nothing performs expiry on a schedule. If the calendar goes unvisited, past Scheduled rows keep their Status until the next visit. Giving Expire a real performer (a management command on a timer) is tracked in #1; at that point this ADR's reasoning holds unchanged — a timer has even less business broadcasting than a read does.
_wishlist_querysetdeliberately does not sweep. Adding the call back would be pure cost: it cannot affect that query's result.