Canonical sources: Portal philosophy, posture, and language live in
/docs/planning/champion-portal/source/README.md.
Use these sources (includingCHRIST_CENTERED__IDENTITY_STATEMENT.md) when writing specs or user-facing copy. Prefer quoting/paraphrasing over inventing new language.
Status: ✅ Complete
Started: January 2026
Completed: January 2026
Priority: High — Blocks new champion onboarding experience
Dependencies: Phase 9.1-9.3 (Onboarding flow, seeded questions, admin tooling)
This phase implements a “trust-first” permissions model where verification unlocks contribution, not belonging. New Champions should be able to view, join, and read community content immediately — verification unlocks more advanced features like directory browsing, messaging, and reactions.
The community step during wizard onboarding is gated behind status_champion_verified? in three places, creating a chicken-and-egg problem:
should_see_community_step? requires verificationgenerate_community_suggestions_if_needed returns early if not verifiedtrigger_community_detection_for_step returns early if not verifiedResult: Unverified champions NEVER see the community step, even though:
| Feature | Requires Verification? | Rationale |
|---|---|---|
| Browse Champion Directory | ✅ Yes | Browsing alumni requires verified identity |
| Send Messages | ✅ Yes | 1:1 contact requires verified identity |
| Post Reactions/Comments | ✅ Yes | Contributing to discussions requires verification |
| Create Events/Posts | ✅ Yes | Content creation requires verified identity |
| Feature | Requires Verification? | Rationale |
|---|---|---|
| View own profile | ❌ No | Self-service |
| View community pages | ❌ No | Read-only doesn’t expose others |
| Join communities (district/industry/affinity) | ❌ No | Self-reported data |
| View community content (news, events, photos) | ❌ No | Read-only consumption |
| Accept community invitation | ❌ No | Trusted invitation path |
These community types are based on data the champion provides during signup:
| Type | Data Source | Gate |
|---|---|---|
| District | ZIP code → district lookup | None (self-reported) |
| Industry | Selected during wizard | None (self-reported) |
| Affinity | Selected during wizard | None (self-reported) |
These community types require alumni record validation:
| Type | Data Source | Gate |
|---|---|---|
| College | degrees.major.college_code |
Verification OR invitation |
| Major | degrees.major_code |
Verification OR invitation |
User guidance: “I would either have a ‘pending BUID’ field or be willing to fill in the BUID and know that it’s BUID + verification status that really solidifies that BUID belonging to that champion”
Implementation: During wizard, staff can link a BUID to a champion before full verification. The combination of buid IS NOT NULL + status == champion_verified solidifies that the BUID belongs to this champion.
status_champion_verified? gate for district/industry/affinity suggestionsinvited_community in wizard step# Remove verification gate from generate_community_suggestions_if_needed
def generate_community_suggestions_if_needed(step)
return unless step == "community"
# REMOVED: return unless @champion.status_champion_verified?
Cp::CommunitySuggestionService.generate_suggestions(@champion)
end
# Remove verification gate from trigger_community_detection_for_step
def trigger_community_detection_for_step
# REMOVED: return unless @champion.status_champion_verified?
# Run detection for self-reported types (district, industry, affinity) regardless of verification
# Run detection for college/major only if BUID linked
end
def should_see_community_step?
# Old: status_champion_verified? && pending_community_suggestions.any?
# New: Has suggestions OR has invited_community
pending_community_suggestions.any? || invited_community.present?
end
before_action :require_verified_for_contributions!, only: [:join, :leave, :message_members]
def require_verified_for_contributions!
return if current_cp_champion.status_champion_verified?
# Allow viewing, disallow contributing
if action_name.in?(%w[show index])
return # Viewing allowed
end
flash[:notice] = "Complete verification to unlock full community features."
redirect_to verification_pending_path
end
<%# Show communities widget to all champions, not just verified %>
<% if @communities.any? || @suggested_communities.any? %>
<%= render "cp/dashboard/communities_widget" %>
<% end %>
Allow read-only access pre-verification:
def require_verified_for_reactions!
return if current_cp_champion.status_champion_verified?
if request.post? || request.patch? || request.delete?
flash[:notice] = "Complete verification to react and comment."
redirect_back fallback_location: cp_dashboard_path
end
end
| File | Change |
|---|---|
app/controllers/cp/profile_wizard_controller.rb |
Remove verification gates in community step logic |
app/models/cp/champion.rb |
Update should_see_community_step? |
app/controllers/cp/communities_controller.rb |
Tiered access based on verification |
app/controllers/cp/dashboard_controller.rb |
Show communities widget pre-verification |
app/views/cp/dashboard/show.html.erb |
Conditional for communities widget |
app/controllers/cp/discussions_controller.rb |
Read-only pre-verification |
app/controllers/cp/news_posts_controller.rb |
Read-only pre-verification |
# test/controllers/cp/profile_wizard_controller_test.rb
test "unverified champion sees community step with district/industry/affinity suggestions" do
# ...
end
test "unverified champion does not see college/major suggestions without BUID" do
# ...
end
test "unverified champion with linked BUID sees college/major suggestions" do
# ...
end
# test/controllers/cp/communities_controller_test.rb
test "unverified champion can view community page" do
# ...
end
test "unverified champion cannot browse directory" do
# ...
end
test "unverified champion cannot message members" do
# ...
end
Trust-First Philosophy Applied:
Files Modified:
app/controllers/cp/profile_wizard_controller.rb
generate_community_suggestions_if_needed: Removed verification gate — suggestions now generated regardless of statustrigger_community_detection_for_step: Removed verification gate — detection runs for all championsapp/models/cp/champion.rb
should_see_community_step?: Removed verification requirement — now returns true if suggestions exist OR invited_community presentapp/views/cp/dashboard/show.html.erb (line 152)
current_cp_champion.status_champion_verified? gate from community suggestions cardapp/controllers/cp/dashboard_controller.rb (load_community_suggestions)
Files Verified (Already Correct):
app/controllers/cp/communities_controller.rb
require_verified_for_communities!app/controllers/cp/community_suggestions_controller.rb
before_action :require_champion_verified! (line 14)Added consistent verification teaser messaging throughout community views:
Files Modified:
app/views/cp/boards/show.html.erb
unless @is_public_view to if !@is_public_view && current_cp_champion.status_champion_verified?Files Verified (Already Correct):
app/views/cp/discussions/index.html.erb — “New Post” button already gated by status_champion_verified?
app/views/cp/communities/index.html.erb — “Join Now” button already has verification gate with “Join available after verification” message
app/views/cp/discussions/show.html.erb — Has 3 verification checks (lines 116, 156, 178) with proper teaser messages
Teaser Message Pattern:
<div class="bg-gray-50 rounded-xl border border-gray-200 p-6 text-center">
<div class="flex items-center justify-center gap-2 text-gray-600">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/>
</svg>
<span>Comments and reactions are available after your account is verified as a Champion.</span>
</div>
</div>