Files
Synculous-2/README.md
chelsea e89656a87c Fix adaptive medication timing and update README
- 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>
2026-02-19 23:34:38 -06:00

22 KiB

Synculous

A comprehensive routine and medication management app designed as a prosthetic for executive function. Built for people with ADHD.

The app externalizes the things ADHD impairs — time awareness, sequence memory, task initiation, and emotional regulation around failure — into a guided, sequential interface with immediate feedback and zero shame. It combines structured routines, intelligent medication tracking, AI-powered safety systems, and peer accountability features into one unified platform.

Architecture

synculous/
├── synculous-client/           # Next.js frontend (React, Tailwind)
├── api/                        # Flask REST API
│   ├── main.py                 # App entry point, auth routes
│   └── routes/                 # Domain route modules
│       ├── routines.py             # Routines CRUD + sessions
│       ├── routine_sessions_extended.py  # Pause, resume, abort, notes
│       ├── routine_stats.py        # Completion stats, streaks, weekly summary
│       ├── routine_templates.py    # Premade routine templates
│       ├── routine_steps_extended.py     # Step instructions, types, media
│       ├── routine_tags.py         # Tagging system
│       ├── medications.py          # Medication scheduling + adherence
│       ├── adaptive_meds.py        # Adaptive medication timing (learning)
│       ├── tasks.py                # One-off tasks/appointments CRUD
│       ├── ai.py                   # AI-powered step generation
│       ├── preferences.py          # User settings + timezone
│       ├── notifications.py        # Web push subscriptions
│       ├── rewards.py              # Variable reward system
│       ├── victories.py            # Achievement detection
│       └── snitch.py               # Peer accountability contacts + notifications
├── core/                       # Shared business logic
│   ├── postgres.py             # Generic PostgreSQL CRUD
│   ├── auth.py                 # JWT + bcrypt authentication
│   ├── users.py                # User management
│   ├── routines.py             # Routine/session/streak logic
│   ├── stats.py                # Statistics calculations (completion rates, streaks)
│   ├── snitch.py               # Snitch trigger logic + notification delivery
│   ├── adaptive_meds.py        # Adaptive medication timing logic
│   ├── tz.py                   # Timezone-aware date/time helpers (IANA + offset)
│   └── notifications.py        # Multi-channel notifications
├── scheduler/
│   └── daemon.py               # Background polling for reminders
├── bot/                        # Discord bot with knowledge RAG
│   ├── bot.py                  # Bot entry point + session management
│   ├── command_registry.py     # Module-based command routing
│   ├── commands/               # Command modules
│   │   ├── routines.py         # /routine commands
│   │   ├── medications.py      # /med, /take, /skip commands
│   │   ├── tasks.py            # /task commands (one-off tasks/appointments)
│   │   └── knowledge.py        # /ask command (jury-filtered RAG)
│   └── hooks.py                # Event listeners
├── ai/                         # LLM-powered features
│   ├── parser.py               # OpenRouter API client
│   ├── jury_council.py         # 5-juror safety filtration system
│   ├── ai_config.json          # Model + prompt configuration
│   └── (optional) RAG embeddings
├── config/
│   ├── schema.sql              # Database schema
│   ├── seed_templates.sql      # 12 premade ADHD-designed routine templates
│   ├── seed_rewards.sql        # Variable reward pool
│   └── .env.example            # Environment template
├── diagrams/                   # Architecture diagrams (Mermaid)
├── docker-compose.yml
├── Dockerfile
└── tests/

Quick Start

# Copy environment config
cp config/.env.example config/.env

# Edit with your values (at minimum: DB_PASS, JWT_SECRET, optionally DISCORD_BOT_TOKEN, OPENROUTER_API_KEY)
nano config/.env

# Start everything (db, api, scheduler, optional bot, frontend client)
docker-compose up

This starts five services:

Service Port Description
db 5432 PostgreSQL 16 with schema + seed data
app 8010 Flask API (internal: 5000)
scheduler Background daemon for medication/routine reminders
bot Discord bot with commands and knowledge RAG (optional, needs DISCORD_BOT_TOKEN)
client 3001 Next.js frontend (internal: 3000)

Features

Routines & Sessions

  • Create routines with ordered steps (4-7 steps recommended)
  • Run sessions with a guided, one-step-at-a-time focus interface
  • Complete, skip, pause, resume, or cancel sessions
  • Swipe gestures for step completion on mobile
  • Per-step timing with visual countdown
  • Animated celebration screen on completion with streak stats and variable rewards
  • Every-N-day frequency option for routines
  • Tagging system for organizing routines by category
  • Session notes for logging context or blockers

Premade Templates

12 ADHD-designed templates ship out of the box, seeded from config/seed_templates.sql:

Template What it's for
Morning Launch Getting from bed to ready. First step: just sit up.
Leaving the House The keys-wallet-phone checklist.
Focus Sprint One focused work block with environment setup first.
Wind Down Screen-first sleep transition.
Quick Tidy Fast sweep, not deep cleaning. Completable on bad days.
Body Reset Minimum viable hygiene. Zero judgment.
Unstuck For executive function paralysis. Pure two-minute-rule.
Evening Reset Set tomorrow up to be easier.
Move Your Body Not a workout. Just movement. Starts with shoes.
Sunday Reset Weekly prep so Monday doesn't ambush you.
Cook a Meal One meal, start to finish, low activation energy.
Errand Run Externalized planning + sequencing for errands.

All templates follow the design framework: two-minute-rule entry points, concrete instructions, zero-shame language, 4-6 steps max.

One-Off Tasks & Appointments

  • Create standalone tasks and appointments outside of routines
  • Scheduled date/time with optional reminders
  • Quick-complete action for fast check-off
  • Tasks appear on the routines timeline for a unified daily view
  • AI-powered task composition via bot and web client
  • Natural language date parsing in bot commands

AI-Powered Step Generation

  • Generate ADHD-friendly routine steps from a plain-language goal description
  • Uses OpenRouter LLM to produce 2-minute-rule-compliant steps
  • Each step includes name and estimated duration
  • Available via API endpoint and integrated into the web client's routine creation flow

Medications

  • Scheduling: daily, twice daily, specific days, every N days, as-needed (PRN); batch-schedule multiple meds at once
  • "Today's meds" view with cross-midnight lookahead (late night + early morning)
  • Take, skip, snooze actions with logging
  • Adherence tracking and statistics
  • Refill tracking with low-quantity alerts
  • Background reminders via the scheduler daemon
  • Medication editing (update name, dose, schedule, refill count)
  • Web push and Discord notifications for doses

Adaptive Medication Timing

  • Machine-learning-based timing predictions based on user adherence patterns
  • System learns when you're most likely to take meds
  • Automatic reminder optimization (fewer false-positive reminders)
  • Override and manual timing adjustments always available
  • Useful for people with irregular schedules

Peer Accountability: "Snitch" Feature

  • Designate trusted contacts to receive medication adherence notifications
  • Contacts don't need an account or login
  • Granular privacy controls: users choose what to share (meds, streaks, notes)
  • Contacts receive weekly summaries or real-time alerts for missed doses
  • Based on research showing peer accountability improves adherence
  • Optional consent-based consent flow for contact approvals

Streaks and Stats

  • Per-routine streak tracking (current + longest)
  • Weekly summary across all routines
  • Completion rate and average duration stats
  • Victory detection (comebacks, weekend completions, variety, consistency)
  • Milestone celebrations at 3, 7, 14, 21, 30, 60, 90, 100, 365 days

Rewards

  • Variable reward pool seeded from config/seed_rewards.sql
  • Random reward on routine completion (post-completion only, never mid-routine)
  • Reward history tracking per user
  • Common and rare rarity tiers
  • Designed to leverage variable-ratio reinforcement schedules

Notifications

  • Web push notifications via VAPID (in-browser)
  • Discord webhooks (for bot mentions)
  • Discord DMs (for sensitive notifications like "snitch" alerts)
  • ntfy support (for self-hosted push)
  • Timezone-aware scheduled reminders for medications and routines

Timezone Support (Dual-Path)

All date/time operations respect the user's local timezone via two mechanisms:

In request context:

  • The frontend sends X-Timezone-Name (IANA standard, e.g., "America/New_York") or X-Timezone-Offset header
  • Handlers use these headers for real-time API operations

In background jobs (scheduler daemon):

  • Timezone is stored in user_preferences.timezone_name (IANA format)
  • Scheduler retrieves stored timezone for each user
  • Falls back to numeric offset, then UTC if name is unavailable
  • Enables accurate reminder delivery even if user's browser context is offline

Result: Streaks, "today's meds," weekly stats, and reminders all use the user's local date.

Authentication & Session Persistence

  • JWT access tokens (1-hour expiry) + optional long-lived refresh tokens (30 days)
  • "Trust this device" option on login issues a refresh token for seamless re-auth
  • Bot session caching with persistent pickle storage across restarts

User Preferences

  • Sound effects (default off — habituation risk)
  • Haptic feedback (default on)
  • Launch screen toggle
  • Celebration style
  • Timezone (IANA name + numeric offset, auto-synced from browser)
  • Discord presence indicator toggle (shows online/offline in Discord)

Discord Bot Integration

Full-featured Discord bot for managing routines and medications without opening the app:

Bot Commands

  • /routine list — List all routines
  • /routine start <name> — Start a routine session (guided steps in Discord thread)
  • /routine stats <name> — View streak and completion stats
  • /med today — Show today's medications with status
  • /med take <med_name> — Log a dose as taken
  • /med skip <med_name> — Log a dose as skipped
  • /task add <description> — Create a one-off task (supports natural language dates)
  • /task list — Show upcoming tasks
  • /task done <name> — Mark a task as complete
  • /ask <question> — Query the knowledge base with jury-filtered safety checks
  • Discord presence automatically shows your routine/meditation status

Jury Council Safety System

The /ask command uses a sophisticated 5-juror safety filtration pipeline:

Two-stage process:

  1. Question Generator (Qwen3 Nitro, fallback to Qwen3-235B): Expands user query into 2-3 precise search questions
  2. Jury Council (5 parallel jurors, 100% consensus required): Each juror evaluates questions from a distinct safety lens

Juror Roles:

  • Safety: Would answering cause harm? Evaluates crisis risk (C-SSRS framework), self-harm methods, lethal means
  • Empathy: Is this emotionally appropriate for someone in distress? Checks for deceptive empathy, harmful validation, stigmatizing language
  • Intent: Is this benign? Detects jailbreaks, social engineering, prompt injection, method-seeking disguised as education
  • Clarity: Is the question retrievable? Checks if the question is specific enough to get meaningful results from the knowledge base
  • Ethics: Within bounds for an informational AI? Blocks diagnosis, treatment planning, medication advice, scope violations, deceptive role-play

Safety model: Questions only approved if ALL 5 jurors vote yes. Any juror error = fail closed. Crisis indicators trigger immediate resource redirection (988, Crisis Text Line, etc.) instead of RAG answers.

This system makes it possible to serve help-seeking users asking about self-harm coping strategies, suicidal ideation management, and DBT skills while firmly rejecting harmful intent (method-seeking, glorification, extraction attempts).

Knowledge Base & RAG

  • Embeddings-based retrieval of ADHD, DBT, and mental health educational content
  • Multi-book support with user-selectable knowledge bases
  • Jury-filtered questions for safety
  • LLM-powered intent classification routes general questions to the knowledge base automatically
  • Multi-query retrieval with deduplication for better coverage
  • DBT advice evaluation mode (checks advice against DBT principles)
  • Discord bot /ask command uses RAG with jury council checks
  • Extensible knowledge source (can add more documents)

API Overview

All endpoints require Authorization: Bearer <token> except /api/register, /api/login, and /api/refresh.

Auth

Method Endpoint Description
POST /api/register Create account
POST /api/login Get JWT token (optionally with refresh token via trust_device)
POST /api/refresh Exchange refresh token for new access token

Routines

Method Endpoint Description
GET /api/routines List user's routines
POST /api/routines Create a routine
GET /api/routines/:id Get routine with steps
PUT /api/routines/:id Update routine
DELETE /api/routines/:id Delete routine
GET /api/routines/:id/steps List steps
POST /api/routines/:id/steps Add a step
PUT /api/routines/:id/steps/reorder Reorder steps
POST /api/routines/:id/start Start a session

Sessions

Method Endpoint Description
GET /api/sessions/active Get active or paused session
POST /api/sessions/:id/complete-step Complete current step
POST /api/sessions/:id/skip-step Skip current step
POST /api/sessions/:id/pause Pause session
POST /api/sessions/:id/resume Resume session
POST /api/sessions/:id/cancel Cancel session
POST /api/sessions/:id/abort Abort with reason
POST /api/sessions/:id/note Add session note

Medications

Method Endpoint Description
GET /api/medications List medications
POST /api/medications Add medication
GET /api/medications/today Today's schedule with status
POST /api/medications/:id/take Log dose taken
POST /api/medications/:id/skip Log dose skipped
GET /api/medications/adherence Adherence stats
GET /api/medications/refills-due Refills due soon

Adaptive Medications

Method Endpoint Description
GET /api/adaptive-meds/:id Get adaptive timing data for a medication
PUT /api/adaptive-meds/:id Update adaptive timing preferences
POST /api/adaptive-meds/:id/reset Reset adaptive learning and return to default schedule
GET /api/adaptive-meds/stats View adaptive timing effectiveness stats

Snitch (Peer Accountability)

Method Endpoint Description
GET /api/snitch/contacts List designated accountability contacts
POST /api/snitch/contacts Add a new contact (generates invite link)
PUT /api/snitch/contacts/:id Update contact (name, shared info, frequency)
DELETE /api/snitch/contacts/:id Remove a contact
POST /api/snitch/contacts/:id/resend-invite Resend contact invite link
GET /api/snitch/contacts/:id/consent Get contact's consent status
POST /api/snitch/contacts/:id/consent Contact accepts or declines sharing
GET /api/snitch/history View recent alerts sent to contacts
POST /api/snitch/test-send Send test alert to a contact

Tasks

Method Endpoint Description
GET /api/tasks List user's tasks
POST /api/tasks Create a task
PUT /api/tasks/:id Update a task
DELETE /api/tasks/:id Delete a task
POST /api/tasks/:id/complete Mark task as complete

AI

Method Endpoint Description
POST /api/ai/generate-steps Generate ADHD-friendly routine steps from a goal description

Stats

Method Endpoint Description
GET /api/routines/:id/stats Routine completion stats
GET /api/routines/:id/streak Routine streak
GET /api/routines/streaks All streaks
GET /api/routines/weekly-summary Weekly progress
GET /api/victories Achievement detection

Templates, Tags, Rewards, Preferences, Notifications

Method Endpoint Description
GET /api/templates List available templates
POST /api/templates/:id/clone Clone template to user's routines
GET/PUT /api/preferences User settings and timezone
GET /api/rewards/random Random completion reward
POST /api/notifications/subscribe Subscribe to web push notifications (VAPID)
POST /api/notifications/unsubscribe Unsubscribe from push notifications

Environment Variables

Variable Required Description
DB_HOST Yes PostgreSQL host (default: db in Docker)
DB_PORT Yes PostgreSQL port (default: 5432)
DB_NAME Yes Database name (default: app)
DB_USER Yes Database user (default: app)
DB_PASS Yes Database password
JWT_SECRET Yes JWT signing secret (generate a random string, min 32 chars)
DISCORD_BOT_TOKEN No Discord bot token (if running Discord bot)
OPENROUTER_API_KEY No OpenRouter API key (if using jury council RAG features)
API_URL No API URL for bot (default: http://app:5000 in Docker)
POLL_INTERVAL No Scheduler poll interval in seconds (default: 60)
VAPID_PUBLIC_KEY No VAPID public key for web push notifications
VAPID_PRIVATE_KEY No VAPID private key for web push notifications

Design Framework

Synculous follows a documented design framework based on research from 9 books on behavior design, cognitive psychology, and ADHD. The core principles inform every feature:

Core Principles

  1. Immediate Feedback — Visual state change on tap in <0.1s. Per-step completion signals. Post-routine celebration.
  2. One Thing at a Time — Current step visually dominant. No decisions during execution. 4-7 steps max per routine.
  3. Zero Shame — No failure language. Streaks as identity markers, not performance metrics. Non-punitive everywhere.

Behavioral Foundations

  • Two-Minute Rule Entry Points — Every routine starts with something achievable in under 2 minutes (lower activation energy)
  • Variable Reward Scheduling — Random rewards on completion leverage variable-ratio reinforcement (proven for habit building)
  • Streak-Based Identity — Streaks build intrinsic motivation by making completion a visible, accumulating identity signal
  • Peer Accountability — "Snitch" contacts provide external accountability without shame (research shows this improves adherence)
  • Adaptive Timing — System learns your natural rhythm and optimizes reminders based on your actual behavior (reduces cognitive load)

Safety & Ethics

  • Jury Council — 5-juror consensus model for AI safety ensures content is appropriate for emotionally vulnerable users
  • Crisis Awareness — System detects crisis indicators and redirects to professional resources (988, Crisis Text Line) rather than generic psychoeducation
  • Transparent Limitations — All system messages clarify "this is educational, not treatment" and encourage professional care
  • User Agency — All adaptive and automated features can be overridden; manual controls are always available

Development & Testing

Running Tests

# Run pytest on all tests
pytest

# Run specific test file
pytest tests/test_routines.py

# Run with coverage
pytest --cov=core --cov=api tests/

Database Migrations

For schema changes, create migration scripts in config/ and reference them in docker-compose.yml or run manually:

psql -h localhost -U app -d app -f config/migration_name.sql

Timezone Testing

The system uses dual-path timezone support. Test both:

  1. Request headers: X-Timezone-Name (IANA) or X-Timezone-Offset
  2. Stored preferences: Verify user_preferences.timezone_name is persisted and read by scheduler

Discord Bot Development

Bot commands are modular in bot/commands/. To add a command:

  1. Create a new command file in bot/commands/
  2. Import and register in bot/bot.py
  3. Bot automatically syncs commands to Discord on startup

Documentation

Comprehensive documentation is available in DOCUMENTATION.md:

  • Detailed feature explanations
  • Database schema reference
  • Jury Council safety model (full spec)
  • Deployment & configuration
  • Contributing guidelines

License

MIT


Built with evidence-based design for ADHD. Not a replacement for therapy or medication — a tool to support them.