41 Commits

Author SHA1 Message Date
5660d882d6 Merge release/v0.3.0: Phase 3 - Participant Self-Management v0.3.0 2025-12-22 21:22:54 -07:00
0941315a70 Merge feature/phase-3-participant-self-management: Participant Self-Management (Stories 4.5, 6.1, 6.2, 6.3) 2025-12-22 21:21:26 -07:00
c2b3641d74 feat: implement reminder preferences and withdrawal (Stories 6.3, 6.2)
Implement Phase 3 participant self-management features:

Story 6.3 - Reminder Preferences:
- Add ReminderPreferenceForm to participant forms
- Add update_preferences route for preference updates
- Update dashboard template with reminder preference toggle
- Participants can enable/disable reminder emails at any time

Story 6.2 - Withdrawal from Exchange:
- Add can_withdraw utility function for state validation
- Create WithdrawalService to handle withdrawal process
- Add WithdrawForm with explicit confirmation requirement
- Add withdraw route with GET (confirmation) and POST (process)
- Add withdrawal confirmation email template
- Update dashboard to show withdraw link when allowed
- Withdrawal only allowed before registration closes
- Session cleared after withdrawal, user redirected to registration

All acceptance criteria met for both stories.
Test coverage: 90.02% (156 tests passing)
Linting and type checking: passed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 21:18:18 -07:00
4fbb681e03 fix: display participant count in admin dashboard and add startup diagnostics
- Fix admin dashboard showing hardcoded 0 for participant count
- Fix exchange detail page to show participant list with count
- Filter out withdrawn participants from counts
- Add startup diagnostics to entrypoint.sh for troubleshooting
- Fix test_profile_update test that was missing auth fixture

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 21:01:45 -07:00
a7902aa623 feat: implement Phase 3 participant self-management (stories 4.5, 6.1)
Implemented features:
- Story 4.5: View Participant List - participants can see other active participants
- Story 6.1: Update Profile - participants can edit name and gift ideas before matching
- Utility functions for state management and business logic
- Comprehensive unit and integration tests

New files:
- src/utils/participant.py - Business logic utilities
- src/templates/participant/profile_edit.html - Profile edit form
- tests/unit/test_participant_utils.py - Unit tests for utilities
- tests/integration/test_participant_list.py - Integration tests for participant list
- tests/integration/test_profile_update.py - Integration tests for profile updates

Modified files:
- src/routes/participant.py - Added dashboard participant list and profile edit route
- src/templates/participant/dashboard.html - Added participant list section and edit link
- src/forms/participant.py - Added ProfileUpdateForm
- src/app.py - Added participant.profile_edit to setup check exemptions
- tests/conftest.py - Added exchange_factory, participant_factory, auth_participant fixtures

All tests passing. Phase 3.3 (reminder preferences) and 3.4 (withdrawal) remain to be implemented.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 21:01:45 -07:00
75378ac769 Merge release/v0.2.1: Critical database persistence fix v0.2.1 2025-12-22 20:37:23 -07:00
6e8a7186cf fix: use absolute paths for database to ensure persistence
Critical bug fix: Path(__file__).parent.parent returns a relative path,
causing the database to be created in different locations depending on
the working directory. This caused data loss on container restarts.

Changes:
- Add .resolve() to BASE_DIR in src/config.py and migrations/env.py
- Fix admin dashboard showing 0 for participant count (was hardcoded)
- Fix exchange detail page to show actual participant list
- Add startup diagnostics to entrypoint.sh for troubleshooting

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 20:37:11 -07:00
915e77d994 chore: add production deployment config and upgrade path requirements
- Add docker-compose.yml and docker-compose.example.yml for production deployment
- Add .env.example with all required environment variables
- Update architect agent with upgrade path requirements
- Update developer agent with migration best practices
- Add Phase 3 design documents (v0.3.0)
- Add ADR-0006 for participant state management

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 19:32:42 -07:00
155bd5fcf3 Merge release/v0.2.0: Phase 2 - Participant Registration & Authentication
Features implemented:
- Story 4.1: View Registration Page
- Story 4.2: New Participant Registration
- Story 4.3: Returning Participant Detection
- Story 5.1: Magic Link Request
- Story 5.2: Magic Link Login
- Story 5.3: Participant Session (Dashboard + Logout)
- Story 10.1: DEV Mode Email Logging

Technical changes:
- Alembic migrations with auto-apply on container start
- Filesystem-based sessions (avoids SQLAlchemy race conditions)
- Magic link authentication with SHA-256 hashed tokens
- Rate limiting for registration and magic link requests
- CSRF protection for all forms

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 19:04:30 -07:00
b38fc328f9 fix: resolve database and session initialization issues
- Switch from SQLAlchemy sessions to filesystem sessions to avoid race
  conditions with multiple gunicorn workers trying to create the sessions
  table simultaneously
- Update Alembic env.py to use a minimal Flask app without Flask-Session
  to prevent session table creation during migrations
- Add data directory creation to entrypoint.sh for clean container starts
- Configure test environment to use filesystem sessions with temp directory

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 18:57:57 -07:00
8c3b333f61 fix: add CSRF token to participant logout form
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 18:24:35 -07:00
80f7926985 fix: use print instead of logger for dev mode email output
Logger.info wasn't visible in gunicorn container logs. Using print
with flush=True ensures magic link URLs are visible in container
logs for QA testing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 18:16:48 -07:00
5a9f0d552a fix: use string budget in email service instead of float
The budget field is stored as a string (e.g., '$25-50'), not a number.
Updated EmailService.send_registration_confirmation() to accept budget
as a string and display it directly in the email.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:56:59 -07:00
37d50e328d fix: regenerate Alembic migration with complete schema
Previous migrations were empty (just 'pass'). Regenerated a single
migration that includes all models: Admin, Exchange, Participant,
MagicToken, and RateLimit tables with proper indexes and constraints.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:49:35 -07:00
4e9fd5ad07 fix: correct EmailService return type annotations
Fixed return type annotations in EmailService to use Any instead of dict[str, Any] to properly handle Resend API's SendResponse type.

The Resend library returns a SendResponse object in production mode and a dict in dev mode. Using Any allows both return types while maintaining type safety for callers.

Changes:
- Updated send_email() return type from dict[str, Any] to Any
- Updated send_magic_link() return type from dict[str, Any] to Any
- Updated send_registration_confirmation() return type from dict[str, Any] to Any
- Removed unnecessary type annotation on result variable

This resolves mypy type checking errors while preserving the existing dev/production mode behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:41:57 -07:00
44ef77ca68 feat: implement Stories 5.2 & 5.3 - Magic Link Login and Participant Session
Implemented complete participant authentication flow with magic link login and session management.

Story 5.2 - Magic Link Login:
- Participants can click magic links to securely access their dashboard
- Single-use tokens that expire after 1 hour
- Session creation with participant_id, user_type, and exchange_id
- Error handling for expired, used, or invalid tokens
- Fixed timezone-aware datetime comparison for SQLite compatibility

Story 5.3 - Participant Session:
- Authenticated participants can access their exchange dashboard
- participant_required decorator protects participant-only routes
- Participants can only access their own exchange (403 for others)
- Logout functionality clears session and redirects appropriately
- Unauthenticated access returns 403 Forbidden

Technical changes:
- Added magic_login() route for token validation and session creation
- Added dashboard() route with exchange and participant data
- Added logout() route with smart redirect to request access page
- Added participant_required decorator for route protection
- Enhanced MagicToken.is_expired for timezone-naive datetime handling
- Added participant.logout to setup check exclusions
- Created templates: dashboard.html, magic_link_error.html, 403.html
- Comprehensive test coverage for all user flows

Acceptance Criteria Met:
✓ Valid magic links create authenticated sessions
✓ Invalid/expired/used tokens show appropriate errors
✓ Authenticated participants see their dashboard
✓ Participants cannot access other exchanges
✓ Unauthenticated users cannot access protected routes
✓ Logout clears session and provides feedback

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:41:29 -07:00
321d7b1395 feat: implement Story 5.1 - Magic Link Request
Allow participants to request magic links for dashboard access:

- POST /exchange/<slug>/request-access handles form submission
- Accept email, look up participant in database
- Generate token (secrets.token_urlsafe(32)), store SHA-256 hash
- Send magic link email via EmailService.send_magic_link()
- Rate limit: 3 requests per hour per email
- Always show generic success message (prevent enumeration)
- Only send email if participant exists
- Case-insensitive email lookup
- Comprehensive test suite with 7 tests

Includes:
- MagicLinkRequestForm with email validation
- request_access.html template
- GET endpoint to display request form

All tests passing (97 total), 91% coverage maintained.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:19:56 -07:00
43bfce3913 feat: implement Story 4.3 - Returning Participant Detection
Prevent duplicate registrations for the same exchange:

- Check for existing participant with same email (case-insensitive)
- Show friendly message if already registered
- Offer link to request new magic link for access
- Allow same email to register for different exchanges
- Comprehensive test suite with 4 tests

All tests passing (90 total), 91% coverage maintained.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:16:31 -07:00
3467d97828 feat: implement Story 4.2 - New Participant Registration
Implement participant registration with the following features:

- POST handler for /exchange/<slug>/register
- Create Participant record with lowercased email
- Generate magic token and send confirmation email
- Redirect to success page after registration
- Rate limiting: 10 registrations per hour per IP
- Validation for exchange state (must be registration_open)
- Form validation for required fields (name, email)
- Email format validation
- Optional fields support (gift_ideas, reminder_enabled)

Also includes:
- Registration success page template
- 429 error handling template
- Flash message support in base template
- Test config update for email service dev mode
- Comprehensive test suite with 8 tests

All tests passing (86 total), 91% coverage maintained.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:14:11 -07:00
81e2cb8c86 feat: implement Story 4.1 - Access Registration Page
Allows potential participants to view exchange details and access
the registration form via unique slug URLs.

Implementation:
- Added ParticipantRegistrationForm with name, email, gift_ideas, and reminder fields
- Created GET /exchange/<slug>/register route
- Built responsive registration template with exchange details
- Exempted participant routes from admin setup requirement
- Comprehensive test coverage for all scenarios

Acceptance Criteria Met:
- Valid slug displays registration form with exchange details
- Invalid slug returns 404
- Form includes all required fields with CSRF protection
- Registration deadline is displayed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 17:05:09 -07:00
abed4ac84a feat: add EmailService with Resend integration and dev mode
Implements a flexible email service that integrates with Resend API
and supports development mode for easier testing.

Features:
- Resend API integration for production email delivery
- Development mode that logs emails instead of sending
- Magic link URL logging in dev mode for testing
- Helper methods for magic link and registration emails
- Comprehensive test coverage (100% for service)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 16:58:17 -07:00
eaafa78cf3 feat: add Participant and MagicToken models with automatic migrations
Implements Phase 2 infrastructure for participant registration and authentication:

Database Models:
- Add Participant model with exchange scoping and soft deletes
- Add MagicToken model for passwordless authentication
- Add participants relationship to Exchange model
- Include proper indexes and foreign key constraints

Migration Infrastructure:
- Generate Alembic migration for new models
- Create entrypoint.sh script for automatic migrations on container startup
- Update Containerfile to use entrypoint script and include uv binary
- Remove db.create_all() in favor of migration-based schema management

This establishes the foundation for implementing stories 4.1-4.3, 5.1-5.3, and 10.1.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 16:23:47 -07:00
5201b2f036 Release v0.1.0 - Phase 1 MVP
Phase 1 complete with 8 stories:
- 1.1 Initial Admin Setup
- 1.2 Admin Login
- 1.4 Admin Logout
- 2.1 Create Exchange
- 2.2 View Exchange List
- 2.3 View Exchange Details
- 2.6 Generate Registration Link
- 3.1 Open Registration

QA tested and verified with Playwright E2E tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 14:04:48 -07:00
a08cc51f34 chore: add podman-compose.yml for container orchestration 2025-12-22 13:16:56 -07:00
6dbc84a04c feat: add containerization support
- Add Containerfile with multi-stage build for minimal image
- Add .containerignore to exclude unnecessary files
- Add /health endpoint for container health checks
- Update main.py to expose Flask app for gunicorn

Uses Python 3.12-slim base, runs as non-root user, exposes port 8000.
2025-12-22 13:10:47 -07:00
e8e30442d8 Merge feature/3.1-open-registration 2025-12-22 13:06:12 -07:00
cc865a85dc feat: implement Story 3.1 Open Registration
Add complete implementation with tests:
- New route POST /admin/exchange/<id>/state/open-registration
- State validation (only from draft state)
- Success/error messages
- Authentication required
- Update exchange detail template with "Open Registration" button
- 8 comprehensive integration tests

All acceptance criteria met:
- "Open Registration" action available from Draft state
- Exchange state changes to "Registration Open"
- Registration link becomes active
- Participants can now access registration form
- Success message displayed
- Only admin can perform action
- Redirects to exchange detail after completion

Story: 3.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 13:06:00 -07:00
3b0257c377 Merge feature/2.6-generate-registration-link 2025-12-22 13:00:26 -07:00
2f667f3718 feat: add comprehensive tests for Story 2.6 Generate Registration Link
Add integration tests covering all acceptance criteria:
- Unique link generated for each exchange
- Link is copyable to clipboard
- Link is displayed when exchange is in appropriate state
- Link leads to registration page for that specific exchange
- Link uses slug instead of numeric ID for security
- Link contains full URL for easy sharing

Story: 2.6

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 13:00:15 -07:00
ca85f93f39 Merge feature/2.3-view-exchange-details 2025-12-22 12:54:29 -07:00
a695095713 feat: add comprehensive tests for Story 2.3 View Exchange Details
Add integration tests covering all acceptance criteria:
- Clicking an exchange opens detail view
- Shows all exchange information
- Shows list of registered participants
- Shows current state
- Shows registration link (when applicable)
- Handle missing optional fields gracefully
- Navigation back to dashboard

Story: 2.3

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 12:54:18 -07:00
f11c1ff104 Merge feature/2.2-view-exchange-list 2025-12-22 12:50:22 -07:00
f2dc3dfccf feat: add comprehensive tests for Story 2.2 View Exchange List
Add integration tests covering all acceptance criteria:
- Dashboard shows list of all exchanges
- Each exchange displays name, state, participant count, exchange date
- Exchanges sorted by exchange date (upcoming first)
- Visual indicator for exchange state
- Summary counts for draft, active, completed
- Empty state handling

Story: 2.2

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 12:49:56 -07:00
90c603a11d Merge feature/2.1-create-exchange into release/v0.1.0 2025-12-22 12:41:49 -07:00
8554f27d86 feat: implement exchange creation
Add Exchange model, API endpoint, and form validation
for creating new gift exchanges.

- Create ExchangeForm with timezone validation
- Add admin routes for creating and viewing exchanges
- Generate unique 12-char slug for each exchange
- Validate registration/exchange dates
- Display exchanges in dashboard
- All tests passing with 92% coverage

Story: 2.1
2025-12-22 12:41:28 -07:00
7580c39a84 docs: update git workflow to release-based branching
- Rename master to main
- Add release branch workflow (release/vX.Y.Z)
- Feature branches now created from release branches
- Release branches merge to main when confirmed good
2025-12-22 12:27:05 -07:00
f3de9d1ee4 Merge stories 1.1, 1.2, 1.4 into main 2025-12-22 12:24:46 -07:00
e9108c05d5 feat: add logout button to admin interface (Story 1.4)
Add logout button to admin dashboard and test to verify its presence.
This completes the missing acceptance criterion for Story 1.4:
"Logout option available from admin interface"

Changes:
- Add logout form with CSRF protection to dashboard header
- Add integration test to verify logout button is present
- All tests pass (24/24)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 11:59:11 -07:00
6764455703 feat: implement admin login
Implement Story 1.2 (Admin Login) with full TDD approach including:

- RateLimit model for tracking authentication attempts
- LoginForm for admin authentication with email, password, and remember_me fields
- Rate limiting utility functions (check, increment, reset)
- admin_required decorator for route protection
- Login route with rate limiting (5 attempts per 15 minutes)
- Logout route with session clearing
- Admin dashboard now requires authentication
- Login template with flash message support
- 14 comprehensive integration tests covering all acceptance criteria
- Email normalization to lowercase
- Session persistence with configurable duration (7 or 30 days)

All acceptance criteria met:
- Login form accepts email and password
- Invalid credentials show appropriate error message
- Successful login redirects to admin dashboard
- Session persists across browser refreshes
- Rate limiting after 5 failed attempts

Test coverage: 90.67% (exceeds 80% requirement)
All linting and type checking passes

Story: 1.2

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 11:53:27 -07:00
6a2ac7a8a7 feat: implement initial admin setup (Story 1.1)
Add complete initial admin setup functionality including:
- SetupForm with email, password, and password confirmation fields
- Password validation (minimum 12 characters) and confirmation matching
- Email format validation
- Setup route with GET (display form) and POST (process setup) handlers
- bcrypt password hashing before storing admin credentials
- Auto-login after successful setup with session management
- First-run detection middleware that redirects to /setup if no admin exists
- Setup page returns 404 after admin account is created
- Base HTML template with Pico CSS integration
- Admin dashboard placeholder template
- 404 error template

All tests pass with 90.09% code coverage (exceeds 80% requirement).
Code passes ruff linting and mypy type checking.

Story: 1.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 11:40:38 -07:00
b077112aba chore: initial project setup
Initialize Sneaky Klaus project with:
- uv package management and pyproject.toml
- Flask application structure (app.py, config.py)
- SQLAlchemy models for Admin and Exchange
- Alembic database migrations
- Pre-commit hooks configuration
- Development tooling (pytest, ruff, mypy)

Initial structure follows design documents in docs/:
- src/app.py: Application factory with Flask extensions
- src/config.py: Environment-based configuration
- src/models/: Admin and Exchange models
- migrations/: Alembic migration setup

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-22 11:28:15 -07:00