diff --git a/rslib/src/scheduler/fsrs/params.rs b/rslib/src/scheduler/fsrs/params.rs index 276150676..6da02776a 100644 --- a/rslib/src/scheduler/fsrs/params.rs +++ b/rslib/src/scheduler/fsrs/params.rs @@ -313,9 +313,12 @@ pub(crate) fn reviews_for_fsrs( if entry.review_kind == RevlogReviewKind::Filtered && entry.ease_factor == 0 { continue; } + // For incomplete review histories, initial memory state is based on the first + // user-graded review after the cutoff date with interval >= 1d. let within_cutoff = entry.id.0 > ignore_revlogs_before.0; let user_graded = matches!(entry.button_chosen, 1..=4); - if user_graded && within_cutoff { + let interday = entry.interval >= 1 || entry.interval <= -86400; + if user_graded && within_cutoff && interday { first_user_grade_idx = Some(index); } @@ -477,6 +480,7 @@ pub(crate) mod tests { review_kind, id: days_ago_ms(days_ago).into(), button_chosen: 3, + interval: 1, ..Default::default() } } @@ -711,6 +715,28 @@ pub(crate) mod tests { assert_eq!(convert_ignore_before(revlogs, true, days_ago_ms(9)), None); } + #[test] + fn skip_initial_relearning_steps() { + let revlogs = &[ + revlog(RevlogReviewKind::Review, 10), + RevlogEntry { + button_chosen: 1, // Again + interval: -600, + ..revlog(RevlogReviewKind::Review, 8) + }, + revlog(RevlogReviewKind::Relearning, 8), + revlog(RevlogReviewKind::Review, 6), + ]; + // | = Ignore before + // A = Again + // X = Relearning + // R | A X R + assert_eq!( + convert_ignore_before(revlogs, false, days_ago_ms(9)), + fsrs_items!([review(0)], [review(0), review(2)]) + ); + } + #[test] fn ignore_before_date_between_learning_steps_when_reviewing() { let revlogs = &[