Zadanie 10: radicale-basecamp-sync

Treść zadania

Request Kuby: stworzyć specyfikację projektu synchronizacja kalendarzy Radicale → Basecamp (przez 5 iteracji audytu, skill spec-iteration/superpowers), opublikować do zatwierdzenia. Zakaz implementacji — tylko specyfikacja.

Pytania Kuby do rozstrzygnięcia w spec:

  1. Czy da się zsyncować wiele kalendarzy Radicale do 1 kalendarza Basecamp?
  2. Czy lepiej mieć wiele kalendarzy w Basecamp?
  3. Inne rozwiązanie? — zaproponować warianty z trade-offs i rekomendacją.

Specyfikacja

Cel

Widzieć eventy z kalendarzy Radicale (self-hosted, https://radicale.parhelium.com/) w Basecamp, z łatwą opcją podglądu wszystkich eventów w jednym miejscu.

Stan zbadany (fakty zweryfikowane 2026-09-03, nie założenia)

Radicale (3.7.8, auth HTTP Basic):

Basecamp (BC3 API + CLI, konto Convertere 6251746, bot [email protected]):

Warianty architektury (odpowiedź na pytania Kuby)

KryteriumA1 (1 projekt BC)A2 (2 projekty: praca/prywatne)B (projekt per kalendarz)C (feed zewn. za auth)
„Wszystko w jednym miejscu"✅ (1 Schedule)⚠️ 2 Schedule❌ rozproszone✅ (własny widok)
Separacja prywatne/służbowe
Koszt utrzymanianajniższyniskiśredni (4–5 projektów)niski, ale trzeci system
Spełnia cel „w Basecamp"
Prywatność (RODO)niskaśredniaśredniawysoka (bez BC)
Rozszerzalnośćzmiana configuzmiana configunowy projekt + bot (człowiek)zmiana configu

Odpowiedzi: (1) TAK — da się wiele kalendarzy do 1 Schedule (zweryfikowane API); (2) wiele kalendarzy w BC = wiele projektów (Schedule 1/projekt) — rozprasza widok; (3) rekomendacja: A2 — dwa projekty („Kalendarze (praca)" + „Kalendarze (prywatne)"), routing target_project per źródło w configu; B odrzucone (rozproszenie, bot nie może tworzyć projektów), C komplementarne (nie zamiennik; wyłącznie za auth — publiczny feed prywatnych eventów wykluczony).

Kierunek synchronizacji

Decyzja Kuby 2026-09-03 (nadpisuje one-way): sync DWUKIERUNKOWY dla wpisów zarządzanych przez sync.

Mechanizm

Failure modes i monitoring (podsumowanie)

7 scenariuszy (Radicale down, BC down, wygaśnięcie tokenu BC ~70 h, DNS, dysk pełny, uszkodzenie state.db, timeout) — każda z zdefiniowanym zachowaniem i odzyskiwaniem; kill w trakcie cyklu bezpieczny. Alert po 3 nieudanych cyklach dwoma kanałami: komentarz BC + lokalny watchdog (niezależny od BC). Heartbeat file do monitoringu.

Bezpieczeństwo i prywatność (RODO)

Kryteria akceptacji wdrożenia (mierzalne)

  1. 100% eventów z 5 aktywnych kalendarzy (6. pominięty: 0 eventów) w oknie [−30;+180 d] w Schedule projektu docelowego wg configu, z poprawnym prefiksem (Radicale vs BC ±0).
  2. Edycja eventy w Radicale widoczna w BC ≤ 20 min.
  3. Usunięcie → trash w BC ≤ 20 min; zero duplikatów po 3 pełnych restartach (w tym kill -9 w trakcie cyklu).
  4. RRULE: poprawne wystąpienia; DST: event 19:15 CET = 18:15 UTC zimą, 19:15 CEST = 17:15 UTC latem; RECURRENCE-ID = osobny wpis; EXDATE = brak wpisu.
  5. Po 7 dniach: zero duplikatów, zero utraconych eventów, 100% cykli OK lub z poprawnym retry.
  6. Sekrety nieobecne w repo/spec/logach publicznych (grep).

Plan wdrożenia (fazy)

Ryzyka (top)

R1 prywatne eventy w BC USA (RODO) — minimizacja + decyzja administratora; R2 wygaśnięcie tokenu BC ~70 h — runbook + 2-kanłowy alarm; R3 rate limit — throttle + Retry-After; R4 masowe usunięcie w źródle — guard 20% + pause; R5 DST/strefy — zoneinfo + testy; R6 bot nie tworzy projektów — Faza 0; R7 kolizja UID — klucz (calendar, UID).

Otwarte pytania (do decyzji Kuby)

  1. A1 czy A2 (rekomendacja: A2)?
  2. Akceptacja R1 (prywatne eventy w Basecamp)?
  3. LOCATION/DESCRIPTION przenoszone? (domyślnie NIE)
  4. Dedykowane konto sync read-only w Radicale?

Status

ZATWIERDZONE przez Kubę 2026-09-03 — decyzja: wariant A1 (wszystkie kalendarze → 1 Schedule), projekt „Rodzinne" bucket 48748049, schedule 10266228493. Prefiksy w tytułach per kalendarz: [N], [Rodzina], [K-Praca], [K], [K-Sport]. R1 zaakceptowane (sync wszystkich, w tym prywatnych). LOCATION: domyślnie NIE. Implementacja w toku wg faz (Faza 0 zrealizowana — projekt utworzony przez Kubę).

Źródła (Pliki)

Źródła (Linki URL)