Changelog
All notable changes to the Alumni Lookup application are documented here.
The format follows Keep a Changelog.
[Unreleased]
Added - Champion Portal Phase 1C (Champion Role Selection & Quiz)
- ChampionRoleService — Single source of truth for 4 champion roles
- Connection Advisor (🤝), Digital Ambassador (🙌), Community Builder (👋), Giving Advocate (🙏)
- Metadata includes title, description, detailed activities, colors
narrative_for generates personalized role descriptions
- ChampionQuizService — 7-question role discovery quiz
- Answer scoring algorithm maps responses to role recommendations
calculate_primary_role finds most frequent match
generate_results returns primary role, all roles with percentages, narrative
- Test Coverage — 42 new tests (24 role service + 18 quiz service)
SubPhase 1C.2: Profile Wizard Integration
- Champion Role Selection in Profile Wizard — Integrated role discovery into onboarding
- Added
champion_role step as 2nd step in wizard (after basic_info)
- Role selection page with 4 role cards showing emoji, title, and description
- “Take the Quiz” option to discover role through 7-question assessment
- Manual role selection option for champions who already know their role
- Updated wizard progress indicator to 6-step icon-only design
- Champion Role Quiz Flow — Interactive 7-question assessment
- Full-screen quiz interface with progress indicator (Question X of 7)
- Randomized answer order (letters a-d maintained for scoring)
- Session-based answer storage during quiz
- Previous/Next navigation through quiz questions
- Exit quiz option returns to role selection page
- Quiz Results Page — Personalized role recommendation
- Primary role recommendation with emoji, title, and description
- Personalized narrative explaining role match based on quiz responses
- Complete role breakdown showing all roles with percentages
- “Select This Role” button to confirm and continue wizard
- Options to retake quiz or manually choose different role
- Test Coverage — 7 new controller tests for quiz flow
SubPhase 1C.3: Profile Edit Integration
- Champion Role in Profile Edit — Manage role after signup
- Added
champion_role section to profile edit sidebar
- Current role displayed in colored card with emoji when set
- “Choose a different role” expandable section with role cards
- “Retake the Quiz” link for role rediscovery
- Context-Aware Quiz Navigation — Quiz remembers where you came from
- Session-based referrer tracking (
session[:quiz_referrer])
quiz_return_path helper determines correct return destination
- Exit Quiz returns to Profile Edit when started from Profile Edit
- Exit Quiz returns to Wizard when started from Wizard
- “Choose a different role” on results page respects referrer
- Wizard Completion Logic — Updated profile sequencing
next_incomplete_wizard_step includes champion_role after basic_info
- Dashboard/profile links guide champions through role selection
- Test Coverage — Updated profile controller tests + model tests
UX Improvements
- Simplified Name Fields — Less bureaucratic onboarding flow
- Primary fields: First Name and Last Name (cleaner layout)
- Collapsible “Add additional name details” section for optional fields:
- Legal First Name (if different from preferred name)
- Last Name at Belmont (maiden name for searchability)
- Data Architecture: Clean validation requiring at least one name field
- Custom validation
at_least_one_name_present (pref_first_name OR first_name)
- No data duplication: doesn’t store same value in both fields
display_first_name method falls back from pref_first_name to first_name
- Backward compatible with existing data that only has first_name
- Stimulus controller
toggle-legal-name handles expand/collapse with animated icon
- Redesigned Role Selection UI — Clear state-based display
- When role IS set: Shows current role prominently in colored card
- When role NOT set: Shows quiz CTA prominently with hidden manual selection
- Stimulus controller
toggle-role-selection for expandable role options
- Bio/About Me Field — Added to basic_info step and profile edit
- Optional textarea for champions to introduce themselves
- Helps other champions connect and find common interests
- Profile Photo Management — Enhanced photo editing
- “Edit Photo” link overlays profile photo on hover in profile header
- “Remove Photo” button on edit page (only shown when photo exists)
- Removed photo from sidebar navigation (accessible via header)
Changed
- Wizard Flow — Expanded from 5 to 6 steps with champion_role as step 2
- Progress Indicator — Changed to icon-only circles (removed step text for space)
- Tailwind v4 Compatibility — Used CSS variable syntax for custom colors throughout
- Name Validation — Changed from
validates :first_name to validates :pref_first_name
- Quiz Display — Questions render with HTML entities, answers in randomized order without letter labels
- “Continue as [Role]” button to select role and advance wizard
- “Retake Quiz” and “Choose a different role” secondary actions
- “What’s Next?” informational section
Changed - Champion Portal SubPhase 1C.2
- ProfileWizardController — Updated for 6-step flow
- STEPS constant now includes
champion_role (6 steps instead of 5)
- Added 4 quiz-related actions:
quiz, save_quiz_answer, quiz_results, select_quiz_role
- Quiz answers stored in session during multi-question flow
- Session cleared after role selection
- Routes — Quiz routes added before wildcard :step route
- Added
GET /profile/wizard/quiz/:question for quiz questions
- Added
POST /profile/wizard/quiz/:question for answer submission
- Added
GET /profile/wizard/quiz-results for results display
- Added
POST /profile/wizard/select-role for role selection
- Critical: Quiz routes placed before
profile/wizard/:step to prevent route conflict
- Tests — 7 new integration tests for quiz flow
- Test quiz question display and navigation
- Test answer submission and advancement
- Test redirect to results after final question
- Test results page display
- Test role selection and wizard advancement
- Test session clearing after role selection
- Test quiz retake from beginning
- Updated existing tests for 6-step wizard flow
Technical
- All 912 tests passing (added 7 tests, updated 2 tests)
- Views created:
_step_champion_role.html.erb, quiz.html.erb, quiz_results.html.erb
- Route ordering critical: specific quiz routes must come before wildcard :step route
Added - Champion Portal SubPhase 1.4
- Profile Wizard — Linear onboarding flow for new champions
- 5-step wizard: Basic Info → Location & Contact → Professional → Affinities → Photo
- Progress indicator showing current step and completion
- Auto-advances to next step on successful save
- “Back” and “Skip” navigation options
- Redirects to dashboard when complete
- Profile Edit (Section-Based) — Tabbed editing for returning users
- Sidebar navigation with 5 sections matching wizard steps
- Stays on current section after save (no auto-advance)
- Completion checkmarks on sidebar for finished sections
- Accessible via “Edit Profile” from profile show page
- Profile Show (“How Others See You”) — Directory-style preview
- Shows champion’s profile as it appears to other champions
- Privacy indicators (“Hidden” badges) on private fields
- Education section from linked alumni record (degrees)
- District shown in header
- Contact section hidden entirely if all items are private
- Affinities Selection — Multi-select affinity picker
- Searchable dropdown with all available affinities
- Shows selected affinities with remove buttons
- Syncs to
cp_affinities join table
- Photo Upload — Profile photo with cropping preview
- Drag-and-drop or click to upload
- Client-side preview before save
- Stored via ActiveStorage
- Displayed in directory cards and profile
- OAuth BUID Conflict Resolution — Fixed duplicate account creation
- When OAuth email matches alumni with existing champion account (by BUID)
- Now links OAuth to existing account instead of failing with unique constraint error
- Added 5 tests covering all OAuth scenarios
Changed
- Profile Controller — Enhanced with section-based editing
SECTIONS constant defines available sections
section_params method scopes permitted params per section
- Added
bio to professional section params (was missing)
- Routes — Added section parameter for profile edit
GET /profile/edit(/:section) for section-based navigation
Fixed
- Contact section visibility — No longer reveals hidden information exists
- If all contact items are private, section is completely hidden
- Previously showed “Some information is hidden” message
[1.0.13] - 2025-12-11
Added
- Background scan with preview approval - Large file imports no longer timeout
- Upload triggers
AffinaquestScanJob running in background worker
- Shows “Scanning” progress page with auto-refresh polling
- When complete, redirects to full preview of ALL changes
- User reviews and clicks “Commit Import” to apply
AffinaquestApplyJob applies changes in background
- Redis manifest storage - Manifests shared across Heroku dynos
- New
ManifestStore service stores manifests in Redis with 1-hour TTL
- Solves ephemeral filesystem issue (worker saves, web reads)
- Auto-expires, no database bloat for large manifests
- Worker detection - Warning appears after 2 minutes if scan stuck
- Shows Heroku command to enable worker dyno
possibly_stuck? method on batch model
- Import change audit trail - All field updates logged to
crm_data_changes
- Each field change records old value, new value, and batch ID
- Batch detail page shows all field updates made during import
- New importer methods:
Csv::AffinaquestContactImporter.scan(file) - Read-only analysis, returns manifest
Csv::AffinaquestContactImporter.apply_manifest(manifest, imported_by:) - Targeted updates
- Redis added to CI workflow - ManifestStore tests now pass on GitHub Actions
Changed
- Two-pass import architecture - Complete rewrite of import workflow
- Pass 1 (Scan): Background job streams through file, builds change manifest
- Pass 2 (Apply): Background job processes only records that need changes
- Preview shows ALL changes for entire file, not just a sample
- Manifest stored in Redis (shared across dynos) between preview and commit
- New batch statuses:
scanning, scanned added to workflow
- Flow: pending → scanning → scanned → running → completed/failed
- Routes restructured for batch-based flow:
POST /settings/affinaquest/upload - Starts scan job
GET /settings/affinaquest/batches/:id/scanning - Progress page
GET /settings/affinaquest/batches/:id/preview - Shows manifest
POST /settings/affinaquest/batches/:id/commit - Starts apply job
Fixed
- Critical performance fix for Affinaquest import - Import was timing out on production
- Before: N+1 queries - each field for each row queried database for protection status
- After: Single query preloads all protections into hash for O(1) lookup
- Added
CrmDataChange.preload_protections(buids) for bulk loading
- Chunked processing (500 rows) with GC between chunks
- Preview accuracy - Previous version showed “No records to import” when first 50 rows had no changes
- Now scans entire file so preview reflects actual import results
- Heroku memory/timeout issues - 48K row files were causing R14 (793MB) and H12 (timeout)
- Background jobs run on worker dyno, not web dyno
- Chunked processing minimizes memory usage
Migration
- Added
manifest_path column to affinaquest_import_batches (stores Redis key)
[1.0.12] - 2025-12-10
Added
Fixed
- SSO-generated passwords now meet complexity requirements
- Enhanced matching for maiden names and compound first names in import
Data Fixes Applied
- Restored 97 records overwritten by initial Affinaquest import (78 pref_name, 18 last_name, 1 manual)
- Fixed 11 school email records (moved to email_school, personal emails restored)
[1.0.8 - 1.0.11] - 2025-12-09
- Phase 1: Database migrations for Affinaquest sync infrastructure
- Alumni fields:
email_school, email_personal, email_business, email_other, zip, affinaquest_updated_at, affinaquest_synced_at
- New tables:
affinaquest_import_batches, affinaquest_import_conflicts
- Unified CRM sync tables:
crm_data_changes, crm_data_export_batches
- Phase 2: Models for import and CRM sync
AffinaquestImportBatch - Track import history with lifecycle methods
AffinaquestImportConflict - Handle ID mismatches with resolve/dismiss workflow
CrmDataChange - Unified change tracking for CRM export (Affinaquest conflicts, Champion Portal updates, manual edits)
CrmDataExportBatch - Export batch management with convenience class methods
- Alumni model updates:
filter_by_any_email, filter_by_zip, with_degrees, without_degrees scopes; has_degree?, zip_district, zip_region, all_emails methods
- Phase 3: Import service
Csv::AffinaquestContactImporter - Main import orchestrator
- Full import mode with batch tracking and database writes
- Preview mode for reviewing changes before commit
- Recency-aware field updates (preserves newer local data)
- ID mismatch detection with conflict logging
- CRM sync integration via
CrmDataChange.log_affinaquest_conflict
- Phase 4: Controller & routes
Settings::AffinaquestController with 8 actions (index, preview, commit, conflicts, export, resolve, batches, show_batch)
- Routes at
/settings/affinaquest/* for full import workflow
- Conflict management with resolve/dismiss workflow
- CSV export of conflicts for Advancement Services
- Phase 5: Views & UI
- Upload form with file drag-and-drop and recent batches overview
- Preview page with stats, column mapping, sample rows, and commit button
- Conflicts page with resolve/dismiss modal workflow
- Batch history and details pages
- Settings sub-nav updated with Affinaquest link
- Phase 6: Search & Filtering Updates
- District autocomplete search with state disambiguation (e.g., “TN-04 (TN)”)
- API endpoint:
GET /api/districts/autocomplete?query=...
- Stimulus controller:
district_autocomplete_controller.js
- Multi-email search:
filter_by_any_email scope checks email, email_school, email_personal, email_business, email_other
AlumniLookupService and AlumniMatcher updated for multi-email matching
- Batch search supports matching on any email field
- Phase 7: Testing
- Full test coverage for models, services, and controllers
- 733 tests passing with 0 failures, 0 errors
- Phase 8: Documentation
docs/features/AFFINAQUEST_IMPORT.md - Complete feature documentation
Added - Event RSVP Converter (December 8-9, 2025)
- New utility for converting GiveCampus RSVP exports to event app format
- Accessible at Settings > Event Converter for all authenticated users
- Parses GiveCampus CSV format and transforms to event check-in app format
- Automatic alumni matching via
AlumniLookupService (BUID, BQID, email, name)
- Nickname matching support (e.g., “Maddie” matches “Madeline” via NICKNAME_MAP)
- Duplicate registration detection and removal with stats display
- Resolution UI for ambiguous name matches (select from candidates or confirm as guest)
- Preview with detailed statistics before download
- Smart guest resolution (December 9, 2025):
- Two-pass algorithm to identify registrations with BQID holders
- Empty guest info with BQID → uses registrant name (identifying themselves)
- Empty guest info without BQID on multi-ticket registration → marked as “+1”
- Guest first name = “Guest” → marked as “+1”
- +1 format:
first_name = "John", last_name = "Smith +1"
- Email/Phone comparison with system data:
rsvp_email / system_email / email_differs columns
rsvp_phone / system_phone / phone_differs columns
- Flags “YES” when RSVP data differs from Alumni Lookup data
- Helps identify outdated contact info in the system
- AlumniLookupService enhancements:
- Centralized bulk alumni matching with preload optimization (~3.7s for 49K records)
- Multiple matching strategies: BUID → BQID → Email → Name (in priority order)
- Nickname expansion via
generate_name_variations() at search-time
find_name_candidates() method for ambiguous match resolution
- New
filter_by_bqids scope on Alumni model
- Parallel to existing
filter_by_buids for batch BQID lookups
- New files created:
app/services/csv/event_rsvp_converter.rb — Core conversion logic
app/services/alumni_lookup_service.rb — Centralized alumni matching service
app/controllers/settings/event_converter_controller.rb — Upload, preview, download, resolve actions
app/views/settings/event_converter/new.html.erb — Upload form
app/views/settings/event_converter/preview.html.erb — Preview with resolution UI
docs/planning/event-checkin-integration/04-event-rsvp-converter.md — Feature documentation
- Documentation updates:
- Added AlumniLookupService section to
docs/development/AGENTS.md
- Added BQID UI feature request to
docs/development/TODO_BUGS.md
- Test coverage:
test/services/csv/event_rsvp_converter_test.rb — Unit tests
test/services/alumni_lookup_service_test.rb — Service tests
test/controllers/settings/event_converter_controller_test.rb — Controller tests
test/fixtures/files/givecampus_rsvp_sample.csv — Sample fixture data
Added - Champion Portal Phase 1.3: SSO Authentication (December 8, 2025)
- Google OAuth integration
- “Sign in with Google” button at top of login/signup pages
- OmniAuth configured with
/auth path prefix for multi-model Devise support
- Origin-based CSRF protection for OAuth requests
- Callback routes wrapped in
devise_scope blocks
- Profile management
Cp::ProfileController for profile view/edit
- ZIP code completion prompt for SSO signups (
/profile/complete)
- SSO users redirected to complete ZIP if missing
- Controllers created:
Cp::OmniauthCallbacksController — Google OAuth callback handling
Cp::ProfileController — profile show/edit/complete actions
- Test coverage:
test/controllers/cp/omniauth_callbacks_controller_test.rb
test/controllers/cp/profile_controller_test.rb
- Future enhancements deferred:
- Apple Sign In integration
- Facebook OAuth integration
- Account linking (SSO to existing email account)
Added - Champion Portal Phase 1.2: Email Authentication (December 5, 2025)
- Email-based account creation (progressive signup)
- Champions enter name + email + zip code, password set after email confirmation
- Custom Devise controllers in
Cp:: namespace for full control
- Mobile-first TailwindUI forms for signup, login, password reset
- Custom Champion Mailer
Cp::ChampionMailer for Champion Portal emails using champions subdomain
- Branded HTML+text templates: confirmation, password reset, email change, password change
- Uses champion’s first_name in greetings
- Development URL:
champions.bualum.dev:3000
- Champion Dashboard
- Profile completion card for incomplete profiles
- Quick stats: district, verification status, member since
- Quick action cards (placeholders for Phase 1.4+)
- Auth-aware layout with navigation
- Header Navigation
- Alumni Champions logo from
/public/alumni-champions.svg
- Desktop profile dropdown with Stimulus controller
- Mobile hamburger menu with toggle
- Sign out functionality in both desktop and mobile
- Stimulus Controllers
dropdown_controller.js - Desktop profile dropdown with click-outside close
mobile_menu_controller.js - Mobile hamburger menu toggle
- Controllers created:
Cp::BaseController - feature flag checking, layout setup
Cp::RegistrationsController - progressive signup flow
Cp::SessionsController - login/logout
Cp::ConfirmationsController - email confirm + password setting
Cp::PasswordsController - forgot/reset password
Cp::DashboardController - authenticated dashboard
- Test coverage (53 tests, 119 assertions):
test/controllers/cp/registrations_controller_test.rb (4 tests)
test/controllers/cp/sessions_controller_test.rb (5 tests)
test/controllers/cp/dashboard_controller_test.rb (4 tests)
test/controllers/cp/confirmations_controller_test.rb (8 tests)
test/controllers/cp/passwords_controller_test.rb (8 tests)
test/models/cp/champion_test.rb (14 tests)
test/mailers/cp/champion_mailer_test.rb (10 tests)
Added - Champion Portal Phase 1.1: Database & Models (December 5, 2025)
- Foundation tables migration
regions - 7 geographic regions
districts - 820 metro/micro areas
zip_codes - 39,305 ZIP codes with city/state
cp_champions - Champion Portal user accounts
cp_profile_changes - CRM export changelog
- Geographic seed data
- Rake task:
rails champion_portal:seed_geographic_data
- Two-pass import: assigns cross-region districts to majority region
- 14 multi-region MSAs handled correctly
- Model enhancements:
Cp::Champion with Devise authentication (email auth only for now)
- Custom mailer configuration via
devise_mailer method
- Verification status enum: unverified → email_verified → champion_verified
Region, District, ZipCode models with associations
Alumni → Cp::Champion association via BUID
Pending
- Champion Portal Phase 1.3: SSO Authentication (Google, Apple, Facebook)
- Champion Portal Phase 1.4: Profile & Directory
- Champion Portal Phase 1.5: Dashboard & Admin verification queue
- Event check-in integration (see
planning/event-checkin-integration/)
[1.0.7] - 2025-12-09
Added
- Rake task
alumni:cleanup_duplicate_buids to handle duplicate BUID records before adding unique index
[1.0.6] - 2025-12-08
Fixed
- Redirect champions.bualum.co to signup flow when Champion Portal feature flag is disabled
[1.0.5] - 2025-12-08
Added
- Event RSVP Converter - See “Added - Event RSVP Converter” section above
- Champion Portal Phase 1.3: SSO Authentication - See “Added - Champion Portal Phase 1.3” section above
[1.0.4] - December 4, 2025: Bug Fixes & Test Coverage Improvements ✅
Addressed TODO items: fixed bugs, added validation, and expanded test coverage.
Fixed
- Alumni affinities modal flow - Added dropdown selector for direct navigation (non-JS fallback)
- File:
app/views/alumni_affinities/_form.html.erb
- Users navigating directly to affinity form can now select affinities from grouped dropdown
- Edit mode continues to display the selected affinity name as before
Added
- EngagementActivity activity_code validation
- Model now validates
activity_code against registered EngagementType codes
- Invalid codes are rejected with clear error message
- Added cache for valid codes with
reset_valid_activity_codes_cache! method
- File:
app/models/engagement_activity.rb
- Controller test coverage
test/controllers/engagement_activities_controller_test.rb - 12 tests
- Authentication, staff access, filtering, statistics display
test/controllers/engagement_stats_controller_test.rb - 21 tests
- All 7 tabs, filters, cache operations, exports
test/controllers/batch_search_controller_test.rb - 21 tests
- Single/multiple names, emails, empty input, large batches (50-100 names)
- Special characters, accents, CSV export, Turbo Stream responses
- Model test coverage
test/models/engagement_activity_test.rb - 13 tests
- activity_code validation, associations, scopes, point calculation
Verified
- Instance variable naming convention - Confirmed
@alum/@alumni usage is consistent across all controllers
[1.0.3] - December 3, 2025: EngagementStats Service Refactoring & Feature Flags ✅
Complete refactoring of engagement stats to use dedicated services, fixing duplicate counting bug.
Fixed
- Demographics discrepancy - Goal #2 showed 283 engaged alumni while Demographics tab showed 461
- Root cause: Demographics counted degree records instead of unique alumni by BUID
- All 6 tabs now count unique alumni consistently
- Matrix scoring consistency - Now matches
EngagementScoreCalculator exactly
- Removed obsolete +5 attendance bonus from MatrixService
- Added proper SQL subqueries for activity-type caps (email_click: 5, event_rsvp: 2)
- Fixed incorrect hardcoded point values (was using wrong LEVEL_POINTS)
- Activity Pairs sorting - Now sorts by count DESC (was incorrectly sorting by level first)
Changed
- EngagementStatsController reduced from 1,755 lines → 745 lines (~1,010 lines removed)
- All tabs now delegate to dedicated services:
OverviewService - Goal #1, Goal #2, Goal #3-5 metrics
DemographicsService - Year/college/major breakdowns (uses COUNT(DISTINCT buid))
BreakdownService - Activity breakdown by role/level
MatrixService - Quadrant analysis with engagement/giving axes
AnalyticsService - Charts, score distributions, monthly activity stacks
ActivityPairsService - Activity combination analysis
- Smoke test delay reduced from 30s to 15s
Added
- Test coverage for all new services (111 service tests total, 409 tests overall)
test/services/engagement_stats/breakdown_service_test.rb
test/services/engagement_stats/matrix_service_test.rb
test/services/engagement_stats/analytics_service_test.rb
test/services/engagement_stats/activity_pairs_service_test.rb
test/services/engagement_stats/scoring_integration_test.rb - Ensures scoring consistency
- Enhanced
AnalyticsService with monthly_activity_stacks for stacked bar chart data
- Services use
BaseService#with_caching for consistent 1-4 hour cache expiration
Technical Notes
- All services inherit from
EngagementStats::BaseService
- Key rule enforced: Count unique alumni by BUID, not degree records
capped_level_to_points_sql now applies activity-type caps via SQL subqueries
- MatrixService fixed for ambiguous
buid column in joined queries
- ActivityPairsService fixed:
EngagementType.all.index_by (not .index_by directly)
Added (Feature Flags)
- Feature flag infrastructure for safe feature rollouts
feature_enabled? helper available in controllers and views
- ENV variable overrides for quick enable/disable without deploy
champion_portal flag ready for Champion Portal development
- See
docs/development/FEATURE_FLAGS.md for usage
Removed
- 9 unused helper methods across 4 files
- Duplicate
lib/tasks/populate_champion_roles.rb
Upgraded Ruby for 15-25% performance improvement.
Changed
- Ruby 3.2.3 → 3.3.8 with YJIT enabled on Heroku
- Added
csv gem proactively (becomes non-default in Ruby 3.4)
- All 298 tests passing
[1.0.1] - December 2, 2025: CI/CD Pipeline Complete ✅
Full CI/CD pipeline implemented with staging environment.
Added
- CI Workflow (
.github/workflows/ci.yml)
- Automated testing on every push
- PostgreSQL 14 service container
- Ruby 3.2.3 + Node 18 setup
- 298 tests passing
- Staging Environment (
alumni-lookup-staging)
- Heroku app with PostgreSQL Essential 0
- Custom domains: lookup-staging.bualum.co, champions-staging.bualum.co
- Auto-deploy on push to main (after CI passes)
- Database copied from production for realistic testing
- Deploy Workflows
deploy-staging.yml - Triggers after CI passes on main
deploy-production.yml - Triggers on version tags (v*)
- Smoke tests verify endpoints after deploy
- Database Backups
- Production: Daily at 2:00 AM CT (7-day retention)
- Staging: Daily at 3:00 AM CT
- Monitoring
- UptimeRobot alerts for both domains
- Heroku built-in metrics and logs
- Documentation
docs/operations/DEPLOY_GUIDE.md - Step-by-step deploy instructions
docs/operations/ENV_SETUP_IMPLEMENTATION_CHECKLIST.md - Full setup checklist
Fixed
- Migrations that loaded Alumni model before columns existed (converted to raw SQL)
- Deploy workflow using
HEAD:main for tag-based production deploys
Pre-Release Versions
The following versions were developed before CI/CD was established.
They represent feature milestones but were not formally tagged releases.
[1.0.0-alpha.3] - November-December 2025: Authentication & Roles ✅
All 6 phases completed. Foundation ready for Champion Portal and Event Check-in.
Added
- Phase 6: Permission Flags Infrastructure — Patterns established for future permission flags
- No code changes needed; Phases 1-5 established all required patterns
- Documented pattern for adding
can_* boolean flags with admin bypass
- Ready for Event Check-in to add
can_event_checkin, can_event_manage flags
- Phase 5: Google SSO for Internal Users — Optional Google account linking for staff
- Added
omniauth-google-oauth2 and omniauth-rails_csrf_protection gems
- Added
google_uid and google_linked_at columns to users table
- Created
Users::OmniauthCallbacksController for Google OAuth2 callback
- Added
google_connected?, unlink_google!, and from_google_oauth methods to User model
- Login page shows “Sign in with Google” button when configured
- Profile page allows users to link/unlink their Google account
- Security: Only existing users can sign in with Google (admin creates accounts first)
- Auto-links Google UID on first SSO sign-in for existing users
- Added 13 integration tests for Google SSO flows
- Production Setup: Set
GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET env vars on Heroku
- Production Fixes:
- Moved Google Sign-In section outside main form to avoid nested form issues
- Added custom OmniAuth origin-based validation to handle fresh session CSRF edge cases
- Configured session store with
same_site: :lax for OAuth compatibility
- Phase 4: Forgot Password Flow — Self-service password reset now working
- Configured custom Mailgun API delivery method (
MailgunDeliveryMethod class)
- Fixed Devise
mailer_sender from placeholder to noreply@email.bualum.co
- Fixed ChampionSignupMailer to use instance method for
default_url_options (was polluting other mailers)
- Production uses
lookup.bualum.co for Devise emails, champions.bualum.co for Champion emails
- Fixed Mailgun initializer to only load in production (was breaking tests)
- Styled password reset views (
new.html.erb, edit.html.erb) with Tailwind
- Branded password reset email template with Belmont header and styling
- Added “Forgot password?” link to login page (next to Remember me)
- Added 11 integration tests for password reset flow
- Phase 3: Admin vs Staff Separation — Role-based access control now enforced
- Added
ensure_staff! method to ApplicationController for staff-accessible features
- Staff users can now access: Alumni search, Engagement stats, Champion signups, Batch search, Statistics
- Staff users CANNOT access: Settings, User management, Cache clearing, Champion merge/delete
- Updated 7 controllers with
ensure_staff! or mixed staff/admin access
- Added comprehensive
staff_access_test.rb with 18 tests
- All role access comments updated in controllers
- Phase 2: Role Infrastructure — Added role enum to User model
- Added
role column with values: staff (default) and admin
- Migration backfills existing admins with ‘admin’ role
- Added
staff? helper method (returns true for both staff and admin)
- Updated
admin? to check role enum OR legacy admin boolean
- Updated user form with role dropdown (replaces admin checkbox)
- Updated users index to show role badges
- Added 7 new tests for role functionality
- Phase 1: Unified Admin Checks — Consolidated admin authorization pattern
- Added
admin? method to User model that uses the admin boolean
- Added
ensure_admin! method to ApplicationController as standardized authorization callback
- All Settings controllers now use centralized
ensure_admin! instead of local methods
- Removed deprecated
access_level column from users table
- Added 3 tests for admin? method
- Authentication & Roles Implementation Guide — Prerequisite project for Champion Portal and Event Check-in
features/AUTH_AND_ROLES_SYSTEM.md — Complete authentication & role system (moved from planning/)
- Unify admin checks, add role infrastructure, Admin/Staff separation
- Forgot Password flow, Google SSO for internal users
- Permission flags infrastructure for hybrid authorization model
- Event Check-in Integration Planning — Complete integration plan for alum-events functionality
planning/event-checkin-integration/00-overview.md — Problem, goals, recommendation
planning/event-checkin-integration/01-pros-cons-and-questions.md — Pros/cons analysis, pre-integration questions
planning/event-checkin-integration/02-phased-integration-plan.md — Phased development roadmap with Contact/Event/Registrant models
planning/event-checkin-integration/03-champion-portal-interaction.md — Champion Portal alignment and sequencing
- Hybrid Authorization Model — Documented Roles + Permission Flags approach in
PERMISSIONS_MATRIX.md
- Roles define baseline access (Admin, Staff, Champion, CLC)
- Permission flags for cross-cutting features that don’t fit role hierarchy
- Guidelines for when to add roles vs. permission flags
- Implementation patterns for User model and Pundit policies
Known Issues
See development/TODO_BUGS.md for tracked bugs and issues.
[1.0.0-alpha.2] - November 2025: Documentation & Preparation
Added
- Product Overview Documentation — Comprehensive stakeholder-ready overview (
PRODUCT_OVERVIEW.md)
- Champion Portal Planning — Complete specification for future Champion Portal development
- Development Cycle Preparation — Stages 1-5 completed preparing codebase for role-based access
Changed
- Authentication Standardization — All internal controllers now explicitly require authentication
- PublicController Base Class — External/public features inherit from dedicated base class
- EngagementStats Services — Extracted calculation logic into service objects
Fixed
affinity_name helper crash on nil input
- Alumni affinities form crash when
affinity_code is nil
UsersController#destroy — @user not set before action
- Alumni fixture naming (
alumnis.yml → alumni.yml)
Documentation
- Created
ARCHITECTURE.md — Two-domain architecture documentation
- Created
AUTHENTICATION.md — Devise setup and authorization patterns
- Created
API.md — Internal API endpoint documentation
- Created
PERMISSIONS_MATRIX.md — Role-based access blueprint
- Updated test suite to 241 tests, 573 assertions
[1.0.0-alpha.1] - August-October 2025: Champion Signup & Features
Added
- Champion Signup System v1.0 — Multi-step wizard at
champions.bualum.co
- Welcome → Personal Info → Questions → Role Selection → Interests → Completion
- Email notifications (Mailgun) for new signups
- Admin management interface on
lookup.bualum.co
- CSV import/export functionality
- Duplicate detection and merging
- Lifestage interest data cleanup tools
- Alumni Contact ID Integration — Salesforce-compatible Contact IDs (C-000000000 format)
- Alumni Photos — Active Storage with Cloudinary for production
- Accent-Insensitive Search — PostgreSQL
unaccent extension for name searches
- Alumni Prospect Status — Manual prospect flagging with notes
- Top Engaged Alumni — Time-period filtering (30 days, 6 months, 1 year, all time)
- Engagement Scoring Improvements
- Activity caps: email_click (max 5), event_rsvp (max 2)
- Distance formula: √((score × 1.5)² + (capped_activity_count × 1.0)²)
- Removed bonus point system
- Flagship Event Tracking — Identify and track follow-up engagement
- Degree Upload Preview — Preview/commit workflow with auto-create alumni
Changed
- Memory Optimizations — 78% reduction in memory allocations for Engagement Stats
- Tailwind CSS 4.1 — Updated frontend framework
- Batch Search — Added email search capability
- Alumni Search Results — Redesigned with photo thumbnails
Fixed
- Engagement activity import from Emma reports
- Matrix export and chart issues
- Degree details display
- Score chart label issues
[1.0.0-alpha.0] - Early 2025: Initial Development
Added
- Core Alumni Management
- Alumni search with full-text PostgreSQL search
- Alumni profiles with contact information
- Degree tracking linked to majors and colleges
- Engagement Tracking System
- Level-based scoring (0-4 levels)
- Activity tracking with types and dates
- Engagement statistics dashboard
- Affinity Management
- Affinity categories (Athletics, Greek Life, Campus Life, etc.)
- Alumni-affinity associations with roles and years
- Data Import/Export
- CSV import for alumni, degrees, engagement activities
- CSV export for search results
- User Administration
- Devise authentication
- Admin/staff access levels
- User management (admin only)
- Settings Management
- Colleges, majors, affinities configuration
- Engagement types and activity codes
Technical Foundation
- Ruby on Rails 7.1.5.1
- PostgreSQL database
- Tailwind CSS with TailwindUI
- Hotwire/Turbo for partial page updates
- Import maps (no JavaScript bundler)
- Heroku deployment with S3/Cloudinary
Pre-1.0 Development
Initial Commit - Late 2024
- Project scaffolding and initial models
- Basic user authentication
- Alumni and degree models
- Settings pages framework
Version Numbering
This project uses semantic versioning:
- Major (X.0.0) — Significant new capabilities or breaking changes
- Minor (0.X.0) — New features, enhancements
- Patch (0.0.X) — Bug fixes, minor improvements
- Pre-release (-alpha.X) — Development versions before CI/CD was established
Version History
| Version |
Date |
Milestone |
| 1.0.0-alpha.0 |
Early 2025 |
Initial development |
| 1.0.0-alpha.1 |
Aug-Oct 2025 |
Champion Signup system |
| 1.0.0-alpha.2 |
Nov 2025 |
Documentation & prep |
| 1.0.0-alpha.3 |
Nov-Dec 2025 |
Auth & Roles system |
| 1.0.1 |
Dec 2, 2025 |
CI/CD Pipeline (first tagged release) |
| 1.0.2 |
Dec 2, 2025 |
Ruby 3.3.8 + YJIT |
| 1.0.3 |
Dec 3, 2025 |
Service refactoring + Feature flags |