Context
Audit of the current Google Calendar integration, to decide whether it could replace Google's own Calendar UI as the daily-driver calendar. Short answer: not yet — see gaps and phased plan below.
The plugin spans three repos:
localthought/atomic-plugins (this repo) — mirror of integrations/, including the undeployed integrations/localthought/calendar-proxy.patch. Work on this plan happens here.
ontola/atomic-server — host: LocalThought browser OAuth/PKCE flow, WASM Syncables engine, generic CalendarView UI. See planning/google-calendar-import-gaps.md and docs/imports/google-calendar-gap-report.md there for the existing (thorough) internal audits this plan builds on.
localthought/devonian (platform-lenses/google-calendar/) — the actual lens: projection, recurrence, and two-way edit logic (sync.ts, import.ts, lens/*.ts). Pulled in as a pinned GitHub dependency, not vendored.
A companion service, localthought/integration-proxy, also needs a deploy (see Phase 1).
Current status
Import (read path) — solid.
- Browser-only OAuth/PKCE via LocalThought, no server secrets.
- Bounded date-range import or "keep recurring series" mode; live-verified against a real Google account (2026-09-09: 22 calendars, 32 events).
- All-day vs. timed events projected correctly (exclusive end dates, civil-date handling, DST-safe).
- Recurrence: daily/weekly/monthly/yearly RRULE with INTERVAL/COUNT/UNTIL/BYDAY/BYMONTHDAY/BYMONTH/BYSETPOS/WKST, RDATE/EXDATE, exceptions and cancellations.
- Auto-refresh every 5 min while a tab is open; stable identity across re-imports; local edits and Atomic-only fields preserved.
Two-way sync — implemented but narrow and not live-deployed.
previewCalendarEdits / applyCalendarEdit (devonian sync.ts) cover only 5 fields: name/summary, description, location, start, end — with ETag/If-Match conflict detection, including individual recurring instances.
- No create-event or delete-event path exists at all — only editing fields on events already imported from Google.
- The proxy change required for writes (
calendar.events scope, If-Match CORS forwarding) is an unmerged/undeployed patch (integrations/localthought/calendar-proxy.patch in this repo). Production OAuth scope is still calendar.readonly, so write-back doesn't function yet even though the client code exists.
Missing outright:
- No RSVP/attendee management, no reminders/notifications, no conferencing links usable, no calendar colors, no special event-type handling (birthdays, OOO, focus time, working location).
- No combined multi-calendar agenda/day/week view with per-calendar visibility — each Google calendar becomes its own Atomic table.
- No incremental sync (persisted
syncToken); every refresh is a bounded re-fetch, deletions inferred only from tombstones inside the fetched window.
- No ICS/CSV import or export.
- No event-creation dialog / recurrence-authoring UI (raw table-cell edits and raw JSON only).
Why it can't yet replace Google's Calendar UI
- No event create/delete from Atomic — anything new or canceled must be done in Google's own UI first, then pulled in on next sync.
- Write scope isn't deployed — even editing an existing imported event's time/title doesn't currently reach Google in production.
- No reminders/notifications and no RSVP handling — anything involving other people or alerts still needs Google's app.
Phased plan
Phase 1 — Close the write-back gap (small: mostly deployment)
- Deploy
calendar-proxy.patch to integration-proxy (or land it as a real PR there); flip OAuth scope in production; require existing connections to reconnect.
- Live-verify
applyCalendarEdit against a real account (currently mock-tested only).
Phase 2 — Event creation & deletion (medium — the actual blocker)
- Add
insertEvent / deleteEvent to the devonian lens (Google events.insert / events.delete), mapped from new-row / row-deletion in an imported calendar table.
- Build a proper event editor dialog (title, description, location, start/end, all-day toggle) so projected fields and the outbound payload can't disagree — this is P0 in the existing gap doc too.
Phase 3 — Make it feel like a calendar, not an event table (largest chunk)
- Unified multi-calendar agenda/week/day view with per-calendar color and visibility toggles, built on existing per-calendar identity.
- Reminders/notifications (surface Google's reminder minutes; browser notification at trigger time as a stretch).
- Attendees/RSVP: read-only display first, write-your-own-RSVP as a follow-up.
Phase 4 — Reliability for daily-driver use
- Incremental sync via Google's
syncToken, proper tombstone/deletion handling, recovery from HTTP 410.
- Recurrence editing UI ("this event" / "this and following" / "entire series") — currently raw-JSON only.
Phase 5 — Nice-to-haves
- Special event types (OOO, focus time, working location, birthdays) rendered distinctly instead of as generic meetings.
- ICS import/export as a fallback path independent of the Google API.
Suggested order of work
Phases 1–2 are the minimum to stop needing Google's UI for basic scheduling; Phase 3 is what makes it pleasant enough to actually prefer day to day.
Moved from ontola/atomic-server#1602 — GitHub doesn't support issue transfer across different-owner repos, so that issue was closed with a pointer here.
🤖 Generated with Claude Code
https://claude.ai/code/session_01LjCR6idtt6BFaKCiB3JDnb
Context
Audit of the current Google Calendar integration, to decide whether it could replace Google's own Calendar UI as the daily-driver calendar. Short answer: not yet — see gaps and phased plan below.
The plugin spans three repos:
localthought/atomic-plugins(this repo) — mirror ofintegrations/, including the undeployedintegrations/localthought/calendar-proxy.patch. Work on this plan happens here.ontola/atomic-server— host: LocalThought browser OAuth/PKCE flow, WASM Syncables engine, genericCalendarViewUI. Seeplanning/google-calendar-import-gaps.mdanddocs/imports/google-calendar-gap-report.mdthere for the existing (thorough) internal audits this plan builds on.localthought/devonian(platform-lenses/google-calendar/) — the actual lens: projection, recurrence, and two-way edit logic (sync.ts,import.ts,lens/*.ts). Pulled in as a pinned GitHub dependency, not vendored.A companion service,
localthought/integration-proxy, also needs a deploy (see Phase 1).Current status
Import (read path) — solid.
Two-way sync — implemented but narrow and not live-deployed.
previewCalendarEdits/applyCalendarEdit(devoniansync.ts) cover only 5 fields: name/summary, description, location, start, end — with ETag/If-Matchconflict detection, including individual recurring instances.calendar.eventsscope,If-MatchCORS forwarding) is an unmerged/undeployed patch (integrations/localthought/calendar-proxy.patchin this repo). Production OAuth scope is stillcalendar.readonly, so write-back doesn't function yet even though the client code exists.Missing outright:
syncToken); every refresh is a bounded re-fetch, deletions inferred only from tombstones inside the fetched window.Why it can't yet replace Google's Calendar UI
Phased plan
Phase 1 — Close the write-back gap (small: mostly deployment)
calendar-proxy.patchtointegration-proxy(or land it as a real PR there); flip OAuth scope in production; require existing connections to reconnect.applyCalendarEditagainst a real account (currently mock-tested only).Phase 2 — Event creation & deletion (medium — the actual blocker)
insertEvent/deleteEventto the devonian lens (Googleevents.insert/events.delete), mapped from new-row / row-deletion in an imported calendar table.Phase 3 — Make it feel like a calendar, not an event table (largest chunk)
Phase 4 — Reliability for daily-driver use
syncToken, proper tombstone/deletion handling, recovery from HTTP 410.Phase 5 — Nice-to-haves
Suggested order of work
Phases 1–2 are the minimum to stop needing Google's UI for basic scheduling; Phase 3 is what makes it pleasant enough to actually prefer day to day.
Moved from ontola/atomic-server#1602 — GitHub doesn't support issue transfer across different-owner repos, so that issue was closed with a pointer here.
🤖 Generated with Claude Code
https://claude.ai/code/session_01LjCR6idtt6BFaKCiB3JDnb