# ============================================================================== # OpenCompletion Activity YAML Specification # ============================================================================== # This document defines ALL supported mechanics for creating educational # activities in the OpenCompletion system. # # Version: 1.0 # Last Updated: 2025-01-10 # ============================================================================== # ============================================================================== # ACTIVITY ROOT LEVEL # ============================================================================== # These fields apply to the entire activity # Maximum number of times a user can attempt each step before auto-advancing # Default: 3 # Optional default_max_attempts_per_step: 3 # Model to use for categorizing user responses into buckets # Default: "MODEL_1" (Hermes-3-Llama-3.1-8B) # Optional classifier_model: "MODEL_1" # Model to use for generating AI feedback # Default: "MODEL_1" (Hermes-3-Llama-3.1-8B) # Optional # Tip: Use faster models (MODEL_1) for classification, specialized models (MODEL_3) for feedback feedback_model: "MODEL_1" # Global rubric for evaluating student responses across all steps # This provides consistent evaluation criteria # Optional tokens_for_ai_rubric: | You are helping students learn about [TOPIC]. Evaluate their responses based on: - Understanding of core concepts - Clarity of explanation - Critical thinking demonstrated Be encouraging and constructive! # ============================================================================== # SECTIONS # ============================================================================== # Activities are organized into sections, which contain steps # Required: At least one section sections: # Each section must have a unique section_id - section_id: "introduction" # REQUIRED - Unique identifier title: "Getting Started" # REQUIRED - Human-readable title # Steps are the individual interactions within a section steps: # ======================================================================== # STEP TYPE 1: CONTENT-ONLY STEP # ======================================================================== # Displays information and automatically advances # No user interaction required - step_id: "welcome" # REQUIRED - Unique within this section title: "Welcome" # REQUIRED - Human-readable title # Content blocks are displayed to the user # Supports markdown formatting content_blocks: # REQUIRED for content-only steps - "# Welcome to the Activity! 🎉" - "" - "This activity will teach you about [TOPIC]." - "" - "**What you'll learn:**" - "- Concept 1" - "- Concept 2" - "- Concept 3" - "" - "Let's get started!" # Content-only steps automatically advance to the next step # No question, buckets, or transitions needed # ======================================================================== # STEP TYPE 2: QUESTION STEP # ======================================================================== # Interactive step that requires user response - step_id: "question_example" title: "Your First Question" # Optional: Content blocks can appear before the question content_blocks: - "## Background Information" - "Before we ask the question, here's some context..." # The question asked to the user question: "What is your name?" # REQUIRED for question steps # Instructions for the AI on how to categorize the user's response # The AI will read this and place the response into one of the buckets tokens_for_ai: | # REQUIRED for question steps Categorize the user's response: - name_provided: They gave a name (any name is acceptable) - set_language: They want to change language preference - off_topic: Their response is unrelated to the question Be generous in accepting names - nicknames, full names, etc. # Instructions for generating feedback after categorization # This is used when creating ai_feedback in transitions feedback_tokens_for_ai: | # Optional but recommended Welcome the user by their name warmly! Make them feel comfortable and ready to learn. Example: "Welcome, [name]! Great to have you here!" # List of possible categories (buckets) for user responses # Every response will be categorized into one of these buckets: # REQUIRED for question steps - name_provided - set_language - off_topic - emergency # Random bucket - also in buckets list - surprise # Random bucket - also in buckets list - bonus # Random bucket - also in buckets list # ====================================================================== # RANDOM BUCKETS (Optional) # ====================================================================== # Probabilistic events that can trigger alongside user responses # Random rolls happen BEFORE categorization # Multiple random buckets can trigger simultaneously random_buckets: # Optional # Each random bucket must also appear in the main buckets list above emergency: probability: 0.05 # 5% chance (0.0 to 1.0) surprise: probability: 0.10 # 10% chance bonus: probability: 0.03 # 3% chance # Processing Order: # 1. Random buckets rolled # 2. User response categorized # 3. User's bucket processed FIRST # 4. Random buckets processed in order they triggered # 5. Metadata accumulates across all transitions # 6. Last transition's navigation wins # ====================================================================== # TRANSITIONS # ====================================================================== # Define what happens for each bucket # REQUIRED: One transition per bucket (including random buckets) transitions: # ================================================================== # TRANSITION STRUCTURE # ================================================================== # Each bucket name maps to a transition configuration name_provided: # ---------------------------------------------------------------- # CONTENT BLOCKS (Optional) # Static text displayed immediately # ---------------------------------------------------------------- content_blocks: - "Great! Let's continue." # ---------------------------------------------------------------- # AI FEEDBACK (Optional) # Dynamic feedback generated by the AI # Uses feedback_tokens_for_ai from the step # ---------------------------------------------------------------- ai_feedback: tokens_for_ai: | Generate personalized feedback based on their response. Reference their specific answer to show you're paying attention. Be encouraging! # ---------------------------------------------------------------- # METADATA OPERATIONS (Optional) # Modify the persistent metadata that follows the user # ---------------------------------------------------------------- # ADD or UPDATE metadata keys metadata_add: # Store the exact user response user_name: "the-users-response" # Numeric increment: n+5 means "add 5 to existing value (or 0)" score: "n+5" # Numeric decrement: n-3 means "subtract 3 from existing value" lives: "n-3" # String concatenation: n+,value means "append value to comma-separated list" achievements: "n+,first_question" # If achievements was "started", becomes "started,first_question" # If achievements was empty, becomes "first_question" # String removal: n-,value means "remove value from comma-separated list" # pending_tasks: "n-,intro" # Removes "intro" from list # Random numeric increment: n+random(1,10) adds random number between 1 and 10 bonus_points: "n+random(1,10)" # Static value step_completed: "true" # Timestamp or any string last_active: "2025-01-10" # TEMPORARY metadata (removed at end of step) # Useful for one-time values that don't persist metadata_tmp_add: temp_hint: "Remember this for the next question!" temp_score: "n+2" # All same operations as metadata_add work here # REMOVE specific metadata keys metadata_remove: - old_key - another_key # Or single key: # metadata_remove: "single_key" # CLEAR all metadata (use with caution!) metadata_clear: true # RANDOM metadata - pick ONE random key-value pair metadata_random: random_event: "event_a" # One of these will be chosen random_event: "event_b" random_event: "event_c" # TEMPORARY random metadata - pick from list, remove at end of step metadata_tmp_random: dice_roll: [1, 2, 3, 4, 5, 6] # One value chosen randomly color_choice: ["red", "blue", "green"] # ---------------------------------------------------------------- # METADATA CONDITIONS (Optional) # Only execute this transition if conditions are met # ---------------------------------------------------------------- metadata_conditions: level: 5 # metadata.level must equal 5 has_key: "yes" # metadata.has_key must equal "yes" # All conditions must be true (AND logic) # ---------------------------------------------------------------- # METADATA FEEDBACK FILTER (Optional) # Only show AI feedback if specific metadata keys exist # ---------------------------------------------------------------- metadata_feedback_filter: - "achievement_unlocked" - "bonus_available" # AI feedback only generated if these keys are present in metadata # ---------------------------------------------------------------- # PROCESSING SCRIPT (Optional) # Execute Python code to perform complex logic # ---------------------------------------------------------------- # Note: processing_script is defined at STEP level, not transition level # Use run_processing_script: true to execute it for this transition run_processing_script: true # ---------------------------------------------------------------- # NAVIGATION (Optional) # Where to go next # ---------------------------------------------------------------- next_section_and_step: "introduction:processing_example" # Format: "section_id:step_id" # If omitted, stays on current step (useful for retry loops) # If ALL transitions omit this, activity terminates # ---------------------------------------------------------------- # ATTEMPT COUNTING (Optional) # Whether this transition counts toward max_attempts_per_step # ---------------------------------------------------------------- counts_as_attempt: false # Default: true # Set to false for: # - Hints that let user retry # - Language changes # - Clarifying questions # Set to true for: # - Wrong answers # - Correct answers # - Progress-making choices # ================================================================== # SPECIAL BUCKETS # ================================================================== # Language change bucket (standard pattern) set_language: content_blocks: - "Language preference updated." metadata_add: language: "the-users-response" counts_as_attempt: false # Don't penalize language changes next_section_and_step: "introduction:question_example" # Retry same question # Off-topic response (retry pattern) off_topic: content_blocks: - "I didn't understand that. Could you try again?" next_section_and_step: "introduction:question_example" # Retry # counts_as_attempt: true (default) - wrong answers count # Random event transitions emergency: ai_feedback: tokens_for_ai: | 🚨 EMERGENCY EVENT! Describe the emergency dramatically. Show how the user handles it with their previous choice. metadata_add: emergencies_handled: "n+1" random_events: "n+,emergency" counts_as_attempt: false # Random events don't count as attempts # No next_section_and_step - uses user's navigation surprise: ai_feedback: tokens_for_ai: | ✨ SURPRISE EVENT! Something unexpected happens! metadata_add: surprises_encountered: "n+1" bonus_points: "n+random(5,15)" counts_as_attempt: false bonus: ai_feedback: tokens_for_ai: | 🎁 BONUS EVENT! You earned a bonus! metadata_add: bonuses_collected: "n+1" counts_as_attempt: false # ======================================================================== # PROCESSING SCRIPTS # ======================================================================== # Python code executed during transitions # Defined at step level, triggered by run_processing_script: true - step_id: "processing_example" title: "Processing Script Demo" question: "Enter a number:" tokens_for_ai: "Categorize as 'number' if numeric, 'invalid' otherwise" buckets: [number, invalid] # Pre-script runs BEFORE categorization # Has access to user_response in metadata pre_script: | # Available: metadata dict (read/write), user_response result = {} # Parse user input try: value = int(metadata.get("user_response", "0")) result["parsed_value"] = value result["is_even"] = value % 2 == 0 except ValueError: result["parsed_value"] = None result["is_even"] = False # Return dict of values to add to metadata return result # Processing script runs DURING transition (if run_processing_script: true) # Has access to user_response in metadata processing_script: | # Available: metadata dict (read/write) result = {} # Complex calculations score = metadata.get("score", 0) multiplier = metadata.get("multiplier", 1) result["final_score"] = score * multiplier # Conditional logic if result["final_score"] > 100: result["achievement"] = "high_scorer" return result transitions: number: run_processing_script: true # Triggers processing_script above ai_feedback: tokens_for_ai: "Confirm their number and show calculated results from metadata" metadata_add: attempts: "n+1" next_section_and_step: "introduction:feedback_prompts_example" invalid: content_blocks: - "Please enter a valid number." next_section_and_step: "introduction:processing_example" # ======================================================================== # FEEDBACK PROMPTS (Multi-Agent Feedback) # ======================================================================== # New system for having multiple AI agents provide feedback # Each agent has their own personality and perspective - step_id: "feedback_prompts_example" title: "Multi-Agent Feedback Demo" question: "Design a solution to [PROBLEM]" tokens_for_ai: | Categorize as: - excellent: Comprehensive, creative solution - good: Solid solution with minor gaps - needs_work: Incomplete or flawed buckets: [excellent, good, needs_work] # Define multiple feedback agents # Each has their own name, emoji, and personality feedback_prompts: # Technical reviewer - focuses on implementation - name: "Tech Lead" emoji: "🔧" tokens_for_ai: | You are a senior technical architect. Review solutions for: - Technical feasibility - Scalability concerns - Implementation complexity Be constructive but thorough. system_prompt: | You are a senior technical architect reviewing student solutions. # Conditions for when this agent provides feedback metadata_conditions: level: "advanced" # Only for advanced students # Buckets this agent responds to buckets_to_respond: [excellent, good] # Skips needs_work # Creative reviewer - focuses on innovation - name: "Design Guru" emoji: "🎨" tokens_for_ai: | You are a creative design expert. Evaluate solutions for: - Innovation and originality - User experience considerations - Aesthetic appeal Inspire them to think outside the box! system_prompt: | You are a creative design expert evaluating student work. # This agent responds to all buckets (default) # Encouraging mentor - provides emotional support - name: "Mentor" emoji: "🌟" tokens_for_ai: | You are an encouraging mentor. Provide: - Emotional support - Encouragement to continue - Recognition of effort Always be positive and uplifting! system_prompt: | You are an encouraging mentor supporting students. # Always include this agent's feedback always_include: true # Legacy feedback tokens (combined with feedback_prompts if both present) feedback_tokens_for_ai: | Provide overall feedback on their solution. This is combined with the multi-agent feedback. transitions: excellent: # Multi-agent feedback automatically generated # Each agent in feedback_prompts provides their perspective metadata_add: score: "n+10" next_section_and_step: "advanced:coding_challenge" good: metadata_add: score: "n+5" next_section_and_step: "advanced:coding_challenge" needs_work: content_blocks: - "Let's try this again with some hints..." next_section_and_step: "introduction:feedback_prompts_example" # ============================================================================== # STEP-LEVEL MODEL OVERRIDES # ============================================================================== # Steps can override the activity-level classifier and feedback models - section_id: "advanced" title: "Advanced Section" steps: - step_id: "coding_challenge" title: "Write Code" # Override classifier model for this step classifier_model: "MODEL_1" # Fast classification # Override feedback model for this step feedback_model: "MODEL_3" # Qwen3-Coder for code review question: "Write a function to solve [PROBLEM]" tokens_for_ai: "Categorize as correct/incorrect based on solution quality" buckets: [correct, incorrect] transitions: correct: ai_feedback: tokens_for_ai: | Review their code professionally. Provide specific feedback on: - Code style and readability - Algorithmic efficiency - Edge case handling next_section_and_step: "conclusion:goodbye_content" incorrect: ai_feedback: tokens_for_ai: "Provide hints without giving away the solution" next_section_and_step: "advanced:coding_challenge" # ============================================================================== # TERMINATION PATTERNS # ============================================================================== # Activities can terminate in several ways - section_id: "conclusion" title: "Wrap Up" steps: # ======================================================================== # TERMINATION 1: Content-Only Final Step # ======================================================================== # Simplest termination - just display content - step_id: "goodbye_content" title: "Thank You!" content_blocks: - "# Thank You for Participating! 🎉" - "" - "You've completed the activity!" - "Your final score: check metadata.score" - "" - "Come back anytime!" # No question = auto-terminates # ======================================================================== # TERMINATION 2: Final Reflection Question # ======================================================================== # Last question with no onward navigation - step_id: "reflection" title: "Final Reflection" question: "What did you learn today?" tokens_for_ai: | Categorize their reflection as: - thoughtful: Deep, meaningful reflection - brief: Short but genuine - off_topic: Not answering the question buckets: [thoughtful, brief, off_topic] transitions: thoughtful: ai_feedback: tokens_for_ai: "Celebrate their learning and growth!" metadata_add: activity_completed: "true" # No next_section_and_step = terminates brief: ai_feedback: tokens_for_ai: "Thank them for their time and effort!" metadata_add: activity_completed: "true" # No next_section_and_step = terminates off_topic: content_blocks: - "Please reflect on what you learned in this activity." next_section_and_step: "conclusion:reflection" # Retry # ======================================================================== # TERMINATION 3: Explicit Exit Path # ======================================================================== # Provide clear exit option - step_id: "play_again" title: "Continue?" question: "Would you like to play again or exit?" tokens_for_ai: "Categorize as 'again' or 'exit'" buckets: [again, exit] transitions: again: metadata_clear: true # Reset game state next_section_and_step: "introduction:welcome" # Restart exit: next_section_and_step: "conclusion:goodbye_content" # Jump to end # ============================================================================== # METADATA SPECIAL VALUES # ============================================================================== # Reference guide for all metadata operations # String Operations: # ------------------ # "the-users-response" → Exact text of user's answer # "n+,value" → Append to comma-separated list # "n-,value" → Remove from comma-separated list # Numeric Operations: # ------------------- # "n+5" → Add 5 to existing value (or 0) # "n-3" → Subtract 3 from existing value # "n+random(1,10)" → Add random number between 1 and 10 # Static Values: # -------------- # "any string" → Store literal string # 42 → Store integer # true / false → Store boolean # ============================================================================== # ADVANCED FEATURES (New in v2.0) # ============================================================================== # ============================================================================== # TEMPLATE VARIABLES # ============================================================================== # Use {{variable_name}} syntax to insert dynamic values into content # Available in: content_blocks, questions, ai_feedback tokens # Built-in Variables: # ------------------- # {{current_attempt}} → Current attempt number (1, 2, 3...) # {{max_attempts}} → Maximum attempts allowed for this step # {{attempts_remaining}} → How many attempts left (max - current) # {{current_section}} → Current section_id # {{current_step}} → Current step_id # {{username}} → Name of the user who last responded # Metadata Variables: # ------------------- # {{metadata.key_name}} → Access any metadata value # {{metadata.score}} → Example: access score # {{metadata.player_name}} → Example: access player name # Example Usage: content_blocks: - "## Your Progress" - "Welcome back, {{metadata.player_name}}!" - "Score: {{metadata.score}}" - "Level: {{metadata.level}}" - "Attempt {{current_attempt}} of {{max_attempts}}" - "You have {{attempts_remaining}} tries remaining" question: "{{metadata.character_name}} asks: What will you do?" # Templates work in: # - step content_blocks # - transition content_blocks # - question text # - ai_feedback tokens_for_ai (for context, not rendered directly) # ============================================================================== # CONDITIONAL CONTENT BLOCKS # ============================================================================== # Show/hide content blocks based on metadata conditions # Format: Each content block can be a string OR an object with conditions content_blocks: # Simple string - always shown - "This is always displayed" # Conditional block - only shown if conditions met - text: "You're doing great! Keep going!" show_if: score_gte: 50 # Only show if score >= 50 - text: "Need more practice. Don't give up!" show_if: score_lt: 50 # Only show if score < 50 - text: "You found the secret key! 🗝️" show_if: inventory_contains: "key" # Only if inventory contains "key" - text: "Welcome, warrior! ⚔️" show_if: class: "warrior" # Only if metadata.class equals "warrior" - text: "Welcome, mage! 🔮" show_if: class: "mage" # Conditional blocks reduce step duplication - one step, multiple paths! # ============================================================================== # ADVANCED METADATA CONDITIONS # ============================================================================== # Rich comparison operators for metadata_conditions # Previously only supported equality: metadata_conditions: level: 5 # metadata.level must equal 5 # Now supports: # -------------- # Equality & Inequality: metadata_conditions: status: "active" # Equal to "active" status_ne: "inactive" # Not equal to "inactive" # Numeric Comparisons: metadata_conditions: score_gte: 100 # Greater than or equal to 100 score_gt: 99 # Greater than 99 score_lt: 200 # Less than 200 score_lte: 199 # Less than or equal to 199 level_between: [5, 10] # Between 5 and 10 (inclusive) # String Operations: metadata_conditions: inventory_contains: "sword" # Comma-separated list contains "sword" inventory_not_contains: "poison" # List does NOT contain "poison" name_matches: "^[A-Z]" # Regex match (starts with capital) # Existence Checks: metadata_conditions: has_key_exists: true # Key "has_key" must exist in metadata temp_flag_not_exists: true # Key "temp_flag" must NOT exist # Boolean Checks: metadata_conditions: is_admin: true # metadata.is_admin must be true is_locked: false # metadata.is_locked must be false # Combining Multiple Conditions (ALL must be true): metadata_conditions: score_gte: 100 level_gte: 5 inventory_contains: "key" quest_completed: true # All four conditions must be met # ============================================================================== # CONDITIONAL NAVIGATION # ============================================================================== # Choose different paths based on metadata state # OLD WAY (still works): transitions: answer_provided: next_section_and_step: "section_2:step_1" # NEW WAY - Conditional branches: transitions: answer_provided: next_section_and_step: - if: score_gte: 100 goto: "expert:challenge" - elif: score_gte: 50 goto: "intermediate:lesson" - elif: score_gte: 25 goto: "beginner:practice" - else: goto: "tutorial:basics" # Another example: Quest completion paths transitions: quest_complete: next_section_and_step: - if: all_secrets_found: true perfect_score: true goto: "endings:perfect_ending" - elif: all_secrets_found: true goto: "endings:good_ending" - elif: quest_failed: true goto: "endings:bad_ending" - else: goto: "endings:neutral_ending" # Conditions use same operators as metadata_conditions: # - Equality: key: value # - Comparisons: key_gte, key_gt, key_lt, key_lte # - String ops: key_contains, key_not_contains, key_matches # - Existence: key_exists, key_not_exists # - Boolean: key: true/false # ============================================================================== # PROGRESSIVE HINTS SYSTEM # ============================================================================== # Built-in system for providing hints that escalate with attempts # # Example step with progressive hints: # # - step_id: "difficult_question" # question: "What is the capital of Burkina Faso?" # # hints: # - attempt: 1 # text: "💡 Hint: It's not the largest city in the country." # counts_as_attempt: false # # - attempt: 2 # text: "💡 Hint: The name means 'City of Honest People'." # counts_as_attempt: false # # - attempt: 3 # text: "💡 Strong Hint: It starts with 'Oua' and ends with 'dougou'." # counts_as_attempt: false # # buckets: [correct, incorrect, need_hint] # # transitions: # correct: # content_blocks: # - "Excellent! Ouagadougou is correct!" # next_section_and_step: "next:step" # # incorrect: # content_blocks: # - "Not quite. Try again!" # next_section_and_step: "current:difficult_question" # # need_hint: # content_blocks: # - "Let me help you..." # counts_as_attempt: false # next_section_and_step: "current:difficult_question" # # Hints auto-display when attempt number matches # Hints support template variables: "Attempt {{current_attempt}} of {{max_attempts}}" progressive_hints_example: "See activity-test-v2-features.yaml for working example" # ============================================================================== # WEIGHTED RANDOM SELECTION # ============================================================================== # Choose random values with different probabilities # OLD WAY - Equal probability: metadata_random: loot: "sword" # 33% each loot: "dagger" # 33% each loot: "staff" # 33% each # NEW WAY - Weighted probabilities: metadata_weighted_random: loot: - value: "common_sword" weight: 70 # 70% chance - value: "rare_dagger" weight: 25 # 25% chance - value: "legendary_staff" weight: 5 # 5% chance # Weights don't need to sum to 100 - they're relative: metadata_weighted_random: reward: - value: "gold" weight: 10 # 10/(10+3+1) = 71.4% - value: "gem" weight: 3 # 3/(10+3+1) = 21.4% - value: "artifact" weight: 1 # 1/(10+3+1) = 7.1% # Also works with metadata_tmp_weighted_random for temporary values # Example: Random encounter transitions: explore_forest: metadata_weighted_random: encounter: - value: "nothing" weight: 50 # 50% - No encounter - value: "merchant" weight: 30 # 30% - Friendly merchant - value: "goblin" weight: 15 # 15% - Fight goblin - value: "treasure" weight: 5 # 5% - Find treasure! ai_feedback: tokens_for_ai: | Describe what happens based on metadata.encounter: - nothing: Peaceful walk through forest - merchant: Meet a traveling merchant - goblin: Surprise goblin attack! - treasure: Discover hidden treasure chest! # ============================================================================== # DYNAMIC QUESTION TEXT # ============================================================================== # Questions can now use template variables # Static question (old way): question: "What is 2 + 2?" # Dynamic question with templates (new way): question: "What is {{metadata.num1}} + {{metadata.num2}}?" # Example: Math quiz with random numbers # # - step_id: "addition" # pre_script: | # import random # result = { # "metadata": { # "num1": random.randint(1, 10), # "num2": random.randint(1, 10) # } # } # return result # # question: "What is {{metadata.num1}} + {{metadata.num2}}?" # # tokens_for_ai: | # Calculate the correct answer: {{metadata.num1}} + {{metadata.num2}} # Categorize as 'correct' if their answer matches. # # buckets: [correct, incorrect] # # More personalized question examples: # - "{{metadata.character_name}}, what is your quest?" # - "You have {{metadata.gold}} gold. How much do you spend?" # - "Round {{current_attempt}}: What's your move?" dynamic_question_example: "See activity-test-v2-features.yaml for working example" # ============================================================================== # BUILT-IN ATTEMPT COUNTER ACCESS # ============================================================================== # Access attempt information in templates # Available variables: # - {{current_attempt}} : 1, 2, 3, ... (current attempt number) # - {{max_attempts}} : 3 (or custom value from default_max_attempts_per_step) # - {{attempts_remaining}} : max_attempts - current_attempt # Examples: content_blocks: - "Attempt {{current_attempt}} of {{max_attempts}}" - "You have {{attempts_remaining}} tries left" question: "Try {{current_attempt}}: What's your answer?" ai_feedback: tokens_for_ai: | This is attempt {{current_attempt}} of {{max_attempts}}. They have {{attempts_remaining}} attempts remaining. Adjust your feedback based on the attempt number: - On their last attempt: Be clear and helpful - With 2 attempts left: Provide a gentle hint - With more attempts: Encourage them to think carefully # Conditional content based on attempts: content_blocks: - text: "First try - think carefully!" show_if: current_attempt: 1 - text: "Second try - you're getting closer!" show_if: current_attempt: 2 - text: "Last chance! Here's a hint..." show_if: current_attempt: 3 # ============================================================================== # SESSION PERSISTENCE (Twitch Plays Model) # ============================================================================== # How metadata and state persist across users and sessions # Key Facts: # ---------- # 1. ONE GAME STATE PER ROOM: All users in a room share the same activity state # 2. METADATA IS SHARED: When one user updates metadata, all users see it # 3. DATABASE PERSISTENCE: State survives browser refreshes and reconnections # 4. ANYONE CAN CONTROL: Any user can provide input to advance the shared game # 5. LIKE TWITCH PLAYS POKEMON: Collaborative control of single game instance # Lifecycle: # ---------- # Activity starts → State saved to database (room_id, section_id, step_id, metadata) # User interacts → Metadata updates, state progresses # Browser refreshes → State persists (loaded from database) # Activity completes → State deleted from database # Activity canceled → State deleted from database # Use Cases: # ---------- # - Classroom: Teacher projects, students call out answers collectively # - Collaboration: Multiple people solve puzzle together # - Public challenges: Community progresses through shared experience # - Learning together: Everyone learns from same shared game state # Implications for Activity Design: # ---------------------------------- # - Design for SHARED state, not per-player state # - Metadata represents THE GAME, not individual players # - Multiple users may answer - first valid response advances # - Consider: "What if 10 people are playing together?" # ============================================================================== # VALIDATION RULES # ============================================================================== # REQUIRED: # --------- # ✓ Every activity must have "sections" (at least one) # ✓ Every section needs: section_id, title, steps # ✓ Every step needs: step_id, title # ✓ Every step needs EITHER content_blocks OR question (or both) # ✓ Steps with questions need: buckets, transitions, tokens_for_ai # ✓ Every bucket must have a corresponding transition # ✓ All next_section_and_step targets must exist # FORBIDDEN: # ---------- # ✗ Terminal steps (no next_section_and_step) CANNOT have questions # ✗ Section IDs must be unique within activity # ✗ Step IDs must be unique within section # ✗ Random bucket names must exist in main buckets list # ✗ Random bucket probabilities must be 0.0 to 1.0 # WARNINGS: # --------- # ⚠ Total random bucket probability > 1.0 (overlapping events) # ⚠ Circular loops without exit path # ⚠ Python syntax errors in processing scripts # ============================================================================== # BEST PRACTICES # ============================================================================== # 1. START SIMPLE # - Begin with content-only steps and simple questions # - Add complexity incrementally # - Test frequently with CLI simulator # 2. CLEAR INSTRUCTIONS # - Write specific tokens_for_ai that explain each bucket clearly # - Give examples of what qualifies for each category # - Be generous in accepting valid responses # 3. METADATA STRATEGY # - Track meaningful state: score, progress, user choices # - Use descriptive key names: "programming_language" not "pl" # - Clean up temporary metadata with metadata_tmp_add # 4. RANDOM EVENTS # - Use probabilities that feel right (5-15% for rare events) # - Set counts_as_attempt: false for random buckets # - Don't override user navigation unless necessary # 5. FEEDBACK QUALITY # - Reference specific parts of user's answer # - Provide actionable suggestions for improvement # - Celebrate progress and effort # 6. TERMINATION # - Always provide clear path to completion # - Mark completion: metadata_add: activity_completed: "true" # - Give users a sense of accomplishment # 7. TESTING # - Validate YAML: python activity_yaml_validator.py your_activity.yaml # - Test all paths: source vars.sh && python research/guarded_ai.py your_activity.yaml # - Try wrong answers, edge cases, language switching # ============================================================================== # MODEL CONFIGURATION # ============================================================================== # Environment Variables (in vars.sh): # ------------------------------------ # MODEL_ENDPOINT_1=http://localhost:8080/v1 # MODEL_API_KEY_1=your-api-key # MODEL_NAME_1=model # Optional: actual model name for endpoint # # MODEL_ENDPOINT_2=http://localhost:8081/v1 # MODEL_API_KEY_2=your-api-key # MODEL_NAME_2=gpt-4 # # MODEL_ENDPOINT_3=http://localhost:8082/v1 # MODEL_API_KEY_3=your-api-key # MODEL_NAME_3=model # Recommended Models: # ------------------- # MODEL_1: Hermes-3-Llama-3.1-8B (default, fast, excellent for classification) # MODEL_2: Larger general model (if available) # MODEL_3: Qwen3-Coder-30B (for programming activities) # Model Selection Strategy: # ------------------------- # - Classifier: Use MODEL_1 (fast 8B model) for instant categorization # - Feedback: Use specialized model for domain-specific feedback # - Programming → MODEL_3 (Qwen3-Coder) # - General → MODEL_1 (Hermes) # - Advanced reasoning → MODEL_2 (larger model) # ============================================================================== # EXAMPLES # ============================================================================== # See these reference activities: # ------------------------------- # activity26-magic-8-ball.yaml - Looping, randomness, replayability # activity31-scientific-method.yaml - Educational scaffolding # activity37-programming-languages.yaml - Model overrides, code generation # activity40-fashion-empire-backrooms.yaml - Random buckets, complex navigation # ============================================================================== # END OF SPECIFICATION # ==============================================================================