upgrade-integration
Integrate Carnegie Learning's UpGrade A/B testing platform into LMS and EdTech applications. Guides setup of decision points, experiment conditions, LTI/xAPI integration, and outcome logging. Use when asked to add A/B testing, experiments, or feature flags to educational software.
When & Why to Use This Skill
This Claude skill provides expert guidance for integrating Carnegie Learning's UpGrade, an open-source A/B testing platform specifically architected for educational technology. It assists developers in implementing complex experimental designs, such as group-level assignments (classrooms) and consistency rules, while ensuring robust integration with Learning Management Systems (LMS) via LTI and xAPI standards. By addressing EdTech-specific challenges like iframe cookie blocking and identity mapping, it streamlines the deployment of randomized controlled trials in digital learning environments.
Use Cases
- Implementing classroom-level randomized controlled trials (RCTs) where all students in a group receive the same experimental condition.
- Integrating feature flags and A/B testing into LMS platforms like Canvas or Moodle using LTI 1.3 and xAPI for outcome tracking.
- Setting up pedagogical experiments to compare different instructional strategies, such as immediate versus delayed feedback or different content orderings.
- Resolving technical integration issues such as third-party cookie blocking in Safari and Chrome for apps running within LMS iframes.
- Managing complex user identity mapping across different platforms to ensure consistent experiment assignment for students using multiple IDs.
| name | upgrade-integration |
|---|---|
| description | Integrate Carnegie Learning's UpGrade A/B testing platform into LMS and EdTech applications. Guides setup of decision points, experiment conditions, LTI/xAPI integration, and outcome logging. Use when asked to add A/B testing, experiments, or feature flags to educational software. |
| allowed-tools | Read, Write, Edit, Bash, WebFetch, Glob, Grep |
UpGrade A/B Testing Integration for EdTech
You are helping integrate UpGrade, Carnegie Learning's open-source A/B testing platform designed specifically for educational technology.
When This Skill Applies
- Adding A/B testing or experiments to educational apps
- Integrating feature flags in learning platforms
- Connecting EdTech apps to LMS via LTI with experiment support
- Setting up learning analytics with xAPI + experimentation
- Debugging experiment assignment issues
Critical Context
UpGrade is NOT a generic A/B testing tool. It's designed for education with features like:
- Group-level assignment (whole classrooms get same condition)
- Consistency rules (students don't get re-randomized)
- Educational context metadata (grade, subject, proficiency)
ALWAYS ask these questions before implementing:
- What's the assignment unit? (Individual student vs classroom vs school)
- What happens if a student changes classes mid-experiment?
- How will teachers access both conditions (or will they)?
- Is the app used in LMS iframes? (Cookie issues!)
Quick Reference
Installation
# JavaScript/TypeScript
npm install upgrade_client_lib
# Java (Maven)
<dependency>
<groupId>com.carnegielearning</groupId>
<artifactId>upgrade-client</artifactId>
</dependency>
Core Integration Pattern
import UpgradeClient from 'upgrade_client_lib/dist/browser';
import { MARKED_DECISION_POINT_STATUS } from 'upgrade_client_lib';
// 1. Initialize (MUST complete before getDecisionPointAssignment)
const client = new UpgradeClient(userId, hostUrl, context);
await client.init(groupData, workingGroupData);
// 2. Get assignment at decision point
const assignment = await client.getDecisionPointAssignment('feature_name', 'target');
const condition = assignment.getCondition();
const payload = assignment.getPayload();
// 3. Mark that condition was applied
client.markDecisionPoint('feature_name', MARKED_DECISION_POINT_STATUS.CONDITION_APPLIED);
// 4. Log outcomes
client.log('score', 85);
client.log('completion_time_ms', 45000);
Three SDK Modes
| Mode | Use Case | Requires init()? |
|---|---|---|
| Standard | Pre-registered users with stored groups | Yes |
| Ephemeral | Runtime-provided groups, no DB lookup | No |
| Merged | Combine session + stored groups | Yes |
Integration Architecture
┌─────────────────────────────────────────────────────────────┐
│ LMS │
│ (Canvas, Moodle, etc.) │
└─────────────────────┬───────────────────────────────────────┘
│ LTI 1.3 Launch
│ (user_id, context_id, roles)
▼
┌─────────────────────────────────────────────────────────────┐
│ Your EdTech App │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Identity Mapper │ │
│ │ LTI user_id → UpGrade userId + altUserIds │ │
│ └─────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌─────────────────────▼───────────────────────────────┐ │
│ │ UpGrade SDK │ │
│ │ - init(groupData) │ │
│ │ - getDecisionPointAssignment() │ │
│ │ - markDecisionPoint() │ │
│ │ - log() │ │
│ └─────────────────────┬───────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────────┘
│ HTTPS
▼
┌─────────────────────────────────────────────────────────────┐
│ UpGrade Backend │
│ (Hosted: upgrade-hosting | Self-hosted | Carnegie) │
└─────────────────────────────────────────────────────────────┘
Deployment Options
| Option | Best For | Setup |
|---|---|---|
| Hosted Platform | Most EdTech apps, quick start | Get API key, point SDK |
| Self-hosted | Full control, on-prem requirements | Deploy UpGrade yourself |
| Carnegie Learning | Enterprise, research partnerships | Contact Carnegie |
Using the Hosted Platform (Recommended)
The easiest way to get started. See the live demo.
import UpgradeClient from 'upgrade_client_lib/dist/browser';
const client = new UpgradeClient(
userId,
'https://api.upgrade-hosting.com/v1', // Hosted platform
'your-app-context'
);
// Add your API key from the dashboard
client.setCustomHeaders({
'X-API-Key': 'upg_live_your_key_here'
});
await client.init({
schoolId: 'lincoln-elementary',
classId: 'math-301',
teacherId: 'ms-rodriguez'
});
Features of hosted platform:
- Multi-tenant isolation: Each district/organization gets isolated database
- No infrastructure: We manage UpGrade backend, you focus on your app
- Dashboard: Manage experiments, API keys, view results
- FERPA-ready: District-level data isolation for compliance
CRITICAL: Known Pitfalls
Before writing ANY integration code, read resources/pitfalls-and-edge-cases.md. The most dangerous issues:
1. Third-Party Cookie Blocking (CRITICAL)
If your app runs in an LMS iframe, Safari and Chrome WILL block your session cookies by default.
Symptoms:
- User gets re-randomized on every page load
init()seems to work but assignments are inconsistent- Works in development, fails in production LMS
Solutions:
- Set
SameSite=None; Secureon all cookies - Use localStorage with user-provided identifier (not session-based)
- Consider opening in new window instead of iframe
2. Race Condition: init() vs getDecisionPointAssignment()
// WRONG - race condition
const client = new UpgradeClient(userId, hostUrl, context);
client.init(groupData); // async, not awaited!
const assignment = await client.getDecisionPointAssignment('feature');
// assignment may be NO_CONDITION_ASSIGNED
// CORRECT
const client = new UpgradeClient(userId, hostUrl, context);
await client.init(groupData); // AWAIT this!
const assignment = await client.getDecisionPointAssignment('feature');
3. LTI Identity Mismatch
LTI provides user_id which is opaque and LMS-specific. Your app may use email or internal IDs.
// Map all identities together
await client.init(groupData);
client.setAltUserIds([
ltiUserId, // from LTI launch
userEmail, // from your auth
internalDatabaseId // your user table PK
]);
4. Group Assignment Drift
Student transfers from Mrs. Smith's class (Treatment A) to Mr. Jones' class (Treatment B).
Default behavior: Student KEEPS original assignment (consistency rule)
Problem: Student now sees different UI than classmates, causing confusion.
Solution: Decide upfront:
- Accept drift (experimental purity)
- Re-assign on class change (classroom consistency)
- Exclude transferred students from analysis
5. Shared Device / Chromebook Cart Problem
// WRONG - userId persists from previous student
const client = new UpgradeClient(getUserIdFromLocalStorage(), ...);
// CORRECT - validate identity on each session
const client = new UpgradeClient(getCurrentAuthenticatedUserId(), ...);
// Clear any cached assignments if userId changed
Decision Point Patterns for Education
See resources/decision-point-patterns.md for detailed examples:
| Pattern | Example | Assignment Unit |
|---|---|---|
| UI Variant | New vs old problem interface | Individual |
| Pedagogical | Worked examples vs practice problems | Classroom |
| Adaptive Algorithm | Mastery threshold 80% vs 90% | Individual |
| Content Ordering | Teach fractions before decimals vs after | School |
| Feedback Timing | Immediate vs delayed feedback | Classroom |
Code Templates
See templates/ directory for:
react-hook.tsx- React hook with proper error handlingnextjs-middleware.ts- Next.js API route patternvanilla-js.js- Framework-agnostic implementationlti-launch-handler.ts- LTI 1.3 launch with UpGrade init
Logging Best Practices
// DO: Use consistent types
client.log('score', 85); // number
client.log('completed', true); // boolean
client.log('response', 'correct'); // string
// DON'T: Mix types for same metric
client.log('score', 85);
client.log('score', '85'); // String! Will corrupt analysis
// DON'T: Log before marking decision point
client.log('score', 85); // Not associated with condition!
client.markDecisionPoint('quiz', MARKED_DECISION_POINT_STATUS.CONDITION_APPLIED);
// DO: Mark first, then log
client.markDecisionPoint('quiz', MARKED_DECISION_POINT_STATUS.CONDITION_APPLIED);
client.log('score', 85); // Correctly associated
Troubleshooting Decision Tree
Assignment returns NO_CONDITION_ASSIGNED
├── Did you await init()?
│ └── No → Add await before init()
├── Is experiment in ENROLLING state?
│ └── No → Check UpGrade UI, start experiment
├── Does user match segment criteria?
│ └── No → Check inclusion/exclusion rules
├── Is context correct?
│ └── No → Verify context string matches experiment config
└── Check network tab for failed requests
User gets different condition than classmates
├── Is assignment unit set to GROUP?
│ └── No → Change to GROUP in experiment settings
├── Was groupData provided to init()?
│ └── No → Pass { classId: 'xxx' } to init()
├── Did student join after experiment started?
│ └── Yes → Check POST_EXPERIMENT_RULE
└── Is student in multiple groups?
└── Yes → Check group priority/override logic
External Resources
- Hosted Platform - Multi-tenant hosted UpGrade
- Live Demo - Interactive walkthrough
- UpGrade GitHub - Core UpGrade source
- UpGrade Documentation
- JavaScript SDK
- LTI 1.3 Spec
- xAPI Spec
When to Escalate
Recommend the developer contact UpGrade support or file a GitHub issue if:
- Assignment algorithm seems incorrect (statistical anomaly)
- Data export shows impossible values
- Backend returns 5xx errors consistently
- Need custom consistency rules not supported by SDK