Devlog #2
Table of Contents
Devlog — 18 May 2026#
TL;DR — The web app shipped in a very beta version. Android got real-time timer broadcasting via push notifications and a stats screen overhaul. Big week on both sides.
User-facing features#
Kombine on the web. Full web client, built and shipped this week. Task list with inline status toggles, create/edit/delete, grouping by priority, energy level, Eisenhower matrix, and date. Pomodoro and Flow timer modes, activity and task pickers, break configuration. Calendar with week and day views. Statistics page with focus time, streaks, activity breakdown, and a year grid. Settings and account management. It all lives in the same binary as the API — no separate frontend deployment.
Real-time timer sync, everywhere. Start a timer on your phone, it shows up on the web within seconds (and vice versa). Push notifications now carry live timer state — the app receives the broadcast and applies it immediately. No manual refresh needed.
Force-sync on demand. When the backend tells a device to sync (via push or WebSocket), the app now fetches tasks, sessions, and activities in a single call rather than per-entity round-trips. Keeps things consistent after cross-device activity.
Web push notifications. Task reminders and daily summaries delivered to the browser. Works independently of whether the web tab is open.
Stats screen redesign (Android). Compact layout with a period toggle (week/month/year), a cleaner hero card for focus time and sessions, dual-streak display, and the year heatmap and hour chart restyled in Kombine orange. Old sprawling layout is gone.
Change display name. Available from account settings. Finally.
Sound picker as a bottom sheet. The alert dialog for picking timer sounds is now a bottom sheet — less modal, easier to browse on tall phones.
Technical improvements#
Server-side outbox. Timer state broadcasts (and sync signals) now go through a persistent outbox before delivery. If the server restarts mid-session, nothing drops — events are replayed from the database. Background worker polls and prunes entries older than 30 days.
Resilient event handlers. All event handlers on Android now auto-recover from exceptions: a failure triggers a short delay and automatic resubscription. Previously, an unhandled exception in a handler coroutine would kill that handler permanently — silently breaking whatever feature it backed. Fixed for all 15 handlers at once.
Encrypted sync session storage. Sync session IDs on both phone and watch now use AES-256-GCM encrypted storage, consistent with how auth tokens are handled. Small change, right thing to do.
CSRF protection on the web layer. All state-changing web requests now require a CSRF token. Login rate-limited to slow credential stuffing attempts. Timer JS fetch calls also covered.
FCM reliability. Push registration retries on failure and re-registers when the app comes back to the foreground. Previously a failed registration on first launch would leave the device unreachable until the next install.
Dead letter monitoring. Event handler failures that exhaust retries are now reported to Sentry with full context. Before, they’d fail silently and leave the app in an inconsistent state.
Code coverage in CI. Kover now runs on every PR and publishes a coverage report to the job summary. Baseline to build from.
Watch↔phone sync integration tests. The Wearable Data Layer sync path now has end-to-end integration tests. Previously it was tested manually.
R8 obfuscation guard for sync fields. Sync payload field names were being renamed by the minifier in release builds, causing silent deserialization failures on the backend. ProGuard rules added to keep them.