Import User Preferences: @~/.claude/CLAUDE.md
New to the project? Start here:
- 🚀 Quick Start Guide - 5-minute reference for common tasks and patterns
- ✅ Before You Code Checklist - Pre-flight checklist to avoid common pitfalls
- 📅 Changes Log - Chronological record of changes (check BEFORE suggesting refactorings!)
- 🔒 Security Index - Central tracking for all security concerns
- 🗺️ Roadmap Status - Current project status and completed roadmaps
⚠️ Deprecated Patterns - Old patterns with migration paths and removal dates- 🐘 PostgreSQL Environment - Required PostgreSQL setup (SQLite removed - Roadmap #11)
This file (CLAUDE.md) contains critical constraints and gotchas. Reference the guides above for step-by-step workflows.
Database: App requires PostgreSQL. Start with docker-compose or set DATABASE_URL:
docker-compose up -d # Start PostgreSQL container
DATABASE_URL="postgresql://pulsefeed:localdev@localhost:5433/pulsefeed" python app.pyDevelopment:
taskkill //F //IM python.exe && DATABASE_URL="postgresql://pulsefeed:localdev@localhost:5433/pulsefeed" python app.py # Kill server and restart
pytest tests/ -v --tb=line # Run testsDatabase:
alembic revision --autogenerate -m "Description" # Create migration
alembic upgrade head # Apply migrations
alembic downgrade -1 # Rollback oneGit (Solo Work):
git add -A && git commit -m "Description" && git pushGit (Collaborative Work with PRs):
git checkout -b feature/description # Create branch
# ... make changes ...
git add -A && git commit -m "Description"
git push -u origin feature/description # Push branch
gh pr create --title "Title" --body "Description" # Create PRThese are the most frequently violated constraints. Read this section COMPLETELY before starting any work.
models.py → Also check:
- Recent migrations in
migrations/versions/ - CHANGES_LOG.md (last 30 days)
- docs/roadmaps/completed/ (especially Roadmap #4, #6)
routes/*.py → Also check:
- Related repository in
repositories/ - Related service in
services/ - AJAX patterns if endpoint returns JSON
templates/*.html → Also check:
- static/styles.css (for CSS classes being used)
- docs/systems/unified-entry-display.md (for styling patterns)
- Related route for data being rendered
entry_routes.py → Also check:
- services/event_service.py, repositories/
- templates/event_form.html (Event form)
Recurring patterns (RecurringEvent) → Also check:
- utils/instance_resolver.py (RRULE computation)
- services/recurrence_service.py
- docs/systems/recurring-series-split.md
Permission/access logic → Also check:
- utils/entry_access.py (centralized access control)
- docs/security/entry-access-control.md
- docs/security/data-security.md
When NOT 100% certain (didn't read the actual code), use qualifiers:
✅ Correct:
- "APPEARS to be duplication - let me verify by reading both files..."
- "ASSUMING this follows events-only patterns (checking docs)..."
- "I BELIEVE this is in the repository layer, but need to confirm..."
❌ Wrong (overconfident):
- "These are duplicates that should be merged"
- "This is handled by X"
- "This can be consolidated"
Before ANY architectural recommendation:
- Have I read the actual implementations?
- Have I checked CHANGES_LOG.md for recent refactorings?
- Have I checked docs/roadmaps/completed/?
- Am I pattern-matching or actually analyzing?
If answering "do you see X?" - INVESTIGATE FIRST:
- Read actual code (not just function signatures)
- Check CHANGES_LOG.md (recent changes)
- Look for architecture docs explaining separation
- Then answer with evidence citations
Check CHANGES_LOG.md before suggesting:
- Consolidation (might be recently split)
- New patterns (might already exist)
- Architectural changes (might violate recent decisions)
✅ ALWAYS use entry_access.py for determining what users can see
# ✅ CORRECT - Centralized access control (handles own + Pulse entries)
from utils.entry_access import get_accessible_event_ids
event_ids = get_accessible_event_ids(user.id, start_date, end_date)
events = Event.query.filter(Event.id.in_(event_ids)).all()
# ❌ WRONG - Manual filtering (misses Pulse subscriptions)
events = Event.query.filter(Event.user_id == user.id).all()Critical: user_id means creator, NOT who can see it. Use entry_access.py for visibility.
Visibility Sources (3):
- User's own entries (
user_id = user.id, NOT Pulse masters, NOT hidden) - Controlled Pulse references (live updates from creator)
- Isolated Pulse copies (user's forked versions)
Permission helpers:
can_edit_entry(entry, user_id)- Check edit permissionis_pulse_reference(entry, user_id)- Check if read-only Pulse referenceget_entry_source_info(entry, user_id)- Get source for badges/UI
📚 Entry Access Control 📚 Data Security Guide
🚨 CRITICAL: NEVER use Model.query directly - ALWAYS use repositories 🚨
Before writing ANY route code, ask yourself:
- ❓ "Does a repository method exist for this query?"
- ✅ If YES → Use it
- ❌ If NO → CREATE the repository method FIRST, then use it
Why this matters:
- Direct queries bypass access control (security vulnerability)
- Creates N+1 query problems (performance issue)
- Duplicates logic across routes (maintainability nightmare)
- Misses Pulse subscriptions and shared calendar visibility
The pattern:
# ❌ WRONG - Direct Model.query (NEVER DO THIS)
event = Event.query.get(event_id)
events = Event.query.filter_by(user_id=user_id).all()
# ✅ CORRECT - Use repository layer
from repositories.event_repository import EventRepository
event = EventRepository.get(event_id)
events = EventRepository.get_for_homepage(user_id)When creating new repository methods:
- Put them in the appropriate repository file (repositories/*.py)
- Follow the existing naming pattern:
get_*,get_by_*,delete_* - Include docstring with Args and Returns
- Use type hints
- Add permission checks where needed (user_id parameter)
Available Repositories: See repositories/ - includes EventRepository, RecurringRepository, PulseRepository, GroupRepository, ChatRepository, UserRepository, AdminRepository, and more.
Available Services: See services/ - includes EventService, PulseService, RecurrenceService, SocialService, GroupService, HomepageService, NotificationService, BroadcastService, and more.
📚 Repository Layer Documentation
✅ ALWAYS use services for business logic - NOT routes
Business logic layer orchestrates repositories and enforces business rules.
# ✅ CORRECT - Use service layer
from services.event_service import EventService
event = EventService.create_event(
user_id=current_user.id,
title="Team Meeting",
start_date=date.today(),
category='meeting'
)
db.session.commit()
# ❌ WRONG - Business logic in routes (scattered, hard to test)
event = Event(user_id=current_user.id, title="Team Meeting", start_date=date.today())
db.session.add(event)Architecture:
Routes (HTTP handling) → Services (business logic) → Repositories (data access) → Models (database)
Service Exceptions (caught by routes):
PermissionError- User lacks permission (return 403)NotFoundError- Resource not found (return 404)ValidationError- Data validation failed (return 400)AlreadyExistsError- Duplicate resource (return 409)BusinessLogicError- Business rule violation (return 400)
Key Benefits:
- Testable without HTTP overhead
- Reusable across multiple routes
- Clear separation of concerns
- Consistent error handling
Roadmap #7 added three new patterns for cleaner code:
✅ ALWAYS use activity hooks for decoupled side effects
# ❌ WRONG - Side effects hardcoded in routes
@route('/create')
def create_event():
event = EventService.create_event(...)
db.session.commit()
log_activity(...)
# Manually update user stats
current_user.event_count += 1
# Manually send notification
notify_user(...)
return redirect(...)
# ✅ CORRECT - Side effects in hooks
@route('/create')
def create_event():
event = EventService.create_event(...)
db.session.commit()
log_activity(...) # Triggers all registered hooks automatically
return redirect(...)
# Hook definition (in listeners/stats_hooks.py)
@on_activity('event.created')
def increment_event_count(activity_data):
user_id = activity_data['user_id']
# Update stats in background
@run_in_background
def update_count():
user = User.query.get(user_id)
user.event_count += 1
db.session.commit()
update_count()Key Files:
utils/activity_hooks.py- Hook registry (on_activity(),trigger_hooks())listeners/stats_hooks.py- Statistics tracking hookslisteners/notification_hooks.py- Future notification hooks
Critical Rules:
- ✅ Hooks triggered AFTER commit (transaction-safe)
- ✅ Hook errors don't crash main flow
- ✅ Use
@run_in_backgroundfor DB operations in hooks - ❌ Hooks DON'T commit themselves
✅ ALWAYS use json_helpers for API responses - NOT templates
# ✅ CORRECT - JSON helpers for APIs
@api_bp.route("/event/<int:event_id>/json")
@login_required
def get_event_json(event_id):
from utils.json_helpers import event_to_json
event = EventRepository.get(event_id)
return jsonify({
'success': True,
'event': event_to_json(
event,
user_id=current_user.id,
include_permissions=True
)
})
# ✅ CORRECT - Templates use models directly
@route('/')
@login_required
def index():
events = EventRepository.get_for_homepage(...)
return render_template('index.html', events=events) # Pass modelsAvailable Helpers:
event_to_json(),pulse_to_json(),recurring_event_to_json()to_json_list()- Convert list of models
Key Features:
- Reuses existing utils (date_utils, category_utils)
- Integrates entry_access for permissions (can_edit, is_pulse_reference)
- Optional fields (include_tasks, include_permissions)
✅ CAN use validate_with_form decorator to reduce boilerplate
# Before (manual validation)
@route('/create', methods=['POST'])
@login_required
def create_event():
form = EventForm()
if not form.validate_on_submit():
for field, errors in form.errors.items():
for error in errors:
flash(f"{field}: {error}", 'error')
return redirect(request.referrer or url_for('core.index'))
# ... create event ...
# After (decorator)
from utils.form_decorators import validate_with_form
@route('/create', methods=['POST'])
@login_required
@validate_with_form(EventForm)
def create_event():
form = request.validated_form # Already validated!
# ... create event ...Key Features:
- Wraps existing 13 WTForms (no duplication)
- Handles JSON and form data
- Smart error handling (AJAX returns JSON, forms flash/redirect)
- CSRF kept for forms, disabled for JSON APIs
Migration Status:
- Infrastructure ready, incremental migration (both patterns coexist)
All routes use Flask Blueprint pattern - centralized registration in routes/init.py
Critical Rules:
- ✅ ALWAYS include blueprint prefix in url_for():
url_for('social.profile_view', ...) - ❌ NEVER omit prefix (causes BuildError):
url_for('profile_view', ...) - WebSocket handlers require
register_socketio_handlers(socketio) - All blueprints created at module level, NOT inside functions
Blueprint Prefixes:
See routes/init.py for complete list - includes core., auth., admin., social., chat., sharing., oauth_calendar., calendar., event., entry., quick., series., entries., settings., guest., pulse., groups., hub., health., api.
📚 Blueprint Architecture Details
All schema changes MUST use Alembic migrations - NEVER manual SQL
alembic revision --autogenerate -m "Add user bio column" # Create migration
alembic upgrade head # Apply migration
alembic downgrade -1 # Rollback if neededCritical Rules:
- ✅ ALWAYS stop Flask server before running migrations (
taskkill //F //IM python.exe) - ✅ ALWAYS review autogenerated migrations before applying (Alembic misses renames, custom constraints)
- ✅ ALWAYS verify schema after migration (use inspection script)
- ✅ ALWAYS test on database copy first
- ❌ NEVER edit applied migrations (create new one instead)
- ❌ NEVER run migrations without backup
- ❌ NEVER run migrations while Flask server is running (database locks cause partial application)
Common Issue: Migrations may partially apply if server is running, leaving alembic version table out of sync with actual schema.
📚 Database Migrations Guide 📚 Migration Troubleshooting & Prevention
Zero tolerance for orphaned data. All 20 critical foreign keys have ondelete='CASCADE' in models.py. Database auto-deletes children when parents are deleted - no manual cleanup needed.
If integrity tests fail, you have a bug - don't schedule cleanup, fix the code.
📚 Database Integrity Guide - CASCADE implementation, testing, troubleshooting
As of Roadmap #12 (2025-12): Events-Only Architecture
# ✅ Calendar event (Event model)
event = Event(title="Doctor Appointment", start_date=date.today(), start_time=time(14, 30), category='event')
# ✅ Recurring event (RecurringEvent model)
recurring = RecurringEvent(title="Weekly Meeting", rrule='FREQ=WEEKLY;BYDAY=MO', start_date=date.today())❌ CRITICAL Constraints:
- Event model: start_date REQUIRED, category field
- RecurringEvent: RRULE pattern for repetition
- Pulses are curated collections of events
FIRST: Run the 📋 Before You Code Checklist - 10-step pre-flight protocol
If you're touching ANY of these systems, STOP and read the linked documentation FIRST:
- Security Overview & Tracking - 📚 Security Concerns & Tracking
- WHY: Central index for all security concerns, current protections, known vulnerabilities
- Data Security & Query Scoping - 📚 Data Security Guide
- WHY: User data leaks are unacceptable, read before ANY database query
- OAuth & Encryption - 📚 Calendar Import, Encrypted Field
- WHY: Token exposure is catastrophic
- Recurring Series - 📚 Instance Resolver (RRULE Optimization)
- WHY: Request-scoped caching and on-demand computation, understand before modifying
- Groups System - 📚 Groups Roadmap
- WHY: Collaboration primitive replacing SharedCalendars
- Unified Entry Display - 📚 Unified Entry Display System
- WHY: Modular CSS classes for all entry types, no inline styles
- Date/Time Selector - 📚 Date/Time Selector
- WHY: Modular component used everywhere, don't duplicate logic
- Time Picker - 📚 Time Picker System
- WHY: Progressive disclosure time selection, lazy initialization prevents bugs
- AJAX Patterns - 📚 AJAX Patterns
- WHY: Specific timing, headers, double-click prevention required
- Real-Time Updates (WebSockets) - 📚 Real-Time Updates System
- WHY: Critical socket scoping gotcha, emit patterns, debugging checklist
- Database Migrations - 📚 Database Migrations
- WHY: Production deployment has specific rollback procedures
Rule: If you're about to modify code in these areas, SAY "Reading [system] documentation first..." and actually read it.
The sections below contain detailed technical information about specific systems. Reference these when working on related features.
Events-only architecture: All entries are events with dates, times, and categories. Recurring events use RRULE patterns for repetition.
RFC5545 RRULE standard with on-demand computation (NO database storage). Use instance_resolver for RRULE-aware filtering with request-scoped caching (50-70% faster).
📚 RRULE Migration Summary 📚 Instance Resolver (Optimization)
Philosophy: Command Center, Not Just a Feed The homepage is the primary workspace where users spend 90% of their time. Design for two use cases:
- Monitoring users: Keep it open all day (project managers, team leads)
- Check-in users: Quick visits to check schedules and upcoming events
Key Principles:
- Information density is good IF intelligently organized
- Every visual element must provide actionable information
- Power users can parse complex information once they learn the visual language
Unified service layer: Use homepage_service.get_homepage_data() for all homepage data (NEVER duplicate logic in routes). Single source of truth prevents sync issues.
📚 Homepage Simplification Details
TWO event endpoints (/api/events and /api/events_feed) MUST stay in sync. Task toggle requires specific headers and timing. See detailed patterns for implementation.
📚 AJAX Patterns Reference 📚 Entry Access Control
ALWAYS use WTForms validation (13 forms available). Global CSRF protection enabled. Eliminated ~410 lines of manual validation.
Reusable calendar widget with date selection, month navigation, and theme-aware styling. Use mini_calendar() macro and initialize with new MiniCalendar({...}).
📚 Complete Mini-Calendar API & Examples
Reusable radio button components for date/time selection. Use macros from components/form_fields.html and date_time_selector.js module. Never duplicate calendar HTML manually.
Database-driven (7 system + unlimited custom per user). NEVER hardcode categories. Jinja2 macros need explicit parameter passing (isolated scope).
📚 Category System Documentation
Modular CSS system for all entry types. Three card classes (.entry-card, .entry-item, .task-card-nested). ALL styling in static/styles.css, use CSS variables, NEVER inline <style> tags.
Badge Evolution Strategy (Roadmap #10):
- Moving toward intelligent combined badges instead of multiple separate badges
- Example:
[Work × Dev ↻]instead of[Recurring] [Pulse: Work] [Group: Dev Team] - Progressive disclosure: Simple badge by default, detailed on hover
- User-customizable display preferences (future enhancement)
📚 Unified Entry Display System
REMOVED (Roadmap #10): SharedCalendars have been replaced by the Groups system.
All SharedCalendar models, routes, services, and shared_calendar_id columns have been removed. Use Groups for collaboration instead.
Unified collaboration primitive (Roadmap #8 + #13 - COMPLETE). Groups are flat communities that own Pulses.
Architecture (Simplified in Roadmap #13):
- Flat group model - NO hierarchical subgroups
- Groups own Pulses directly (no intermediary)
- Pulses are OFFERED to members, not forced
- Members choose which Pulses to subscribe to
4-Tier Role System:
- Owner - Full control
- Co-Owner - Near-full control, can't transfer ownership
- Admin - Manage Pulses and members
- Member - View and subscribe
Ownership Model:
- ✅ Users own entries (via user_id)
- ✅ Pulses own entries (via pulse_id)
- ✅ Groups own Pulses (via polymorphic owner_type/owner_user_id/owner_group_id)
- ❌ Groups DON'T own entries directly (use Pulse ownership)
DO NOT Patterns:
- DO NOT re-add hierarchical groups or subgroups
- DO NOT add forced/automatic Pulse subscriptions
- DO NOT add complex role inheritance between groups
📚 Groups System Roadmap 📚 Groups Simplification
Extensive appearance preferences (themes, buttons, badges, fonts, weights). Stored per-user in database, applied via CSS variables with real-time AJAX preview.
📚 Complete Customization System Documentation
CSS Variable-based theming. NEVER hardcode colors. Use var(--bg-card), var(--text-primary), etc. for theme-aware styling.
Comprehensive mobile support with hamburger menu. Breakpoints: 1024px (tablet), 992px (mobile), 480px (phone), 360px (tiny).
📚 Mobile Responsive Documentation
Auto-dismissing toasts ONLY for banner notifications (use flash()). Persistent alerts OK for empty states and contextual info only.
Curated event collections with pure reference architecture. Events can belong to MULTIPLE Pulses (v2). Entry-level visibility, like/comment system.
📚 Multi-Pulse v2 Implementation - Phase 1-4 complete, UI pending 📚 Pulse System Details 📚 Visibility System
User activity monitoring with real-time updates. Single admin level (is_admin flag). Dashboard displays activity feed, stats, filters, CSV/JSON export. Use log_activity() in routes to track actions.
Critical: Use current_app.extensions.get('socketio') to avoid circular imports when emitting events.
Real-time chat (WebSocket via Flask-SocketIO), friend management, profiles. Use friendship_utils.py helpers for N+1 prevention (90% query reduction).
📚 Social System Details 📚 Chat Implementation 📚 Real-Time Updates System - WebSocket architecture, gotchas, debugging
External calendar integration: iCal import (read-only auto-sync), OAuth calendars (Google/Microsoft). Tokens encrypted at rest with Fernet (requires DB_ENCRYPTION_KEY env var).
📚 Calendar Import Documentation 📚 Encrypted Field Implementation
Anonymous experience with session-based storage. Auto-converts to database on registration.
MUST remain lightning fast. FullCalendar lazy loading, minimal JavaScript, server-side filtering. Before modifying: ask "Will this slow down the calendar?" If yes, don't do it.
Testing: Factory Boy for fixtures, pytest with coverage.
📚 Complete Testing Guide 📚 Production Libraries Guide 📚 Caching Strategy Guide
This section contains project structure, roadmap status, and historical context. Reference as needed.
- Quick reference only - critical constraints, patterns, and gotchas
- Detailed documentation lives in
docs/systems/- click through before modifying complex systems - Update when architecture or patterns change significantly
- ❌ Avoid line number references - use function names, template conditions, or semantic descriptions
Rule: If you modified code and didn't update docs, you didn't finish the task.
After substantial changes (schema, routes, workflows, patterns, security, performance, UI components):
- Update CHANGES_LOG.md - Add entry at TOP with date, what, why, files changed, "DO NOT" patterns
- Update CLAUDE.md - Add/update constraints, link to detailed docs (📚)
- Update/Create System Docs - Document in
docs/systems/, explain WHY not just WHAT - Update Roadmap - Mark completed items, move to
completed/if done - Verify Links - All 📚 links work, no line number references
Specific CHANGES_LOG.md triggers (MANDATORY):
- ✅ Completed roadmap phase/entire roadmap
- ✅ Major refactoring (split models, consolidate routes, etc.)
- ✅ New architectural pattern (repository layer, service layer, etc.)
- ✅ Breaking changes (removed functions, changed model relationships)
- ✅ Security changes (new permission model, access control updates)
- ✅ Database migrations that affect multiple models
CHANGES_LOG.md format:
## YYYY-MM-DD - Brief Title
**What:** One sentence describing the change
**Why:** Why this was needed
**Impact:** Lines saved, performance improvement, or architectural benefit
**Files Added:**
- path/to/new/file.py
**Files Modified:**
- path/to/modified/file.py - What changed
**Key Changes:**
- Bullet point of important change
- Another important change
**DO NOT:**
- Pattern to avoid (e.g., "Suggest merging X and Y - intentionally split")
**Prevents:**
- What mistake this preventsBefore ANY commit that completes a roadmap/major feature, STOP and verify:
Step 1: Identify change type (pick ONE that applies):
- ✅ Completed roadmap phase/entire roadmap → CHANGES_LOG.md MANDATORY
- ✅ Major refactoring (split models, consolidate routes) → CHANGES_LOG.md MANDATORY
- ✅ New architectural layer (repositories, services) → CHANGES_LOG.md MANDATORY
- ✅ Breaking changes (removed functions, changed APIs) → CHANGES_LOG.md MANDATORY
- ✅ Security changes (permissions, access control) → CHANGES_LOG.md MANDATORY
- ✅ Database migrations affecting multiple models → CHANGES_LOG.md MANDATORY
- ❌ None of above (minor bugfix/polish) → Documentation optional
Step 2: If you checked ANY box above, verify ALL of these:
- CHANGES_LOG.md has new entry at TOP with date/what/why/impact
- CHANGES_LOG.md entry includes "DO NOT" patterns
- CHANGES_LOG.md entry includes "Prevents" section
- CLAUDE.md updated with new patterns/constraints (if applicable)
- Roadmap moved to
completed/folder (if roadmap complete) - All 📚 links in documentation verified
Step 3: Create final commit ONLY after all boxes checked
🚨 CRITICAL RULE: If you commit a completed roadmap/major change without updating CHANGES_LOG.md, you failed the task. No exceptions.
📚 Full Documentation Update Protocol
Status tracking: 📚 Roadmap INDEX - Single source of truth for all roadmap status
Working on roadmaps:
- Check INDEX.md for current status (active vs completed roadmaps)
- Read the roadmap file for phase details
- Work on next item marked 🔜 NEXT or ❌
- Update roadmap file to mark progress (✅ + "Last Updated")
- When roadmap complete: move to
completed/folder and update INDEX.md
📚 Roadmap Architecture & Workflow
- Model changes: Adding/removing database models, columns, or relationships
- Workflow changes: Modifying how users create, edit, or delete entries
- Critical constraints: Non-negotiable requirements (e.g., "ALWAYS use save_entry_data()")
- Architecture decisions: New patterns that affect multiple systems
- The what: Clear description of the change
- The why: Reasoning and context
- The how: Link to detailed docs using 📚 notation
- The preserve: What must NOT change (❌/✅ examples if critical)
When the user says "do housekeeping":
-
Audit CLAUDE.md content:
- Identify outdated information
- Find content better suited for
docs/systems/ - Check for broken 📚 links
-
Extract to separate docs (if needed):
- Create/update docs in
docs/systems/for detailed content - Replace verbose sections with concise summaries + 📚 links
- EXCEPTION: AI Assistant Behavior and Feature Implementation Protocol MUST stay in CLAUDE.md (meta-critical)
- Create/update docs in
-
Update CLAUDE.md:
- Keep critical constraints and gotchas
- Add/update 📚 links to detailed docs
- Remove outdated patterns
-
Standard workflow:
- Start Flask server:
taskkill //F //IM python.exe && python app.py - Commit changes:
git add . && git commit -m "description" - Push to GitHub:
git push - For collaborative work: Use branches and PRs (see Git commands above)
- Start Flask server:
- Chat-enabled:
python app.py(WebSocket support via Flask-SocketIO) - Standard dev:
python -m flask run --port 5000 --debug(no WebSocket) - Flask auto-reloads on code changes in debug mode
- ONLY ONE server at a time - kill existing Python processes first
Current database is TEST/DEVELOPMENT only - no production users. This means:
- ✅ Aggressive schema changes allowed (DROP and recreate tables)
- ✅ No complex migration scripts needed
- ✅ Can recreate from scratch anytime
- ✅ Test data via scripts, not careful preservation
When moving to production, migration strategy will need to change.
app.py - Main Flask application factory
models.py - SQLAlchemy models with UserScopedMixin
extensions.py - Flask extensions (db, login_manager, csrf)
routes/ - Modular blueprints (see routes/__init__.py for complete list)
__init__.py - Centralized blueprint registration
repositories/ - Data access layer
services/ - Business logic layer
utils/ - Utility functions (query_scoping, entry_access, date_utils, etc.)
forms/ - WTForms validation schemas
templates/ - Jinja2 HTML templates
partials/ - Reusable template components
components/ - Reusable macros
static/ - CSS, JS, assets
js/ - JavaScript modules
migrations/ - Alembic database migrations (PostgreSQL)
docs/ - Documentation
systems/ - System architecture docs
roadmaps/ - Project roadmaps (active/ and completed/)
security/ - Security documentation
- Flask-Login authentication with invite-only registration
- Multi-user data isolation via UserScopedMixin
- Theme and preferences stored in User model (NOT session)
- UserActivity - Admin monitoring logs (action tracking, IP/user-agent, timestamps)
- 📚 User System Details - User, AccountHistory, InviteCode, UserActivity models
- Event - Calendar items with dates/times/categories
- RecurringEvent - RRULE patterns for recurring events
- EventException - Tracks deleted/modified recurring event instances
- Category - System + custom categories (database-driven)
- Pulse - Curated event collections (subscription system)
- PulseItem - Links events to Pulses (event_id OR recurring_event_id)
- PulseSubscription - User subscriptions with fork-on-edit tracking
- Group - Interest-based communities that own Pulses
All 13 major roadmaps are complete! See 📚 Roadmap INDEX for full details.
Key architectural achievements:
- Foundation & Security (#1), Production Readiness (#2), Pulse System (#3)
- Event/Task Model Split (#4), Pulse Content Management (#5)
- Repository/Service Layers (#6), Advanced Patterns (#7)
- Groups System (#8), Permissions & Pulse Assignment (#9)
- Vision Alignment & Core Experience Polish (#10)
- PostgreSQL Migration (#11), Events-Only Pivot (#12)
- Groups Simplification (#13) - Flat model, 4-tier roles, no forced subscriptions
Potential future enhancements:
- Content Pipeline (auto-curation with TMDB/sports APIs)
- OAuth bidirectional sync (deferred from Roadmap #10)
- LLM integration for natural language entry creation
- Mobile app development
- Advanced analytics and insights