Files
calendula/.planning/REQUIREMENTS.md
Jean-Luc Makiola f0e2e12939 feat(edit): event editing — shared form, scoped recurring writes, recurrence picker (v1.3)
The create form (v1.2) now edits: a pencil on the detail screen (writable
calendars only, contextual WRITE upgrade like delete) opens it prefilled via
EventDetail.toEditForm; populated sections always show, the calendar is
fixed, and a dirty-check writes only changed columns (pristine saves are
no-ops). Saving a dirty recurring event parks in SaveUiState.AwaitingScope
and asks how far the change reaches (Google model): "only this event" =
modified-occurrence exception via CONTENT_EXCEPTION_URI (empty optionals as
explicit NULLs since the provider clones the parent row), "this and all
following" = series split (insert new event first, then truncate), "all
events" = series-row update with the time delta applied to the series
DTSTART. A changed rule drops the exception option. Delete gained the same
middle scope.

Recurrence: EventForm.rrule + SimpleRecurrence (FREQ/INTERVAL/UNTIL/COUNT +
weekly BYDAY with locale-ordered weekday toggles) behind a picker on create
and edit; unrepresentable rules render humanized (shared ui/common
RecurrenceText) and survive verbatim. UNTIL validation flags rules ending
before the event starts.

Provider lessons baked in (verified on-device via adb probes): instance
caches regenerate only from an update's own values, so truncation sends the
full time-column set (truncateSeries) — RRULE-only updates left a stale
duplicate occurrence on the split day; UNTIL is written as the local end of
day in UTC (toRRule(zone), previousLocalDayEndUtcMillis) so UTC+x zones
can't leak an extra day. Reminder edits reconcile against actual provider
rows, keeping untouched rows' methods.

Tests: RecurrenceTest (parse/render/round-trip, truncation), update/exception
mapper paths, repository pass-throughs, prefill + populatedFields, raw-title
mapper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 20:57:32 +02:00

1.8 KiB

Calendula — Requirements

See full design spec: docs/superpowers/specs/2026-06-08-calendar-app-design.md

V1 Scope (Variant "B")

Validated (shipped)

  • Foundation & CI infrastructure — v0.1.0 (2026-06-08)

Active (V1)

  • Foundation & CI infrastructure
  • Data Layer over CalendarContract
  • Permission flow (READ_CALENDAR)
  • Month view (S1)
  • Week view (S2)
  • Day view (S3)
  • Event Detail Sheet (S4)
  • Multi-Calendar Filter (M3)
  • Today button (M2) — shipped v0.5; Jump-to-Date cut from scope
  • View-Switcher (M1)
  • Settings screen (M4)
  • Empty / no-permission / no-calendars states
  • German + English localization
  • Loading/Failure/Success states per screen (architectural pattern)

Out of Scope (V2+)

  • Event create / edit / delete (V2)
  • Home-screen widget
  • Full-text search
  • Quick-add
  • Custom notifications/reminders (system already handles these)reversed: Calendula targets sole-calendar-app users, so no other app posts reminder notifications. We post them ourselves (Etar model). Planned for v1.4 — see ROADMAP.md.
  • Tablet/foldable-specific layouts
  • iOS support (Android-only by design)

Constraints

  • Tech stack: Kotlin + Jetpack Compose + Material 3 Expressive, Hilt, DataStore
  • Tech stack pin: Hilt 2.59.2 + KSP 2.3.9; Kotlin 2.3.21 (KSP for Kotlin 2.4.0 not released yet). Material 3 pinned to 1.5.0-alpha21 (Expressive APIs only exist in alpha). Re-evaluate when KSP/Material3 stable land.
  • Platform: Android 10+ (API 29 minimum), Android 16 (API 36) target
  • Offline-first: all data lives in CalendarContract; no app-side network
  • Privacy: zero telemetry, no analytics
  • i18n: German + English from day one
  • Tests + CI from day one
  • License: MIT