- Fix double notifications: remove redundant check_medication_reminders()
call, use adaptive path as primary with basic as fallback
- Fix nag firing immediately: require nag_interval minutes after scheduled
dose time before first nag
- Fix missing schedules: create on-demand if midnight window was missed
- Fix wrong timezone: use user_now_for() instead of request-context
user_now() in calculate_adjusted_times()
- Fix immutable schedules: recalculate pending schedules on wake event
detection so adaptive timing actually adapts
- Fix take/skip not updating schedule: API endpoints now call
mark_med_taken/skipped so nags stop after logging a dose
- Fix skipped doses still triggering reminders: check both taken and
skipped in adaptive reminder and log queries
- Update README with tasks, AI step generation, auth refresh tokens,
knowledge base improvements, and current architecture
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Multi-med creation form: add any number of medication cards in one session, each with independent name/dosage/unit/frequency/times settings
- Submit button labels dynamically (Add 1 / Add N Medications)
- Removed all schedule conflict checking — medications can now coexist at the same time slot as each other and as routines
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB: tasks table with scheduled_datetime, reminder_minutes_before, advance_notified, status
- API: CRUD routes GET/POST /api/tasks, PATCH/DELETE /api/tasks/<id>
- Scheduler: check_task_reminders() fires advance + at-time notifications, tracks advance_notified to prevent double-fire
- Bot: handle_task() with add/list/done/cancel/delete actions + datetime resolution helper
- AI: task interaction type + examples added to command_parser
- Web: task list page with overdue/notified color coding + new task form with datetime-local picker
- Nav: replaced Templates with Tasks in bottom nav
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Users can now describe a goal and have AI auto-generate 4-7 ADHD-friendly
steps, which they can review and modify before saving.
- ai/ai_config.json: Add step_generator prompt and ai_compose examples
to command_parser so bot recognises vague task descriptions
- api/routes/ai.py: New POST /api/ai/generate-steps endpoint — calls
LLM via ai_parser, validates and sanitises returned steps
- api/main.py: Register new ai_routes module
- bot/commands/routines.py: Add ai_compose action — generates steps,
shows numbered list with durations, uses existing yes/no confirm flow
- synculous-client/src/lib/api.ts: Add api.ai.generateSteps(goal)
- synculous-client/src/app/dashboard/routines/new/page.tsx: Add
Generate with AI panel with collapsible textarea, loading spinner,
and inline error; generated steps slot into existing editable list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix TIME object vs string comparison in scheduler preventing adaptive med
reminders from ever firing (#12, #6)
- Add frequency filtering to midnight schedule creation for every_n_days meds
- Require start_date and interval_days for every_n_days medications
- Add refresh token support (30-day) to API and bot for persistent sessions (#13)
- Add "trusted device" checkbox to frontend login for long-lived sessions (#7)
- Auto-refresh expired tokens in both bot (apiRequest) and frontend (api.ts)
- Restore bot sessions from cache on restart using refresh tokens
- Duration-aware routine scheduling conflict detection (#11)
- Add conflict check when starting routine sessions against medication times
- Add diagnostic logging to notification delivery channels
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both handlers were building update_data with defaults for every field,
so a partial save (e.g. toggling one toggle) would silently reset all
other settings back to their defaults. Now only fields explicitly
present in the request body are written to the DB.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. config/schema.sql — Added timezone_name VARCHAR(100) to user_preferences table + an ALTER TABLE migration at the bottom for existing DBs.
2. core/tz.py — Rewrote with dual-path timezone support:
- Request context: _get_request_tz() now checks X-Timezone-Name header (IANA) first, falls back to X-Timezone-Offset
- Background jobs: New tz_for_user(user_uuid) and user_now_for(user_uuid) read stored timezone_name from prefs, fall back to numeric offset, then UTC
- All existing function signatures (user_now(), user_today()) preserved for backward compat
3. scheduler/daemon.py — Fixed 3 bugs:
- _user_now_for() now delegates to tz.user_now_for() which uses IANA timezone names (DST-safe)
- check_nagging() — replaced datetime.utcnow() with _user_now_for(user_uuid) so nags evaluate in user's timezone
- poll_callback() — replaced single UTC midnight check with _check_per_user_midnight_schedules() that iterates users and creates daily schedules at their local midnight
4. api/routes/preferences.py — Added "timezone_name" to allowed PUT fields.
5. synculous-client/src/lib/api.ts — Added X-Timezone-Name header to every request + added timezone_name to preferences update type.
6. synculous-client/src/app/dashboard/layout.tsx — Now syncs both timezone_offset and timezone_name (via Intl.DateTimeFormat().resolvedOptions().timeZone) on session start.
- #11: Add validation to prevent simultaneous scheduling of routines and medications
- Added _check_schedule_conflicts() in routines.py
- Added _check_med_schedule_conflicts() in medications.py
- Returns HTTP 409 with descriptive error on conflict
- #10: Fix medication reminders not being sent
- Added call to check_adaptive_medication_reminders() in daemon poll loop
- #9: Fix can't enable adaptive timing
- Added proper error handling and logging in update_adaptive_settings()
- Returns meaningful error message on database failures
- #8: Fix nagging not working
- Added debug logging for missing settings
- Auto-create medication schedules if they don't exist
- Improved error logging (warning -> error)
- Added 'category' column to routine_templates table
- Categorized all 12 templates into: Daily Routines, Getting Things Done, Health & Body, Errands
- Added /api/templates/categories endpoint to list unique categories
- Updated /api/templates to support filtering by category query param
- Redesigned templates page with collapsible accordion sections by category
- Categories are sorted in logical order (Daily → Work → Health → Errands)
- All categories expanded by default for easy browsing
Replaces the flat routine card list with a day-oriented timeline showing
scheduled routines at their time slots, with week strip navigation and
a live "now" indicator. Adds bulk schedules API endpoint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Uses the existing bot token to send DMs to users by their Discord user ID
instead of posting to a channel webhook.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Issue #4 — Skip not clearing med
File: synculous-client/src/app/dashboard/medications/page.tsx Root cause: Skipped medications were routed to the "Upcoming" section (status === 'skipped' in the
upcoming condition), so they appeared as if still pending.
Fix: Removed || status === 'skipped' from the grouping condition. Now skipped meds go to the "Due"
section where they properly render with the "Skipped" label — same pattern as taken meds showing
"Taken".
Issue #5 — "Invested -359m in yourself"
Files: api/routes/routines.py, synculous-client/src/app/dashboard/page.tsx,
synculous-client/src/app/dashboard/stats/page.tsx
Root cause: Session duration was calculated by comparing a naive UTC created_at from PostgreSQL with
the user's local time (after stripping timezone). For users behind UTC (e.g., CST/UTC-6), this
produced negative durations (~-359 minutes ≈ -6 hours offset).
Fix: Added _make_aware_utc() helper that treats naive datetimes as UTC before comparison. Also clamped
durations to max(0, ...) on both backend and frontend formatTime as a safety net.
Issue #6 — Push notifications not working
File: api/routes/notifications.py
Root cause: Subscribing to push notifications created a push_subscriptions row but never set
web_push_enabled: true in the notifications table. The scheduler daemon checks web_push_enabled before
sending, so push notifications were always skipped.
Fix: When subscribing, also call notifications.setNotificationSettings() to enable web_push_enabled.
When unsubscribing (and no subscriptions remain), disable it.
- Add all 14 missing database tables (medications, med_logs, routines, etc.)
- Rewrite medication scheduling: support specific days, every N days, as-needed (PRN)
- Fix taken_times matching: match by created_at date, not scheduled_time string
- Fix adherence calculation: taken / expected doses, not taken / (taken + skipped)
- Add formatSchedule() helper for readable display
- Update client types and API layer
- Rename brilli-ins-client → synculous-client
- Make client PWA: add manifest, service worker, icons
- Bind dev server to 0.0.0.0 for network access
- Fix SVG icon bugs in Icons.tsx
- Add .dockerignore for client npm caching
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>