-
Notifications
You must be signed in to change notification settings - Fork 0
Habits
A habit is a recurring soft block — Lunch, Decompress, Standup buffer — that the scheduler maintains on a target calendar near a preferred time, drifting only as far as you allow.
| Field | Meaning |
|---|---|
| Title | Becomes the event summary. |
| Target calendar | Where occurrences are written. |
| Duration | Minutes per occurrence. |
| Ideal time |
HH:MM in the target account's hours timezone. |
| Flex | How many minutes the scheduler may drift from ideal. |
| Days of week | Subset of mon/tue/wed/thu/fri/sat/sun. |
| Hours window |
working / personal / meeting — which account hours apply. |
| Category | Optional pin; otherwise the auto-categorizer decides. |
| Horizon (days) | How far into the future occurrences are maintained. |
| Enabled | Disabled habits aren't placed. |
The scheduler walks each day in [today, today + horizon). For days
matching a days_of_week entry it pulls a per-day freebusy across
every enabled calendar on every connected account — not just the
target — applies each calendar's own buffer padding, and calls
hours.NearestFitSlot(duration, flex, ideal).
Habits share the placement path with Tasks, so the same caveats apply — most importantly that placement fails closed when an account's freebusy can't be fetched.
- If a slot fits within
±flexofideal, an occurrence is created (or moved if it already existed for that date). - If no slot fits, any stale occurrence on that day is removed. The next recompute will retry.
Each occurrence is stored in habit_occurrence with a unique
(habit_id, occurs_on) constraint, so the scheduler is idempotent.
- Saving a new or edited habit (future occurrences are wiped first so the scheduler rebuilds under the new rules).
- The Recompute button on the habits list.
- The maintenance tick, every 6 hours. It re-runs placement for every enabled habit so the rolling horizon stays current as days fall off the back of it.
- Title
Lunch, duration 60m, ideal12:00, flex 90m, weekdays, hours windowpersonal, horizon 14 days.
Shipped — you don't need a habit for this. The buffer engine writes a visible "Decompress" event after every non-managed meeting with two or more human attendees, keeps it in step when the meeting moves, and reaps it when the meeting is cancelled. Configure the length at Settings → Buffers; see Buffers.
- Title
Standup, duration 15m, ideal09:30, flex 30m, weekdays, hours windowworking. Even on busy days you'll keep the slot.
Every placement / reschedule / drop lands in the audit log with
kind="habit".
- Tasks — one-shot scheduled blocks
- Hours — Working/Personal/Meeting windows
- Buffers — padding between scheduled blocks
-
AI Assistant —
create_habit,update_habit,delete_habit