Recurrence¶
The core stores concrete, dated slots. Recurrence - repeating availability
("every Tuesday 10:00-11:00") - is an optional layer that materialises
those concrete slots, shipped separately as booking_recur.
Why materialise rather than store a rule¶
Bookings attach to concrete slots, and the BookingManager is oblivious to a
slot's origin. So recurrence adds cleanly with zero core/schema change
beyond the single nullable Slot.rule field already provisioned.
flowchart LR
RULE[RecurrenceRule<br/>booking_recur] -->|generator + cron| GEN[Materialise slots]
GEN --> S1[Slot 2026-07-07]
GEN --> S2[Slot 2026-07-14]
GEN --> S3[Slot 2026-07-21]
S1 --> C1[categories + bookings]
S2 --> C2[categories + bookings]
Note[Slot.rule = rule id] -.-> S1
Bonus of materialising versus storing a pure RRULE:
- Per-occurrence overrides are trivial - skip a date or bump one day's capacity by editing that single slot.
- Open-ended recurrence is handled by a rolling-horizon cron that keeps a window of future slots materialised (the Smart Date Recur approach). The engine only ever sees concrete slots.
What booking_recur will add¶
- A
RecurrenceRuleentity (RRULE + a default category template + horizon). - A generator service + cron that creates/updates slots and stamps
Slot.rulewith the originating rule id. - A policy for already-booked generated slots when a rule changes - the one decision recurrence defers, and it lives entirely in the submodule.
Already provisioned
Slot.rule (nullable string) exists in the core today purely so this layer
can attach later without a schema update.