feat(help-system): add schema for progressive help and feedback loop

Phase 1 of help system implementation:

Schema changes:
- Add HelpLevel type (0-3) to session-plans.ts
- Extend SlotResult with helpLevelUsed, incorrectAttempts, helpTrigger
- Add REINFORCEMENT_CONFIG constants for mastery credit multipliers
- Add reinforcement tracking columns to player_skill_mastery:
  - needsReinforcement: flag for skills needing extra practice
  - lastHelpLevel: track struggling patterns
  - reinforcementStreak: track progress toward clearing reinforcement
- Add StudentHelpSettings interface and column to players:
  - helpMode: 'auto' | 'manual' | 'teacher-approved'
  - autoEscalationTimingMs: configurable help timing thresholds
  - beginnerFreeHelp: unlimited L1-L2 help without penalty
  - advancedRequiresApproval: require teacher auth for L2+ help

This closes the feedback loop between help usage and session planning:
- Help usage informs skill mastery scoring
- Reinforcement flags guide session planner to include extra practice
- Teacher has visibility into which skills need attention

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-12-06 15:16:52 -06:00
parent f153dddfce
commit 41c46038d8
6 changed files with 1184 additions and 0 deletions

View File

@ -0,0 +1,12 @@
-- Custom SQL migration file, put your code below! --
-- Add reinforcement tracking columns to player_skill_mastery table
ALTER TABLE `player_skill_mastery` ADD `needs_reinforcement` integer DEFAULT 0 NOT NULL;
--> statement-breakpoint
ALTER TABLE `player_skill_mastery` ADD `last_help_level` integer DEFAULT 0 NOT NULL;
--> statement-breakpoint
ALTER TABLE `player_skill_mastery` ADD `reinforcement_streak` integer DEFAULT 0 NOT NULL;
--> statement-breakpoint
-- Add help_settings column to players table
ALTER TABLE `players` ADD `help_settings` text DEFAULT '{"helpMode":"auto","autoEscalationTimingMs":{"level1":30000,"level2":60000,"level3":90000},"beginnerFreeHelp":true,"advancedRequiresApproval":false}';

File diff suppressed because it is too large Load Diff

View File

@ -190,6 +190,13 @@
"when": 1765035309900,
"tag": "0026_add_practice_sessions",
"breakpoints": true
},
{
"idx": 27,
"version": "6",
"when": 1765055035935,
"tag": "0027_help_system_schema",
"breakpoints": true
}
]
}

View File

@ -77,6 +77,29 @@ export const playerSkillMastery = sqliteTable(
updatedAt: integer('updated_at', { mode: 'timestamp' })
.notNull()
.$defaultFn(() => new Date()),
// ---- Reinforcement Tracking (for help system feedback loop) ----
/**
* Whether this skill needs reinforcement
* Set to true when student uses heavy help (level 2+) or has multiple incorrect attempts
* Cleared after N consecutive correct answers without help
*/
needsReinforcement: integer('needs_reinforcement', { mode: 'boolean' })
.notNull()
.default(false),
/**
* Last help level used on this skill (0-3)
* Used to track struggling patterns
*/
lastHelpLevel: integer('last_help_level').notNull().default(0),
/**
* Consecutive correct answers without heavy help since reinforcement was flagged
* Resets to 0 when reinforcement is cleared or when help level 2+ is used
*/
reinforcementStreak: integer('reinforcement_streak').notNull().default(0),
},
(table) => ({
/** Index for fast lookups by playerId */
@ -110,6 +133,39 @@ export const MASTERY_CONFIG = {
minimumForPracticing: 5,
} as const
/**
* Reinforcement configuration constants
*/
export const REINFORCEMENT_CONFIG = {
/**
* Help level threshold that triggers reinforcement flag
* Level 2+ (decomposition or bead arrows) indicates the student needed significant help
*/
helpLevelThreshold: 2,
/**
* Number of consecutive correct answers without heavy help to clear reinforcement
*/
streakToClear: 3,
/**
* Maximum help level that still counts toward clearing reinforcement
* Level 1 (hints) is OK, but Level 2+ resets the streak
*/
maxHelpLevelToCount: 1,
/**
* Mastery credit multipliers based on help level
* Used when updating skill mastery after a correct answer
*/
creditMultipliers: {
0: 1.0, // No help: full credit
1: 1.0, // Hint only: full credit
2: 0.5, // Decomposition: half credit
3: 0.25, // Bead arrows: quarter credit
} as Record<0 | 1 | 2 | 3, number>,
} as const
/**
* Calculate the mastery level based on current stats
*/

View File

@ -2,6 +2,49 @@ import { createId } from '@paralleldrive/cuid2'
import { index, integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { users } from './users'
/**
* Help mode for practice sessions
* - 'auto': Help automatically appears after time thresholds
* - 'manual': Help only appears when student clicks for it
* - 'teacher-approved': Student can request help, but teacher must approve
*/
export type HelpMode = 'auto' | 'manual' | 'teacher-approved'
/**
* Settings that control help behavior during practice sessions
*/
export interface StudentHelpSettings {
/** How help is triggered */
helpMode: HelpMode
/** For 'auto' mode: milliseconds before each help level appears */
autoEscalationTimingMs: {
level1: number // Default: 30000 (30s)
level2: number // Default: 60000 (60s)
level3: number // Default: 90000 (90s)
}
/** For beginners: unlimited L1-L2 help without mastery penalty */
beginnerFreeHelp: boolean
/** For advanced: L2+ help requires teacher approval */
advancedRequiresApproval: boolean
}
/**
* Default help settings for new students
*/
export const DEFAULT_HELP_SETTINGS: StudentHelpSettings = {
helpMode: 'auto',
autoEscalationTimingMs: {
level1: 30000,
level2: 60000,
level3: 90000,
},
beginnerFreeHelp: true,
advancedRequiresApproval: false,
}
/**
* Players table - user-created player profiles for games
*
@ -36,6 +79,14 @@ export const players = sqliteTable(
createdAt: integer('created_at', { mode: 'timestamp' })
.notNull()
.$defaultFn(() => new Date()),
/**
* Help settings for practice sessions
* Controls how help is triggered and escalated
*/
helpSettings: text('help_settings', { mode: 'json' })
.$type<StudentHelpSettings>()
.default(JSON.stringify(DEFAULT_HELP_SETTINGS)),
},
(table) => ({
/** Index for fast lookups by userId */

View File

@ -128,6 +128,15 @@ export interface SessionAdjustment {
previousHealth: SessionHealth
}
/**
* Help level used during a problem
* - 0: No help requested
* - 1: Coach hint only (e.g., "Add the tens digit first")
* - 2: Decomposition shown (e.g., "45 + 27 = 45 + 20 + 7")
* - 3: Bead highlighting (arrows showing which beads to move)
*/
export type HelpLevel = 0 | 1 | 2 | 3
/**
* Result of a single problem slot
*/
@ -143,6 +152,17 @@ export interface SlotResult {
skillsExercised: string[]
usedOnScreenAbacus: boolean
timestamp: Date
// ---- Help Tracking (for feedback loop) ----
/** Maximum help level used during this problem (0 = no help) */
helpLevelUsed: HelpLevel
/** Number of incorrect attempts before getting the right answer */
incorrectAttempts: number
/** How help was triggered */
helpTrigger?: 'none' | 'manual' | 'auto-time' | 'auto-errors' | 'teacher-approved'
}
export type SessionStatus = 'draft' | 'approved' | 'in_progress' | 'completed' | 'abandoned'