Provides comprehensive guidance for future Claude Code instances including development commands, architecture overview, and common patterns. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
15 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Development Commands
Local Development (Non-Docker)
# Set environment for development
export DJANGO_SETTINGS_MODULE=config.settings.development
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Database operations
python manage.py migrate
python manage.py makemigrations
python manage.py createsuperuser
# Run development server
python manage.py runserver
# Run tests
python manage.py test
# Django shell
python manage.py shell
# Collect static files (for production)
python manage.py collectstatic --noinput
Docker Development
# Development with live reload
make dev
make dev-build # Rebuild containers
# Production mode
make prod
make build # Build production containers
make deploy # Full deployment (pull, build, migrate, restart)
# Database operations
make migrate
make makemigrations
make createsuperuser
make dbshell # Access database shell
make backup # Backup database
# Monitoring
make logs # All container logs
make logs-web # Web container logs only
make logs-celery # Celery worker logs
make status # Container status
# Control
make stop
make restart
make clean # Stop and remove all containers/volumes
# Shell access
make shell # Django shell
make test # Run tests in container
Celery (Background Tasks)
# Local development (non-Docker)
# Terminal 1: Run Celery worker
celery -A config worker -l info
# Terminal 2: Run Celery beat (scheduled tasks)
celery -A config beat -l info
# In Docker, Celery runs automatically as separate services
High-Level Architecture
Django Apps
users/ - User authentication and management
- Custom User model with email-based authentication (no username)
- Three-tier verification: email verification → admin approval → login
- JWT token authentication with refresh tokens
- Device token management for push notifications
- Rate-limited registration (5/hour) and login (10/hour)
tasks/ - Core task management
- Hierarchical tasks with parent/subtask relationships
- Task status (pending, in_progress, completed, cancelled) and priority (low, medium, high, urgent)
- Recurrence patterns (daily, weekly, monthly, yearly, custom RRULE)
- Time tracking with start/stop timer functionality
- Tag system with colors and icons for organization
- Task sharing with permission levels (viewer/editor)
- Soft-delete architecture (is_deleted flag) for sync compatibility
sync/ - Mobile app synchronization
- Offline-first bidirectional sync protocol
- Timestamp-based conflict detection with three resolution strategies (local, server, merged)
- Multi-device support with independent sync tokens per device
- Incremental sync (delta updates) and full sync support
- Uses sync_id (UUID) separate from Django id for mobile compatibility
- Soft-delete propagation to enable client-side deletion
notifications/ - Email notifications and reminders
- Daily email digest (overdue + due today tasks)
- Timezone-aware scheduling (6-7 AM in user's local time)
- Deduplication (one email per user per day)
- Celery-based background processing
- Recurring task safety net (creates missed recurrences)
Key Architectural Patterns
UUID Primary Keys - All models use UUID as primary key for distributed system compatibility and security.
Dual ID System - Each entity has both id (Django PK) and sync_id (for mobile sync). Sync protocol uses sync_id to maintain consistency across devices.
Soft Delete Pattern - All entities have is_deleted boolean instead of hard deletes. Dual managers enable filtering:
objects- Default manager (excludes deleted)all_objects- Includes deleted items (used in sync)
Multiple Serializers Per Context - Different serializers for list/detail/sync endpoints to optimize performance:
TaskListSerializer- Lightweight for list viewsTaskSerializer- Full details with nested subtasksTaskSyncSerializer- Uses sync_id, includes is_deleted
Permission System - Custom IsOwnerOrReadOnlyIfShared permission:
- Read access: Owner OR users with shared access
- Write access: Owner only
- Security filtering in serializers prevents tag/task leakage
Custom Authentication Backend - EmailVerifiedApprovedBackend enforces:
- Email verification check
- Admin approval check
- Superusers bypass both checks
- Stores error state in session for web UI
Timezone-Aware Operations - All time calculations use user's timezone:
- Daily emails scheduled in user's local 6-7 AM
- Overdue calculation relative to user's local date
- UTC storage, converted for display and logic
Mobile App Support - AllowMobileAppFramingMiddleware detects mobile app via User-Agent and removes X-Frame-Options to allow WebView embedding while maintaining security for web browsers.
Sync Protocol Details
The sync endpoint (POST /api/sync/) handles offline-first synchronization:
Request Format:
{
"device_id": "unique-device-id",
"last_sync_token": "previous-token-or-null",
"changes": {
"tasks": [...],
"tags": [...],
"time_entries": [...]
}
}
Response Format:
{
"sync_token": "new-sync-token",
"server_time": "2025-01-10T12:00:00Z",
"server_changes": {
"tasks": [...],
"tags": [...],
"time_entries": [...]
},
"conflicts": [...]
}
Conflict Detection: Compares updated_at timestamp between client's last sync and server's current state. If server modified entity since last sync, conflict is flagged.
Conflict Resolution Strategies:
- local - Apply client changes (overwrite server)
- server - Keep server version (discard client)
- merged - Accept manually merged data
Celery Task Schedule
Configured in config/celery.py:
send_daily_task_email - Runs every hour
- Fetches users with email notifications enabled
- Converts UTC to user's timezone
- Sends email only if user's local time is 6-7 AM
- Checks for existing notification to prevent duplicates
- Groups tasks: overdue + due today (pending/in_progress only)
process_recurring_tasks - Runs daily at midnight
- Safety net for recurring task creation
- Processes completions from last 48 hours
- Creates next recurrence if missing
- Prevents duplicate recurrences
Settings Modules
config.settings.development - Local development
- DEBUG=True
- SQLite database
- Console email backend
- Minimal security
config.settings.production - Production deployment
- DEBUG=False
- PostgreSQL required
- SMTP email backend
- Full security (HTTPS, CSP, HSTS)
- Gunicorn with multiple workers
config.settings.selfhosted - Docker/self-hosted
- Environment-based configuration
- PostgreSQL with health checks
- Redis for Celery
- Configurable workers and timeout
- WhiteNoise for static files
Database Models Reference
Task Model Fields:
- Hierarchy:
parent(ForeignKey to self) - Status: pending, in_progress, completed, cancelled
- Priority: low, medium, high, urgent
- Recurrence:
recurrence_type,recurrence_interval,recurrence_rule(RRULE) - Timing:
due_date,due_time,completed_at,created_at,updated_at - Soft delete:
is_deleted(filters viaSoftDeleteManager) - Sync:
sync_id(UUID for mobile sync) - Relationships:
tags(ManyToMany),time_entries(reverse FK)
User Model Fields:
- Authentication:
email(unique),password,first_name,last_name - Verification:
email_verified,is_approved,approved_by - Preferences:
timezone,reminder_minutes_before,notifications_enabled,email_notifications_enabled - UUID primary key for security
TimeEntry Model:
start_time,end_time,duration(auto-calculated)- Foreign keys:
task,user - Soft delete:
is_deleted
TaskShare Model:
shared_by,shared_with(User FKs)permission_level: viewer, editor- Can share tasks or tags
- Generic relation pattern
API Endpoint Overview
Authentication:
POST /api/users/register/- Create account (rate limited 5/hour)POST /api/users/token/- Login (rate limited 10/hour)POST /api/users/token/refresh/- Refresh JWTPOST /api/users/verify-email/- Verify email tokenPOST /api/users/resend-verification/- Resend verification email
Tasks:
GET/POST /api/tasks/- List/create tasks (supports filtering by status, priority, tags, search)GET/PUT/DELETE /api/tasks/<id>/- Task detail operationsPOST /api/tasks/<id>/start-timer/- Start time trackingPOST /api/tasks/time-entries/<id>/stop/- Stop timerGET/POST /api/tasks/tags/- Tag managementGET/POST /api/tasks/shares/- Share management
Sync:
POST /api/sync/- Main sync endpoint (rate limited 100/hour)GET /api/sync/conflicts/- List pending conflictsPOST /api/sync/conflicts/<id>/resolve/- Resolve conflict
Notifications:
GET /api/notifications/- List notificationsPOST /api/notifications/<id>/read/- Mark as readPOST /api/notifications/mark-all-read/- Mark all readGET /api/notifications/unread-count/- Get unread count
Environment Variables
Key variables (see .env.example and stack.env.example):
Django Core:
SECRET_KEY- Django secret (generate withpython -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())")DEBUG- Debug mode (False in production)ALLOWED_HOSTS- Comma-separated hostsCSRF_TRUSTED_ORIGINS- HTTPS originsSITE_DOMAIN- Domain for email links
Database:
DATABASE_URL- Connection string (e.g.,postgresql://user:pass@host:5432/db)
Redis & Celery:
REDIS_URL- Redis connection (e.g.,redis://localhost:6379/0)
Email:
EMAIL_HOST,EMAIL_PORT,EMAIL_USE_TLSEMAIL_HOST_USER,EMAIL_HOST_PASSWORDDEFAULT_FROM_EMAIL- Sender addressEMAIL_VERIFICATION_TOKEN_EXPIRY_HOURS- Token validity (default: 24)
Docker/Gunicorn:
GUNICORN_WORKERS- Worker processes (default: 3)GUNICORN_TIMEOUT- Request timeout (default: 60)WEB_PORT- External port (default: 8000)
Testing
Tests located in each app's tests.py. Run with:
python manage.py test # All tests
python manage.py test users # Specific app
python manage.py test users.tests.TestUserModel # Specific test
Docker:
make test # Run tests in container
Deployment Notes
Docker Deployment:
- Production uses PostgreSQL (postgres:16-alpine)
- Redis for Celery message broker (redis:7-alpine)
- Web service runs Gunicorn with configurable workers
- Celery worker and beat run as separate services
- Health checks on all services
- Named volumes for persistence (db_data, redis_data, static_files)
- Networks: frontend (web-facing), backend (internal services)
Manual Deployment:
- Use
config.settings.productionsettings module - Requires PostgreSQL (not SQLite)
- Configure SMTP for email
- Use Gunicorn behind Nginx reverse proxy
- Set up systemd services for Gunicorn, Celery worker, Celery beat
- Configure SSL/TLS certificates (Let's Encrypt recommended)
- Enable firewall (UFW) with SSH, HTTP, HTTPS only
Production Checklist:
- Set DEBUG=False
- Use strong SECRET_KEY
- Configure ALLOWED_HOSTS (no wildcards)
- Set CSRF_TRUSTED_ORIGINS for HTTPS
- Use PostgreSQL (not SQLite)
- Configure email backend (SMTP)
- Enable security headers (in settings)
- Set up SSL/TLS certificates
- Configure database backups
- Set up log rotation
- Monitor error logs and uptime
Common Development Patterns
Creating Recurring Tasks: When a recurring task is marked complete, the next recurrence is automatically created in tasks/models.py create_next_recurrence() method. Celery task process_recurring_tasks() runs as safety net.
Time Entry Workflow:
POST /api/tasks/<id>/start-timer/creates TimeEntry with start_time- Running timer tracked in UI
POST /api/time-entries/<id>/stop/sets end_time and calculates duration
Email Verification Flow:
- User registers →
email_verified=False,is_approved=False - EmailVerificationToken created with 24-hour expiry
- User clicks link →
email_verified=True, token marked used - Admins notified via email
- Admin approves in Django admin →
is_approved=True - User notified and can now login
Adding New Celery Tasks:
- Create task function in app's
tasks.pywith@shared_taskdecorator - Add to beat schedule in
config/celery.pyif periodic - Import in app's
__init__.pyto ensure task registration - Restart Celery worker and beat services
Extending User Model: The User model is in users/models.py. Add fields there, create migration, update serializers and admin if needed.
Security Filtering: When adding new endpoints that query related objects, filter by user in viewset's get_queryset(). Example from TaskViewSet:
def get_queryset(self):
return Task.objects.filter(user=self.request.user)
File Structure Reference
config/ # Django project configuration
settings/ # Environment-specific settings
base.py # Shared settings
development.py # Local development
production.py # Production deployment
selfhosted.py # Docker/self-hosted
celery.py # Celery configuration
urls.py # Root URL configuration
wsgi.py # WSGI entry point
users/ # User management app
models.py # User, DeviceToken, EmailVerificationToken
views.py # Registration, login, profile
serializers.py # User serializers
backends.py # EmailVerifiedApprovedBackend
utils.py # Email utilities
tasks/ # Task management app
models.py # Task, Tag, TimeEntry, TaskShare
views.py # Task CRUD, filtering, timers
serializers.py # Multiple serializers per context
middleware.py # AllowMobileAppFramingMiddleware
permissions.py # IsOwnerOrReadOnlyIfShared
sync/ # Mobile sync app
models.py # SyncLog, SyncConflict
views.py # Sync endpoint, conflict resolution
serializers.py # SyncSerializer with sync_id
notifications/ # Notifications app
models.py # Notification, ScheduledReminder
views.py # Notification CRUD
tasks.py # Celery tasks (daily email, recurring tasks)
templates/ # Django templates
base.html # Base template with dark mode
tasks/ # Task-related templates
users/ # User-related templates
static/ # Static assets
css/ # Stylesheets
js/ # JavaScript
docker/ # Docker-related files
docker-compose.yml # Production Docker setup
docker-compose.dev.yml # Development overrides
Dockerfile # Production image
Dockerfile.dev # Development image
Makefile # Docker command shortcuts