-
Notifications
You must be signed in to change notification settings - Fork 0
Smart Blocks
A smart block automatically maintains placeholder events on a target calendar that represent your available (or busy-from-another-source) time, refreshed whenever the source calendars change.
The classic use case: you want your work calendar to show "Focus" blocks during every empty slot in your working hours, so coworkers can see when you're protecting deep-work time.
| Field | Meaning |
|---|---|
| Name | Free-form label |
| Target calendar | Where the focus blocks are written |
| Source calendars | Busy time read from these (multi-select) |
| Time zone | IANA timezone name (America/Chicago, Europe/Berlin, etc.) |
| Per-weekday hours | List of HH:MM-HH:MM ranges per weekday |
| Horizon (days) | How far into the future to maintain blocks |
| Min block (min) | Drop windows shorter than this |
| Merge gap (min) | Merge windows separated by ≤ this many minutes |
| Block title | The summary on the generated event (e.g. Focus) |
| Enabled | Disabled blocks aren't recomputed |
Every time a source calendar changes (or every 5 min from the polling fallback), the smart-block engine debounces by 15s and then runs:
- Build the list of working windows from your per-weekday hours,
bounded to
[now, now + horizon]. - Pull busy windows from each source calendar via Google's
Freebusy.query(DST-correct, in your specified timezone). - Subtract busy windows from working windows → free windows.
- Merge any free windows separated by ≤ merge-gap minutes.
- Drop free windows shorter than min-block minutes.
- Diff the result against the existing managed blocks for this
smart_block:
- Overlap with an existing block → update its window in-place (preserves the Google event ID).
- No match → insert a new block.
- Existing block with no match → delete it.
- Every write carries
extendedProperties.private.skulidManaged=1so it doesn't trigger sync rules and is recognizable as ours.
The engine uses Go's time.LoadLocation for the IANA name you provide.
That means working hours like 09:00-17:00 always mean 9am to 5pm
local clock time, even across daylight-saving transitions. The
duration of a window will differ by an hour on the spring-forward and
fall-back days.
- Target: your work calendar
- Sources: same work calendar (so personal events on it count as busy), plus your personal calendar (so personal events block focus too)
- Time zone: yours
- Hours:
09:00-12:00,13:00-17:00Mon-Fri - Horizon: 30 days
- Min block: 30 minutes
- Merge gap: 15 minutes
- Title:
Focus
- Target: shared calendar
- Sources: your work + personal calendars
- Hours:
18:00-22:00every day - Min block: 60 minutes
- Title:
Free
This isn't really a smart-block use case — for a recurring static event, just create it normally in Google Calendar.
Hit Recompute on the row to force a full pass right now. Useful if you just changed the working hours or want to verify a change. Settings → Re-register all webhooks is for a different problem (see Operations).
Every block create/update/delete lands in the audit log with
kind="smart_block", the smart_block ID, and the action.
- The engine writes one event per free window; there is no "batched" block representation. Very fragmented calendars produce many tiny events (which is why min block exists).
- Working hours are weekday-keyed. Holidays aren't auto-detected — if you want a holiday to be treated as a non-working day, just decline the all-day holiday event from your sources.
- The engine doesn't yet support negative-availability (e.g. "only show blocks during certain hours"). It's working hours minus busy.