alumni_lookup

AGENTS.md

This file is intended to help AI coding assistants understand the structure, conventions, and configuration of this Ruby on Rails application. The goal is to reduce investigation time and ensure tools can reason effectively about how to program within this app.

See Also: REPO_OVERVIEW.md for complete architecture documentation and test status.


πŸ› οΈ Framework & Stack

Full Details: See REPO_OVERVIEW.md for complete stack documentation.

Quick Reference: Rails 7.1.5.1 β€’ Ruby 3.2.3+ β€’ PostgreSQL β€’ Tailwind CSS β€’ Hotwire/Turbo β€’ Devise β€’ PgSearch β€’ Kaminari


🧠 Data Model Conventions

Full Details: See MODEL_RELATIONSHIPS.md for complete association documentation.

Key Conventions:

Critical Rule: Association name is :alumni (NOT :alumnus)

# βœ… Correct
EngagementActivity.joins(:alumni)
EngagementActivity.joins(alumni: { degrees: { major: :college } })

# ❌ Wrong
EngagementActivity.joins(:alumnus)
EngagementActivity.joins(alumni: { degrees: :college })  # Skips major

πŸ§ͺ Environment & Config


πŸš€ Features to Know

Full Details: See docs/features/ for complete feature documentation.

Core Features:


🧩 Key Services & Helpers

Service/Helper Purpose
EngagementScoreCalculator Level-based scoring with activity caps
TopEngagedAlumniService Alumni ranking with time period filtering
FiscalYearHelper Fiscal year calculations from degree_date
AlumniFilterService Complex filtering for stats/exports
EngagementStats::* Statistics tab services β†’ See copilot-instructions.md

πŸ”Ž AlumniLookupService: Bulk Alumni Matching

IMPORTANT: Before building any alumni matching/lookup logic, check if AlumniLookupService already handles your use case. It provides:

How Nickname Matching Works

There are two separate lookup paths with different capabilities:

Method What it does Uses Nickname Variations?
find(email:, buid:, name:) Uses preloaded hash index for O(1) lookup ❌ Only exact names
find_name_candidates(first, last) Searches with generate_name_variations() βœ… Expands Maddieβ†’Madeline

Key insight: The preloaded index only stores exact names. Nickname expansion (via NICKNAME_MAP in app/models/concerns/name_variation.rb) happens at search-time in find_name_candidates().

Common pitfall: If find() returns nil, you may still find a match via find_name_candidates(). When exactly 1 candidate is found via nickname expansion, use it as the match:

alumni = lookup_service.find(name: "Maddie Smith")  # Returns nil (no exact match)
candidates = lookup_service.find_name_candidates("Maddie", "Smith")  # Finds "Madeline Smith"

# Use single candidate as the match
if alumni.nil? && candidates&.length == 1
  alumni = candidates.first
end

Performance: Never Expand Variations During Preload

Expanding nickname variations during preload (49K alumni Γ— NICKNAME_MAP iterations) takes 30+ seconds. Keep preload as exact-match only; expand variations at search-time on the smaller search term set.

# ❌ Bad: Expanding variations during preload (slow!)
Alumni.find_each do |a|
  variations = Alumni.generate_name_variations(a.first_name)
  variations.each { |v| @index[v] = a }
end

# βœ… Good: Only expand variations when searching
def find_name_candidates(first_name, last_name)
  name_variations = Alumni.generate_name_variations(first_name)
  name_variations.each do |variant|
    # Search for each variant...
  end
end

πŸ’‘ Naming Conventions

πŸ“› Ruby/Rails Hash Syntax & Eager Loading

Hash Syntax: Use modern Ruby 2.7+ keyword-style hashes with symbol keys.

# βœ… Good
includes(degrees: { major: :college }, champion_signups: [], affinities: [])

# ❌ Avoid mixing styles
includes(:degrees => { :major => :college }, :champion_signups, :affinities)

Eager Loading: For has_many associations, use empty arrays to make intent explicit:

# βœ… Explicit intent - avoids edge cases
Alumni.where(buid: buids)
      .includes(degrees: { major: :college }, champion_signups: [], affinities: [])

πŸ“› Alumni/Alum Terminology Convention

IMPORTANT: The word β€œalumni” is Latin and the same in singular and plural.

Instance Variable Convention:

# βœ… Correct controller patterns
def show
  @alum = Alumni.find_by!(buid: params[:id])
end

def search
  @alumni = Alumni.search(query).page(params[:page])
end

# βœ… Correct fixture accessor in tests
test "should show alumni" do
  alum = alumni(:john_doe)
  get alumni_url(alum.buid)
  assert_response :success
end

πŸ” Debugging Tips

Model Association Errors: See MODEL_RELATIONSHIPS.md for detailed error messages and solutions.

Common Issues:

πŸ–ΌοΈ Local Development: VIPS Library

Photo uploads require the native VIPS library for image processing.

Error: LoadError: Could not open library 'vips.42'

Solution:

# macOS
brew install vips

# Linux
apt-get install libvips-dev

Note: Heroku includes VIPS automatically; this is only needed for local development.

🚨 Development-Specific Debugging

Based on lessons learned from actual development issues encountered in this project.

🎯 Research-First Development Pattern

Before implementing any feature, always research existing patterns in the codebase:

1. Check Existing Implementations

# Before adding pagination, check how it's used elsewhere
grep -r "paginate" app/views/
grep -r "page(" app/controllers/

# Before adding CSV exports, see existing patterns
grep -r "respond_to.*csv" app/controllers/
grep -r "CSV.generate" app/

# Before adding routes, see similar route structures
grep -A5 -B5 "resources.*except" config/routes.rb

2. Verify Dependencies and Configurations

# Check what's actually installed and configured
grep -i kaminari Gemfile                    # Pagination gem
grep -i devise Gemfile                      # Authentication
grep -r "config.active_storage" config/     # File upload config

3. Study Similar Features

πŸ”„ Incremental Development & Testing

Never build a complete feature without testing each component individually.

Step-by-Step Development Process:

  1. Add Route β†’ Test Route
    # Add route to config/routes.rb
    # Test: rails routes | grep new_feature
    
  2. Add Controller Method β†’ Test Method
    # Start with minimal implementation
    def new_feature
      render plain: "Feature works!"
    end
    # Test: curl localhost:3000/path/to/feature
    
  3. Add Basic View β†’ Test Rendering
    <!-- Start with minimal HTML -->
    <h1>New Feature</h1>
    <p>Basic content</p>
    # Test: Visit page in browser
    
  4. Add Features Incrementally β†’ Test Each Addition
    • Add pagination β†’ Test pagination works
    • Add export β†’ Test export works
    • Add styling β†’ Test styling displays correctly

Validation Commands for Each Step:

# Route validation
rails routes | grep feature_name

# Controller validation (basic functionality)
echo "MyController.new.respond_to?(:feature_method)" | rails console

# View validation (template compilation)
# Visit the page and check for ActionView errors

# Feature validation (full functionality test)
curl -I localhost:3000/feature/path

πŸ” Common Pitfall Patterns & Solutions

1. Pagination Issues

2. Missing Dependencies

3. Route Naming Conflicts

4. Association/Join Errors

5. CSS/Styling Inconsistencies

πŸ§ͺ Pre-Deployment Validation Checklist

Before declaring any feature complete:

🏷️ Git Tag Convention

Always create annotated tags with comprehensive release notes (not brief one-liners):

git tag -a v1.0.8 -m "Release v1.0.8: Feature Title

Feature Name (Full Feature):
- Phase 1: Description
- Phase 2: Description
- What was added

Search Enhancements:
- New scopes or filters
- API changes

Fixes & Maintenance:
- Bug fixes
- Rake tasks added
- Documentation updates

Pre-deployment:
- Rake tasks to run before/after deploy
- Manual steps needed

733 tests passing"

The tag message appears on the GitHub Releases page and serves as the official changelog.

🚨 Emergency Debugging Commands

When something breaks, use these commands to quickly diagnose:

# Check route exists
rails routes | grep problematic_route

# Check controller method exists
echo "YourController.instance_methods.include?(:method_name)" | rails console

# Check model relationships
echo "YourModel.reflect_on_all_associations.map(&:name)" | rails console

# Check database state
echo "YourModel.count" | rails console

# Check gem availability
echo "defined?(Kaminari)" | rails console

# Check recent logs
tail -n 50 log/development.log

πŸ“š Documentation Update Requirements

When encountering and fixing any issue:

  1. Add to this debugging section with:
    • Problem description
    • Error symptoms
    • Diagnostic commands
    • Solution steps
  2. Update feature documentation with:
    • Known issues and workarounds
    • Testing instructions
    • Common configuration problems
  3. Cross-reference related docs to help future developers find solutions quickly

🧼 Dev Tooling


❓Common Tasks


⚑ Performance & Memory (Heroku)

Context: App runs on Heroku with memory-constrained dynos. These patterns prevent R14 (memory quota) and H12 (timeout) errors.

Memory-Safe Coding Patterns

Pattern Why
Avoid .to_a on large queries Loads entire dataset into memory
Batch processing (50 records) Limits memory per iteration
Force filtering for large datasets Show warning if > 5,000 records unfiltered
Limit processing to 1,000 max Prevents runaway queries
4-hour caching for expensive calcs Reduces repeated computation

Heroku Configuration

# config/puma.rb - Memory-optimized settings
workers 1          # Single worker (not auto-detect)
threads 3, 3       # Fixed thread count

Timeout Protection

Monitoring Commands

# Monitor memory and performance
heroku logs --tail | grep -E "(Allocations|ActiveRecord|Completed)"

# Check memory usage
heroku run rake maintenance:memory_check

# Clean up sessions (run daily via Heroku Scheduler)
heroku run rake maintenance:cleanup_sessions

Database Performance

Future Optimization Options


πŸ“š Documentation Workflow

REQUIRED: All features and bug fixes must include documentation updates.

πŸ†• For New Features:

  1. Create or update feature docs in docs/features/
    • Include purpose, implementation details, and usage examples
    • Document any new configuration requirements
    • Add troubleshooting section for complex features
  2. Update model documentation in docs/development/MODEL_RELATIONSHIPS.md
    • Add new ActiveRecord associations
    • Document any new foreign key relationships
    • Include join patterns for complex queries
  3. Update setup documentation if configuration changes are needed
    • Add environment variables to setup guides
    • Update database migration requirements
    • Document any new dependencies

πŸ› For Bug Fixes:

  1. Document root cause and solution
    • Add to relevant debugging sections
    • Include error messages and their solutions
    • Update troubleshooting guides
  2. Update association documentation for model-related fixes
    • Correct any relationship errors in MODEL_RELATIONSHIPS.md
    • Add examples of proper join syntax
    • Document common pitfalls and solutions
  3. Enhance debugging guides
    • Add new error patterns to debugging sections
    • Include Rails console commands for verification
    • Update common mistake examples

πŸ“ Documentation Standards:

πŸ” Documentation Checklist: