Skip to main content

ADR-LMS-PHASE-2: Learning Management System - Advanced Learning Platform

Status: Proposed Date: 2025-12-11 Deciders: Hal Casteel (Founder/CEO/CTO), CODITECT Core Team Technical Story: Evolve the Phase 1 onboarding database into a comprehensive learning platform with adaptive content, skill trees, certification paths, and team learning capabilities


Context and Problem Statement

Phase 1 LMS (ADR-030-lms-phase-1) provides persistent onboarding progress tracking. However, it has limitations:

  1. Static curriculum - All users follow the same 11-module path
  2. No adaptive learning - Content doesn't adjust to user skill level
  3. Limited content types - Only modules and badges, no skill trees
  4. Single-user focus - No team or organization learning features
  5. No spaced repetition - No reinforcement of learned concepts
  6. Basic analytics - Event stream without actionable insights
  7. No certification - No formal credential or verification system

The Problem: How do we evolve the LMS from onboarding tracker to a comprehensive learning platform that adapts to users, supports teams, and provides verifiable credentials?


Decision Drivers

Technical Requirements

  • R1: Adaptive content delivery based on user skill assessment
  • R2: Skill tree and dependency graph for learning paths
  • R3: Spaced repetition system for knowledge retention
  • R4: Team/organization learning management
  • R5: Quiz engine with multiple question types
  • R6: Certification system with verifiable credentials
  • R7: Learning analytics dashboard
  • R8: Content versioning and A/B testing
  • R9: Integration with external LMS standards (xAPI/SCORM)

User Experience Goals

  • UX1: Personalized learning paths based on goals and skill level
  • UX2: Visual progress through skill trees
  • UX3: Spaced repetition reminders for review
  • UX4: Team leaderboards and collaborative learning
  • UX5: Downloadable/shareable certificates

Business Requirements

  • B1: Support for paid/premium learning content
  • B2: Team licensing and seat management
  • B3: Analytics for content creators
  • B4: Integration with enterprise identity providers

Proposed Solution

Phase 2.1: Adaptive Learning Engine

New Tables:

-- Skill definitions and relationships
CREATE TABLE learning_skills (
id INTEGER PRIMARY KEY AUTOINCREMENT,
skill_key TEXT UNIQUE NOT NULL,
skill_name TEXT NOT NULL,
description TEXT,
category TEXT, -- core, advanced, specialization
parent_skill_id INTEGER, -- For skill hierarchy
difficulty_level INTEGER DEFAULT 1, -- 1-10 scale
estimated_mastery_hours REAL,
icon TEXT,
is_active BOOLEAN DEFAULT 1,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (parent_skill_id) REFERENCES learning_skills(id)
);

-- Skill prerequisites (dependency graph)
CREATE TABLE learning_skill_prerequisites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
skill_id INTEGER NOT NULL,
prerequisite_skill_id INTEGER NOT NULL,
required_mastery_level INTEGER DEFAULT 50, -- 0-100
FOREIGN KEY (skill_id) REFERENCES learning_skills(id) ON DELETE CASCADE,
FOREIGN KEY (prerequisite_skill_id) REFERENCES learning_skills(id) ON DELETE CASCADE,
UNIQUE(skill_id, prerequisite_skill_id)
);

-- User skill mastery tracking
CREATE TABLE learning_skill_mastery (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
skill_id INTEGER NOT NULL,
mastery_level INTEGER DEFAULT 0, -- 0-100
confidence_score REAL DEFAULT 0.5, -- Bayesian confidence
last_practiced TEXT,
practice_count INTEGER DEFAULT 0,
streak_days INTEGER DEFAULT 0,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES learning_users(user_id) ON DELETE CASCADE,
FOREIGN KEY (skill_id) REFERENCES learning_skills(id) ON DELETE CASCADE,
UNIQUE(user_id, skill_id)
);

-- Module to skill mapping
CREATE TABLE learning_module_skills (
id INTEGER PRIMARY KEY AUTOINCREMENT,
module_id INTEGER NOT NULL,
skill_id INTEGER NOT NULL,
teaches_mastery INTEGER DEFAULT 20, -- Mastery points gained
FOREIGN KEY (module_id) REFERENCES learning_modules(id) ON DELETE CASCADE,
FOREIGN KEY (skill_id) REFERENCES learning_skills(id) ON DELETE CASCADE,
UNIQUE(module_id, skill_id)
);

Adaptive Algorithm:

def recommend_next_content(user_id):
"""
Bayesian knowledge tracing algorithm:
1. Get user's current skill mastery levels
2. Find skills with prerequisites satisfied
3. Rank by: (target_mastery - current_mastery) * prerequisite_confidence
4. Select modules teaching top-ranked skills
5. Filter by user's learning style preference
"""
pass

Phase 2.2: Spaced Repetition System

New Tables:

-- Review cards for spaced repetition
CREATE TABLE learning_review_cards (
id INTEGER PRIMARY KEY AUTOINCREMENT,
card_key TEXT UNIQUE NOT NULL,
module_id INTEGER,
skill_id INTEGER,
card_type TEXT NOT NULL, -- concept, quiz, exercise
front_content TEXT NOT NULL, -- Question/prompt
back_content TEXT NOT NULL, -- Answer/explanation
difficulty_rating REAL DEFAULT 0.3, -- 0-1, affects scheduling
is_active BOOLEAN DEFAULT 1,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (module_id) REFERENCES learning_modules(id) ON DELETE SET NULL,
FOREIGN KEY (skill_id) REFERENCES learning_skills(id) ON DELETE SET NULL
);

-- User review schedule (SM-2 algorithm)
CREATE TABLE learning_review_schedule (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
card_id INTEGER NOT NULL,
ease_factor REAL DEFAULT 2.5, -- SM-2 ease factor
interval_days INTEGER DEFAULT 1, -- Days until next review
repetitions INTEGER DEFAULT 0, -- Successful repetitions
next_review_date TEXT,
last_review_date TEXT,
last_quality INTEGER, -- 0-5 quality rating
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES learning_users(user_id) ON DELETE CASCADE,
FOREIGN KEY (card_id) REFERENCES learning_review_cards(id) ON DELETE CASCADE,
UNIQUE(user_id, card_id)
);

SM-2 Algorithm Implementation:

def update_review_schedule(user_id, card_id, quality):
"""
SuperMemo 2 algorithm:
quality: 0-5 (0=blackout, 5=perfect)

if quality >= 3:
if repetitions == 0: interval = 1
elif repetitions == 1: interval = 6
else: interval = round(interval * ease_factor)
repetitions += 1
else:
repetitions = 0
interval = 1

ease_factor = max(1.3, ease_factor + 0.1 - (5-quality)*(0.08+(5-quality)*0.02))
"""
pass

Phase 2.3: Quiz Engine

New Tables:

-- Quiz definitions
CREATE TABLE learning_quizzes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quiz_key TEXT UNIQUE NOT NULL,
module_id INTEGER,
skill_id INTEGER,
title TEXT NOT NULL,
description TEXT,
time_limit_minutes INTEGER,
passing_score INTEGER DEFAULT 70,
max_attempts INTEGER, -- NULL = unlimited
shuffle_questions BOOLEAN DEFAULT 1,
show_answers_after BOOLEAN DEFAULT 1,
is_active BOOLEAN DEFAULT 1,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (module_id) REFERENCES learning_modules(id) ON DELETE SET NULL,
FOREIGN KEY (skill_id) REFERENCES learning_skills(id) ON DELETE SET NULL
);

-- Quiz questions with multiple types
CREATE TABLE learning_quiz_questions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
quiz_id INTEGER NOT NULL,
question_type TEXT NOT NULL, -- multiple_choice, true_false, short_answer, code
question_text TEXT NOT NULL,
question_code TEXT, -- For code questions
options TEXT, -- JSON array for multiple choice
correct_answer TEXT NOT NULL, -- Answer or JSON for multiple correct
explanation TEXT, -- Shown after answer
points INTEGER DEFAULT 1,
difficulty INTEGER DEFAULT 1, -- 1-5
sort_order INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT 1,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (quiz_id) REFERENCES learning_quizzes(id) ON DELETE CASCADE
);

-- Quiz attempts
CREATE TABLE learning_quiz_attempts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
quiz_id INTEGER NOT NULL,
started_at TEXT DEFAULT CURRENT_TIMESTAMP,
completed_at TEXT,
score INTEGER,
max_score INTEGER,
passed BOOLEAN,
time_spent_seconds INTEGER,
answers TEXT, -- JSON of question_id: user_answer
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES learning_users(user_id) ON DELETE CASCADE,
FOREIGN KEY (quiz_id) REFERENCES learning_quizzes(id) ON DELETE CASCADE
);

Phase 2.4: Team Learning

New Tables:

-- Organizations
CREATE TABLE learning_organizations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
org_key TEXT UNIQUE NOT NULL,
org_name TEXT NOT NULL,
org_type TEXT, -- company, team, community
license_type TEXT, -- free, pro, enterprise
max_seats INTEGER,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN DEFAULT 1
);

-- Organization membership
CREATE TABLE learning_org_members (
id INTEGER PRIMARY KEY AUTOINCREMENT,
org_id INTEGER NOT NULL,
user_id TEXT NOT NULL,
role TEXT DEFAULT 'member', -- admin, manager, member
joined_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (org_id) REFERENCES learning_organizations(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES learning_users(user_id) ON DELETE CASCADE,
UNIQUE(org_id, user_id)
);

-- Learning paths (curated sequences)
CREATE TABLE learning_paths (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path_key TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
description TEXT,
org_id INTEGER, -- NULL = public
created_by TEXT,
estimated_hours REAL,
difficulty_level TEXT,
is_public BOOLEAN DEFAULT 0,
is_active BOOLEAN DEFAULT 1,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (org_id) REFERENCES learning_organizations(id) ON DELETE SET NULL,
FOREIGN KEY (created_by) REFERENCES learning_users(user_id) ON DELETE SET NULL
);

-- Path modules (ordered sequence)
CREATE TABLE learning_path_modules (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path_id INTEGER NOT NULL,
module_id INTEGER NOT NULL,
sort_order INTEGER NOT NULL,
is_required BOOLEAN DEFAULT 1,
FOREIGN KEY (path_id) REFERENCES learning_paths(id) ON DELETE CASCADE,
FOREIGN KEY (module_id) REFERENCES learning_modules(id) ON DELETE CASCADE
);

-- User path enrollment
CREATE TABLE learning_path_enrollments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
path_id INTEGER NOT NULL,
enrolled_at TEXT DEFAULT CURRENT_TIMESTAMP,
completed_at TEXT,
progress_percentage INTEGER DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES learning_users(user_id) ON DELETE CASCADE,
FOREIGN KEY (path_id) REFERENCES learning_paths(id) ON DELETE CASCADE,
UNIQUE(user_id, path_id)
);

Phase 2.5: Certification System

New Tables:

-- Certificate definitions
CREATE TABLE learning_certificates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
cert_key TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
description TEXT,
cert_level TEXT, -- foundational, professional, expert
required_path_id INTEGER, -- Path that grants this cert
required_quiz_id INTEGER, -- Final exam
required_score INTEGER DEFAULT 80,
validity_months INTEGER, -- NULL = never expires
badge_id INTEGER, -- Associated badge
is_active BOOLEAN DEFAULT 1,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (required_path_id) REFERENCES learning_paths(id) ON DELETE SET NULL,
FOREIGN KEY (required_quiz_id) REFERENCES learning_quizzes(id) ON DELETE SET NULL,
FOREIGN KEY (badge_id) REFERENCES learning_badges(id) ON DELETE SET NULL
);

-- Issued certificates
CREATE TABLE learning_issued_certificates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
certificate_id INTEGER NOT NULL,
user_id TEXT NOT NULL,
issued_at TEXT DEFAULT CURRENT_TIMESTAMP,
expires_at TEXT,
verification_code TEXT UNIQUE NOT NULL, -- UUID for verification
final_score INTEGER,
metadata TEXT, -- JSON additional data
is_revoked BOOLEAN DEFAULT 0,
FOREIGN KEY (certificate_id) REFERENCES learning_certificates(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES learning_users(user_id) ON DELETE CASCADE
);

Verification Endpoint:

def verify_certificate(verification_code):
"""
Returns certificate details if valid:
- Holder name
- Certificate title
- Issue date
- Expiration status
- Verification URL
"""
pass

Phase 2.6: Analytics Dashboard

New Tables:

-- Aggregated analytics (computed daily)
CREATE TABLE learning_analytics_daily (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
org_id INTEGER, -- NULL = platform-wide
metric_name TEXT NOT NULL,
metric_value REAL NOT NULL,
dimension TEXT, -- For grouping (skill, module, etc.)
dimension_value TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(date, org_id, metric_name, dimension, dimension_value)
);

-- Funnel tracking
CREATE TABLE learning_funnels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
funnel_key TEXT UNIQUE NOT NULL,
funnel_name TEXT NOT NULL,
steps TEXT NOT NULL, -- JSON array of step definitions
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

-- Funnel step completions
CREATE TABLE learning_funnel_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
funnel_id INTEGER NOT NULL,
user_id TEXT NOT NULL,
step_index INTEGER NOT NULL,
step_name TEXT NOT NULL,
completed_at TEXT DEFAULT CURRENT_TIMESTAMP,
time_from_previous_seconds INTEGER,
FOREIGN KEY (funnel_id) REFERENCES learning_funnels(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES learning_users(user_id) ON DELETE CASCADE
);

Key Metrics:

  • Daily/Weekly/Monthly Active Learners (DAL/WAL/MAL)
  • Module completion rates
  • Quiz pass rates
  • Average time to certification
  • Skill mastery distribution
  • Review retention rates
  • Path completion funnels

New /cxq Commands (Phase 2)

# Skill tree queries
/cxq --skills # List all skills
/cxq --skill-tree # Visual skill tree
/cxq --skill-mastery # User's skill levels
/cxq --skill-recommendations # Recommended skills to learn

# Spaced repetition
/cxq --review-due # Cards due for review
/cxq --review-stats # Review statistics
/cxq --review-schedule # Upcoming review schedule

# Quiz management
/cxq --quizzes # Available quizzes
/cxq --quiz-history # Past quiz attempts
/cxq --quiz-analytics # Quiz performance analytics

# Team learning
/cxq --team-progress # Team progress dashboard
/cxq --team-leaderboard # Team rankings
/cxq --team-paths # Assigned learning paths

# Certification
/cxq --certificates # Available certifications
/cxq --my-certs # User's certificates
/cxq --verify-cert CODE # Verify certificate

# Analytics
/cxq --learning-dashboard # Full analytics dashboard
/cxq --learning-trends # Progress trends over time

Migration Strategy

Phase 2.1 Migration (Adaptive Learning)

-- Add new tables
-- Populate learning_skills from existing categories
-- Map existing modules to skills
-- Initialize skill_mastery from completed modules

Phase 2.2 Migration (Spaced Repetition)

-- Add review tables
-- Generate initial cards from module content
-- No existing data to migrate

Phase 2.3 Migration (Quiz Engine)

-- Add quiz tables
-- Convert existing has_quiz modules to full quizzes
-- Migrate quiz_score data to quiz_attempts

Backward Compatibility

  • All Phase 1 queries continue to work
  • New features are additive
  • Existing progress data preserved

Implementation Roadmap

Sub-PhaseFeaturesEstimated EffortDependencies
2.1Adaptive Learning Engine2 weeksPhase 1 complete
2.2Spaced Repetition1 week2.1
2.3Quiz Engine2 weeks2.1
2.4Team Learning2 weeks2.1
2.5Certification System1 week2.3, 2.4
2.6Analytics Dashboard2 weeksAll above

Total Estimated Effort: 10 weeks


Alternatives Considered

Alternative 1: External LMS Integration

Description: Use existing LMS (Moodle, Canvas, etc.)

Pros:

  • Full-featured out of box
  • Industry standard compliance
  • Existing ecosystem

Cons:

  • External dependency
  • Doesn't integrate with /cxq
  • Overkill for CLI tool learning
  • Licensing costs

Decision: Rejected - lose integration benefits

Alternative 2: Third-Party API (Coursera, Udemy)

Description: Integrate with learning content platforms

Pros:

  • Professional content
  • Recognized credentials
  • Large content library

Cons:

  • Doesn't teach CODITECT specifically
  • External dependency
  • Per-seat costs
  • No CLI integration

Decision: Rejected - need CODITECT-specific content


Success Metrics

MetricTargetMeasurement
Skill mastery improvement+20% avg masteryBefore/after comparison
Knowledge retention80% after 30 daysSpaced repetition success rate
Quiz pass rate>70% first attemptQuiz analytics
Path completion rate>60%Funnel analytics
Certification rate>40% of active usersCertificate issuance
Team adoption>5 orgs in first quarterOrg registrations


Open Questions

  1. Content Authoring: How will new modules/quizzes be authored?

    • Option A: Markdown files with frontmatter
    • Option B: Admin CLI commands
    • Option C: Web-based editor
  2. External Identity: Should we support OAuth/SAML for teams?

    • Consideration: Enterprise customers may require SSO
  3. xAPI/SCORM: Is LRS integration needed for enterprise?

    • Consideration: Some enterprises require xAPI statements
  4. Offline Support: How to handle review cards offline?

    • Consideration: Mobile/disconnected scenarios

Status: Proposed - Awaiting prioritization Last Updated: 2025-12-11 Version: 0.1.0 (Draft)