Skip to content

Latest commit

 

History

History
218 lines (159 loc) · 10.1 KB

File metadata and controls

218 lines (159 loc) · 10.1 KB

Gamification & Multi-Tenant Analysis

Date: February 17, 2026 Status: RESOLVED — Gamification is fully tenant-scoped (migration 20260217030000)


Executive Summary

The gamification system is fully tenant-scoped. All 7 data tables now have tenant_id NOT NULL, and the 2 definition tables (gamification_achievements, gamification_store_items) have optional tenant_id for school-specific customization. Features are gated by plan tier to create an upsell path.

What this means:

  • A student in "Code Academy Pro" and "Default School" has separate XP, levels, streaks, and coin balances
  • Leaderboards only show students from the current school
  • Achievements are tracked independently per tenant
  • Advanced features (leaderboard, achievements, store) are locked behind plan tiers

Current Gamification Features

Feature DB UI Logic Tenant-Aware Plan Gate
XP Awards (6 triggers) Yes Yes Yes YES All plans
20 Levels (auto-progression) Yes Yes Yes Global (OK) All plans
Streaks (daily activity) Yes Yes Yes YES All plans
21 Achievements (5 tiers) Yes Yes Yes YES Basic+
Coins + Store (6 items) Yes Yes Yes YES Professional+
Leaderboard (mini view) Yes Yes Yes YES Basic+
Challenges Schema only No No YES Professional+
Power-Up Rewards Schema only No Partial YES Professional+

What's Working Well

  • XP triggers fire automatically on lesson completion (100 XP), exam submission (200+ XP), exercise (50 XP), comments (10 XP), reviews (50 XP)
  • Level progression through 20 levels (Newcomer → Transcendent) with icons/titles
  • Streak tracking with freeze power-ups purchasable from store
  • Achievement system with lazy checking via edge function, 21 pre-seeded achievements across 5 categories
  • Store with 6 items, purchase validation, max limits, coin balance management
  • Gamification header in student dashboard shows level, XP bar, streak, coins

What's Missing

  • Challenges — table exists but no data, rules, or UI
  • Power-up UI — rewards are tracked in DB but no interface to view/use active buffs
  • Full leaderboard page — only mini top-5 view exists
  • Course-specific leaderboard — backend supports it, no frontend
  • Level perks — JSONB field reserved but unused

Multi-Tenant Isolation Gap

Tables — Tenant Status (RESOLVED)

Table tenant_id Constraint Status
gamification_profiles NOT NULL UNIQUE(user_id, tenant_id) FIXED
gamification_xp_transactions NOT NULL Indexed FIXED
gamification_leaderboard_cache NOT NULL UNIQUE(user_id, tenant_id) FIXED
gamification_user_achievements NOT NULL UNIQUE(user_id, achievement_id, tenant_id) FIXED
gamification_redemptions NOT NULL Indexed FIXED
gamification_user_rewards NOT NULL Indexed FIXED
gamification_challenge_participants NOT NULL Indexed FIXED
gamification_store_items NULLABLE NULL = global, UUID = school-specific FIXED
gamification_achievements NULLABLE NULL = global, UUID = school-specific FIXED
gamification_levels None Global definitions (intentional) OK

Other Tables Still Missing Direct tenant_id

Table Risk Note
exam_questions HIGH Relies on exam→course→tenant join chain
question_options HIGH Same — no direct tenant filter
comments Medium Join-based isolation only
reviews Medium Join-based isolation only
notifications Medium No direct tenant scope

RLS Policies — Tenant Filtering (RESOLVED)

All gamification RLS policies now use get_tenant_id() for tenant filtering:

  • Data tables: USING (tenant_id = get_tenant_id()) for reads, WITH CHECK (tenant_id = get_tenant_id()) for writes
  • Definition tables (achievements, store items): USING (tenant_id IS NULL OR tenant_id = get_tenant_id()) to show global + school-specific items
  • Leaderboard cache: tenant-scoped public read

Business Value Analysis

Why Tenant-Scoped Gamification Matters

For School Owners (your paying customers):

  1. School Identity & Branding — Each school wants THEIR leaderboard, THEIR achievements, THEIR community. A school with 50 students doesn't want to compete against a school with 5,000.

  2. Custom Store Items — Schools should sell their own power-ups, merchandise credits, or special access. A coding school might sell "Code Review Token" while a language school sells "Conversation Session."

  3. School-Specific Achievements — "Complete all React courses" only makes sense in a coding school. Achievement definitions should be customizable per tenant.

  4. Competitive Advantage — Schools can market "Our gamified learning platform" as a unique selling point. Global gamification dilutes this.

  5. Revenue Opportunity — Premium gamification features (custom achievements, branded store, challenge creation) become upsell opportunities for higher-tier plans.

Implemented Business Model

Plan Gamification Features
Free XP + levels + streaks (basic engagement)
Basic + School leaderboard, achievements
Professional + Store, custom achievements, custom store items
Enterprise + All features, analytics, white-label

Revenue Impact Estimate

  • Schools with gamification see 2-3x higher student retention (Duolingo model)
  • Custom gamification is a top-3 requested feature in EdTech SaaS
  • Could justify $20-50/month premium per school for advanced gamification

Implementation: Tenant-Scoped Gamification (COMPLETED)

Migration: 20260217030000_tenant_scope_gamification.sql

Phase 1 — Core Isolation:

  • Added tenant_id NOT NULL to 7 data tables (nullable first, backfilled with default tenant, then NOT NULL)
  • Updated unique constraints to composite keys: (user_id, tenant_id) etc.
  • Dropped old RLS policies, created tenant-scoped replacements using get_tenant_id()

Phase 2 — Functions & Edge Functions:

  • award_xp() — New _tenant_id UUID DEFAULT NULL param, UPSERT on (user_id, tenant_id) for lazy profile creation
  • Removed handle_new_gamification_profile trigger (profiles now created lazily by award_xp())
  • All 5 XP trigger functions rewritten to resolve tenant_id from source tables
  • refresh_leaderboard_cache() — Uses PARTITION BY tenant_id for per-school rankings
  • 4 edge functions (get-gamification-summary, get-leaderboard, check-achievements, spend-points) extract tenant_id from JWT and filter all queries

Phase 3 — Plan-Gated Features:

  • get_gamification_features(_tenant_id UUID) RPC returns JSONB feature flags based on tenants.plan
  • Optional tenant_id added to gamification_achievements and gamification_store_items for custom school content
  • Frontend components show upgrade prompts when features are locked
  • Edge functions return 403 with feature_locked: true for gated features

Files Modified

File Change
supabase/migrations/20260217030000_tenant_scope_gamification.sql NEW — Full migration
supabase/functions/get-gamification-summary/index.ts Tenant filter + features response
supabase/functions/get-leaderboard/index.ts Tenant filter + plan check
supabase/functions/check-achievements/index.ts Tenant filter + plan check
supabase/functions/spend-points/index.ts Tenant filter + plan check + bug fixes
lib/hooks/use-gamification.ts Added GamificationFeatures interface
components/gamification/gamification-header-card.tsx Conditional trophy icon
components/gamification/mini-leaderboard.tsx Upgrade prompt
components/gamification/achievement-grid.tsx Upgrade prompt
components/gamification/store-section.tsx Upgrade prompt
messages/en.json Upgrade i18n keys
messages/es.json Upgrade i18n keys

Bugs Fixed During Implementation

Bug Fix
spend-points used is_active Changed to is_available (correct column name)
spend-points used item.title Changed to item.name (correct column name)

Other Multi-Tenant Gaps

exam_questions and question_options

These tables lack tenant_id and rely on join chains (question → exam → course → tenant). While RLS on exams prevents direct cross-tenant access, adding tenant_id would:

  • Enable direct RLS policies
  • Improve query performance (no join needed)
  • Prevent edge cases where orphaned questions become visible

notifications

Should have tenant_id to scope notification templates and delivery per school.


E2E Test Coverage

Currently Tested (28/31 passing)

  • Course isolation between tenants
  • Enrollment isolation
  • Payment flow isolation
  • Role-based access per tenant
  • Auth flow with tenant context

Gamification Tests (VERIFIED via Playwright MCP)

Tenant isolation verified:

  • Same user (student@e2etest.com) has separate profiles: 310 XP/Level 3 in Default School vs 25 XP/Level 1 in test-academy
  • Leaderboard only shows students from current school
  • award_xp() correctly scopes XP to the target tenant

Plan gating verified (all 3 tiers):

Plan XP/Level/Streak Leaderboard Achievements Store
free Shown Locked (upgrade prompt) Locked (no trophy) Locked (upgrade prompt)
basic Shown Unlocked (rankings) Unlocked (trophy icon) Locked (upgrade prompt)
professional Shown Unlocked Unlocked Unlocked (6 items, balance)

Summary

Area Status Priority
Core LMS (courses, enrollments, payments) ISOLATED Done
Certificates ISOLATED Done
Gamification ISOLATED + PLAN-GATED Done
Exam questions Partially isolated (via joins) P1
Notifications Not isolated P2