Feature detail
Guided repetition ladder
Concept-first rep phases — light, hard, and recovery-light — with explicit attempt budgets and selection reasons so apps can teach first, then fade to independent proof.
Worked example
Rep-phase ladder
Four clean lights clear the light phase; an assisted hard attempt opens recovery-lights with a same-concept detour.
Input
{
"before": {
"conceptId": "factoring",
"independentPassCount": 0,
"supportedPassCount": 0,
"nextEligibleTurn": 1,
"lastSeenTurn": null,
"attempts": 0,
"supplementalExposureCount": 0,
"assistedCount": 0,
"skippedCount": 0,
"recentStruggleCount": 0,
"recoveryDue": false,
"retentionCheckEligibleTurn": null,
"retentionCheckPassed": false,
"mastered": false,
"lastOutcome": null,
"subskillStats": {
"recognition": {
"attempts": 0,
"cleanPasses": 0,
"supportedPasses": 0,
"misses": 0,
"lastMissedTurn": null
},
"setup": {
"attempts": 0,
"cleanPasses": 0,
"supportedPasses": 0,
"misses": 0,
"lastMissedTurn": null
}
},
"lightPassCount": 0,
"hardPassCount": 0,
"recoveryLightRemaining": 0,
"recoverySupportMode": "none"
},
"actions": [
{
"turn": 1,
"outcome": "independent_correct"
},
{
"turn": 2,
"outcome": "independent_correct"
},
{
"turn": 3,
"outcome": "independent_correct"
},
{
"turn": 4,
"outcome": "independent_correct"
},
{
"turn": 5,
"outcome": "assisted"
}
]
}Output
{
"afterLightSweep": {
"state": {
"conceptId": "factoring",
"independentPassCount": 0,
"supportedPassCount": 0,
"nextEligibleTurn": 6,
"lastSeenTurn": 4,
"attempts": 4,
"supplementalExposureCount": 0,
"assistedCount": 0,
"skippedCount": 0,
"recentStruggleCount": 0,
"recoveryDue": false,
"retentionCheckEligibleTurn": null,
"retentionCheckPassed": false,
"mastered": false,
"lastOutcome": "independent_correct",
"subskillStats": {
"recognition": {
"attempts": 0,
"cleanPasses": 0,
"supportedPasses": 0,
"misses": 0,
"lastMissedTurn": null
},
"setup": {
"attempts": 0,
"cleanPasses": 0,
"supportedPasses": 0,
"misses": 0,
"lastMissedTurn": null
}
},
"lightPassCount": 4,
"hardPassCount": 0,
"recoveryLightRemaining": 0,
"recoverySupportMode": "none"
},
"plan": {
"repPhase": "hard",
"repIndex": 5,
"supportMode": "none",
"hardAttemptLimit": 3
}
},
"afterHardStumble": {
"state": {
"conceptId": "factoring",
"independentPassCount": 0,
"supportedPassCount": 0,
"nextEligibleTurn": 6,
"lastSeenTurn": 5,
"attempts": 5,
"supplementalExposureCount": 0,
"assistedCount": 1,
"skippedCount": 0,
"recentStruggleCount": 1,
"recoveryDue": true,
"retentionCheckEligibleTurn": null,
"retentionCheckPassed": false,
"mastered": false,
"lastOutcome": "assisted",
"subskillStats": {
"recognition": {
"attempts": 0,
"cleanPasses": 0,
"supportedPasses": 0,
"misses": 0,
"lastMissedTurn": null
},
"setup": {
"attempts": 0,
"cleanPasses": 0,
"supportedPasses": 0,
"misses": 0,
"lastMissedTurn": null
}
},
"lightPassCount": 4,
"hardPassCount": 0,
"recoveryLightRemaining": 2,
"recoverySupportMode": "same-concept-recovery"
},
"plan": {
"repPhase": "recovery-light",
"repIndex": 3,
"supportMode": "same-concept-recovery",
"hardAttemptLimit": null
},
"nextConceptAtTurn6": "factoring"
}
}Real source excerpt
Repetition phases, attempt budgets, and selection reasons
export type RepetitionPhase = 'light' | 'hard' | 'recovery-light'
export type RecoverySupportMode = 'none' | 'same-concept-recovery' | 'support-concept-recovery'
export type ConceptSelectionReason =
| 'new_concept'
| 'guided_mastery'
| 'recovery_due'
| 'retention_due'
| `weakest_subskill:${string}`
export interface GuidedConceptProgressState<TSubskill extends string = string> extends ConceptScheduleState<TSubskill> {
lightPassCount: number
hardPassCount: number
recoveryLightRemaining: number
recoverySupportMode: RecoverySupportMode
}
export type GuidedConceptProgressMap<TSubskill extends string = string> =
Record<string, GuidedConceptProgressState<TSubskill>>
export interface ConceptRepetitionPlan {
repPhase: RepetitionPhase
repIndex: 1 | 2 | 3 | 4 | 5 | 6
supportMode: RecoverySupportMode
hardAttemptLimit: number | null
}
export type ConceptStateBadge =
| 'Emerging'
| 'Supported'
| 'Independent'
| 'Mastered'
| 'Retention due'
| 'Recovery due'
export const LIGHT_REP_TARGET = 4
export const HARD_REP_TARGET = 2
export const HARD_ATTEMPT_LIMIT = 3
export const HARD_FAILURE_RECOVERY_LIGHTS = 2