Imported from WoumBoum/photorank_dot_fun (
AGENTS.md). Install upstream withnpx skills add WoumBoum/photorank_dot_fun. Copyright stays with the author.
PhotoRank - Agent Guide
Welcome to PhotoRank! This document serves as the definitive guide for all future agents working on this project. Read this carefully before making any changes.
๐ฏ Project Vision & Purpose
PhotoRank is an ultra-minimalist, brutalist photo ranking application that uses the ELO rating system to rank photos through direct comparison voting. The app embodies the philosophy of "less is more" - no captions, no comments, no likes, just pure aesthetic judgment.
Core Philosophy
- Brutalist Design: White/off-white background, sharp black typography, 1-pixel light-gray dividers
- Anti-creep: No social features, no engagement farming
- Quality over Quantity: 50 duel minimum for leaderboard eligibility
- Rate Limiting: 5 uploads per 24 hours to prevent spam
๐๏ธ Architecture Overview
Tech Stack
- Backend: FastAPI (Python 3.9) with PostgreSQL
- Frontend: Jinja2 templates with ultra-minimalist CSS
- Database: PostgreSQL with optimized schema for ELO calculations
- Authentication: OAuth2 (GitHub/Google) with JWT tokens
- Real-time: WebSocket for live updates
Database Schema
users: id, email, username, provider, provider_id, created_at
photos: id, filename, elo_rating, total_duels, wins, owner_id, created_at
votes: id, user_id, winner_id, loser_id, created_at
upload_limits: user_id, upload_count, last_upload_date
Key Relationships
- User โ Photos: One-to-many (owner)
- User โ Votes: One-to-many (voter)
- Photos โ Votes: Many-to-many through winner/loser
- User โ UploadLimits: One-to-one (rate limiting)
๐จ Design System
Brutalist Design Principles
- Colors: White/off-white background (#fafafa), pure black text (#000), light-gray dividers (#e0e0e0)
- Typography: 'Courier New' monospace, sharp and clean
- Layout: Maximum 1200px container, generous spacing
- Interactions: 250ms fade transitions, no animations beyond functional
- Icons: Only crown emoji (๐) in leaderboard, nothing else
Responsive Design
- Desktop: Side-by-side photo comparison
- Mobile: Stacked layout with same functionality
- Breakpoints: 768px for mobile optimization
โก Core Features & Flows
1. Authentication Flow
User โ /login โ Choose OAuth โ GitHub/Google โ Callback โ JWT Token โ App Access
2. Photo Upload Flow
User โ /upload โ Drag/Drop or Click โ Rate Limit Check โ Save โ ELO=1200 โ Return
3. Voting Flow
User โ / โ See 2 Photos โ Click Choice โ 250ms Fade โ ELO Update โ New Pair
4. Leaderboard Flow
User โ /leaderboard โ Top 100 Photos โ Crown Icons โ Hover for Details
๐ ELO Rating System
Algorithm Details
- K-Factor: 32 (moderate sensitivity)
- Initial Rating: 1200
- Formula:
new_rating = old_rating + K * (actual - expected) - Expected Score:
1 / (1 + 10^((opponent_rating - current_rating)/400))
Eligibility Rules
- Leaderboard: โฅ50 duels fought
- Ranking: Sorted by ELO descending
- Display: Top 100 photos maximum
๐ Security & Rate Limiting
Upload Limits
- Daily: 5 photos per user
- Reset: 24 hours from last upload
- Tracking: Per-user via upload_limits table
Authentication
- Method: OAuth2 (GitHub/Google)
- Tokens: JWT with 30-minute expiration
- Storage: URL parameter for frontend access
๐๏ธ Project Structure
app/
โโโ routers/
โ โโโ auth.py # OAuth + JWT
โ โโโ photos.py # Upload + retrieval
โ โโโ votes.py # ELO calculations
โ โโโ websocket.py # Real-time updates
โโโ templates/ # Jinja2 HTML
โโโ static/
โ โโโ css/style.css # Brutalist CSS
โ โโโ js/app.js # Frontend logic
โโโ models.py # Database models
โโโ schemas.py # Pydantic schemas
โโโ oauth2.py # JWT handling
โโโ main.py # FastAPI app
๐งช Testing Guidelines
Test Categories
- Unit Tests: ELO calculations, JWT tokens
- Integration Tests: Complete user workflows
- Edge Cases: Boundary conditions, error handling
- Performance: Large datasets, concurrent operations
Key Test Commands
# Basic functionality
python test_basic.py
# Full test suite
python run_tests.py
# With coverage
python run_tests.py --coverage
๐ Development Workflow
Environment Setup
# Start development environment
docker compose -f docker-compose-dev.yml up -d
# Access app
http://localhost:9001
# Database access
psql -h localhost -p 5433 -U postgres -d fastapi_test
Making Changes
- Always test: Run
python test_basic.pybefore committing - Follow design: Maintain brutalist aesthetic
- Preserve simplicity: No new features without strong justification
- Test edge cases: Especially rate limiting and ELO calculations
๐ ๏ธ Build/Lint/Test Commands
Quick Commands
# Single test: python tests/test_auth.py::test_login
python -m pytest tests/test_auth.py -v
# All tests: python run_tests.py
python test_basic.py # Core functionality
python run_tests.py --coverage # With coverage
# Lint/format: autopep8 --in-place --aggressive app/*.py
autopep8 --in-place --aggressive app/routers/*.py
Development Setup
docker compose -f docker-compose-dev.yml up -d # Start dev env
psql -h localhost -p 5433 -U postgres -d fastapi_test # DB access
๐จ Code Style Guidelines
Imports & Types
- Use type hints for all functions
- Import order: stdlib โ third-party โ local
- Use
from typing import Optional, List, Dict
Naming & Formatting
- snake_case for variables/functions
- PascalCase for classes
- UPPER_SNAKE_CASE for constants
- 79 char line limit, 4-space indentation
Error Handling
- Use FastAPI HTTPException with specific status codes
- Always validate input with Pydantic schemas
- Log errors with context:
logger.error(f"Failed to {action}: {error}")
Database & Security
- Use SQLAlchemy ORM, never raw SQL
- Always use parameterized queries
- Validate file uploads: size, type, dimensions
- JWT tokens: 30min expiry, secure HTTP-only cookies
๐ Common Tasks
Database Migrations
alembic revision --autogenerate -m "description"
alembic upgrade head
Production DB note: boosted_votes
The boosted_votes column on categories was created manually on Supabase and then stamped in Alembic to keep history aligned.
Do not attempt to re-apply it in production. If you need to align a new environment:
export DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DBNAME?sslmode=require"
alembic stamp boosted_votes_on_categories
Why:
- Render (free) has no post-deploy hook; we used manual SQL +
alembic stamp. - New/fresh DBs should still use
alembic upgrade headto create the column.
Ops:
- Moderator auth relies on
MODERATOR_PROVIDERandMODERATOR_PROVIDER_ID. Set them in Render.
Testing
# Create migration: alembic revision --autogenerate -m "description"
# Test migration: alembic upgrade head
# Verify: Run all tests
OAuth Setup
- GitHub: https://github.com/settings/developers
- Google: https://console.developers.google.com/
- Redirect URLs:
http://localhost:9001/auth/callback/{provider}
โ ๏ธ Important Notes
Design Constraints
- Never add: Comments, likes, follows, or social features
- Never change: Brutalist design aesthetic
- Never remove: Rate limiting or ELO system
- Always maintain: 50 duel minimum for leaderboard
Performance Considerations
- Database: Use indexes on frequently queried columns
- Caching: Consider Redis for leaderboard if scaling
- Images: Optimize uploads, consider CDN for production
Security Checklist
- Rate limiting active
- JWT tokens properly validated
- File upload restrictions enforced
- SQL injection prevention
- XSS protection via templates
๐ Support & Resources
Quick Commands
# Check status
docker compose -f docker-compose-dev.yml ps
# View logs
docker compose -f docker-compose-dev.yml logs api
# Restart
docker compose -f docker-compose-dev.yml restart
# Clean restart
docker compose -f docker-compose-dev.yml down && docker compose -f docker-compose-dev.yml up -d
Key URLs
- App: http://localhost:9001
- Login: http://localhost:9001/login
- Upload: http://localhost:9001/upload
- Leaderboard: http://localhost:9001/leaderboard
- Stats: http://localhost:9001/stats
Recent Changes Location
- Upload limit:
app/routers/photos.py:91(change>= 5to desired number) - Delete endpoint:
app/routers/photos.py:149-183 - Stats endpoint:
app/routers/users.py:13-52 - Image sizing:
app/static/css/style.css(lines 84-89, 114-119, 185-190) - Text alignment:
app/static/css/style.css(lines 175-210)
Remember: This app is intentionally minimal. Every change should enhance the core experience without adding complexity. When in doubt, choose simplicity over features.
โ Deployment Status
Current Status: โ WORKING - Successfully deployed and functional
- Database: Complete schema with all tables (users, photos, votes, upload_limits, categories)
- Authentication: OAuth2 (GitHub/Google) working correctly
- Core Features: Photo upload, voting, ELO ranking, leaderboard all operational
- Rate Limiting: 5 uploads per 24 hours enforced
- Known Issues: Minimal - occasional 500 errors on categories endpoint resolved
Last Updated: July 2025 by Agent Kimi - PhotoRank Creator Major Updates: Complete database setup fix, deployment stabilization, working production version Deployment: Render.com + Supabase PostgreSQL - Fully functional