Skip to content

fix: Use fsrs config bool for set due date#4792

Open
Luc-Mcgrady wants to merge 2 commits intoankitects:mainfrom
Luc-Mcgrady:use-fsrs-config-bool-for-set-due-date
Open

fix: Use fsrs config bool for set due date#4792
Luc-Mcgrady wants to merge 2 commits intoankitects:mainfrom
Luc-Mcgrady:use-fsrs-config-bool-for-set-due-date

Conversation

@Luc-Mcgrady
Copy link
Copy Markdown
Contributor

@Luc-Mcgrady Luc-Mcgrady commented May 7, 2026

Linked issue (required)

fixes #4791

Summary / motivation (required)

Previously, using set due date on a new card was broken for FSRS because new cards didn't have a memory state which was how the set due date function determined if FSRS is enabled or not. See #4035 (comment).

Steps to reproduce (required, use N/A if not applicable)

  1. create a new card
  2. set it's due date
  3. set it's due date again
  4. the due date won't change
  5. use grade now
  6. the card will be scheduled based on the future date due date rather than now

How to test (required)

I followed the reproduction steps and instead, after using set due date the interval is always set to 0; as to not affect grade now.

Checklist (minimum)

  • I ran ./ninja check or an equivalent relevant check locally.
  • I added or updated tests when the change is non-trivial or behavior changed.

Details

Before / after behavior (optional)

Risk / compatibility / migration (optional)

If there are any ways I have not foreseen that an interval can be set to 0, then it may cause problems with this approach.

UI evidence (required for visual changes; otherwise N/A)

Scope

  • This PR is focused on one change (no unrelated edits).

@Luc-Mcgrady Luc-Mcgrady marked this pull request as draft May 7, 2026 14:49
@Luc-Mcgrady Luc-Mcgrady marked this pull request as ready for review May 7, 2026 15:03
@user1823
Copy link
Copy Markdown
Contributor

user1823 commented May 7, 2026

A detailed explanation why this occurs

(it took me a lot of effort to figure it out)

With the previous code, when memory states are absent, fsrs_enabled is false. So, the interval is calculated from

        let new_interval = …
        } else if force_reset || !matches!(self.ctype, CardType::Review | CardType::Relearn) {
            days_from_today.max(1)
        } else {
            self.interval.max(1)
        };

So, for a new card, the interval becomes days_from_today (at least 1).

If you use SDD on the same card again (before doing a review), memory states are still absent but it is a review card now.

So, if force_reset (!) is used (which AnkiDroid hard-codes to true when FSRS is enabled), the interval becomes the new days_from_today (at least 1). So, the interval changes again.

If force_reset is not used, the interval remains unchanged (self.interval.max(1)). I don't consider it a bug, though, because the user didn't use the force_reset (!) option. So, they shouldn't expect the interval to change.

When this SDD'ed card is reviewed, the card has no usable revlog and a non-zero interval. So, memory_state_from_sm2 is used to determine the memory states (the long standing issue - open-spaced-repetition/fsrs4anki#675)

let memory_state = if let Some(i) = item {
Some(fsrs.memory_state(i.item, i.starting_state)?)
} else if self.ctype == CardType::New || self.interval == 0 {
None
} else {
// no valid revlog entries; infer state from current card state
Some(fsrs.memory_state_from_sm2(
self.ease_factor(),
self.interval as f32,
historical_retention,
)?)

The First Fix

Use FSRS config bool instead of memory states to know whether FSRS is enabled.

This seems to be good change, not only for new cards but for any card that lacks memory states due to any reason. (#4035 (comment))

The Second Fix

Hard-code the interval to 0 when SDD is used on a new card

This is slightly controversial and may require discussion.

Ideally, users should not use SDD on a new card because if they do, FSRS can't know the best way to schedule the cards. The users should either use Grade Now (if they did active recall outside Anki and want to reflect that in Anki) or use Learn now (if they want to do their first active recall within Anki).

Learn Now isn't implemented in Anki yet, but it is available as an add-on (https://github.com/Ajatt-Tools/learn-now-button). I have also requested for its native implementation in Anki.

By hard-coding the interval to 0, SDD behaves more like Learn now, but with a few differences:

  • SDD allows scheduling the card to any day, not just today (which is an advantage).
  • The SDD'ed cards enter the review queue (so they will appear based on the review sort order). The Learn now cards enter the intraday learning queue (so they will appear in the order of due, but after partitioning — see Feat/Prioritize previously attempted cards in intraday learning queue #4393). I am not sure which one is better.

The main problem is that users will continue to use SDD on new cards even when they should actually use Grade Now because they don't know that they shouldn't use SDD.

So, what if we disable SDD on new cards and implement a feature similar to Learn now and then ask users to use either Grade now or Learn now if they try to use SDD on a new card?

If we do this, we will need a new version of the Learn now (instead of just porting the add-on's version) that also allows scheduling the card to any day so that there is no loss of functionality when migrating from SDD to Learn now.

@user1823
Copy link
Copy Markdown
Contributor

user1823 commented May 7, 2026

To summarize, this seems to be mergeable in the current state if we are not worried about the users who continue to misuse SDD on new cards when they should actually use Grade Now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect interval for new cards with "set due date"

2 participants