For AI agents: the complete documentation index is available at https://open-spaced-repetition.github.io/ts-fsrs/ja-JP/llms.txt, the full documentation bundle is available at https://open-spaced-repetition.github.io/ts-fsrs/ja-JP/llms-full.txt, and this page is available as Markdown at https://open-spaced-repetition.github.io/ts-fsrs/ja-JP/guide/migration.md.

v6 と FSRS-7 への移行

パッケージとモデルのバージョンは別です。ts-fsrs v5 は既に FSRS-6 を使用しています。まず FSRS-6 を維持したままアプリケーションと保存データを v6 API に移行し、その後モデルを移行します。このガイドは本サイトの v6 API に対応しています。記載されたインターフェースを提供する v6 バージョンを指定してください。

移行後は legacy API を使用しないでください

defineScheduler と明示的なモデルのエントリポイントを使用してください。移行後のコードでは fsrs()、FSRS、createEmptyCard、generatorParameters、旧スケジューラーのクラスや strategy、ルートから export される旧 Card、CardInput、ReviewLog、FSRSParameters などの legacy 型を使わないでください。互換性のために export が残っていても、v6 の推奨インターフェースではありません。以下の旧名称は比較用です。実行可能なサンプルは legacy API や型を使用しません。

For your Agent
Agent に移行を実装してもらう

リポジトリにアクセスできる Agent にコピーします。移行先を確認し、変更の TODO List を作成します。ユーザーがリストを承認してから実装・検証します。

1. FSRS-6 を維持して v6 API に移行する

スケジュール方針を定義する

defineScheduler({ model, chrono }) は再利用可能な定義を作成します。.use(...) は middleware を組み合わせた新しい定義を返し、.create({ config }) は設定を検証して独立したスケジューラーを作成します。定義はアプリケーション全体で保持し、ユーザーやデッキの設定に応じてインスタンスを作成してください。

定義だけでは v5 のすべてのスケジュール方針は含まれません。以下は現在の標準プリセットと同じ順序で middleware を組み合わせます。目標保持率、ファジング、統計、予定日数、学習ステップ、最大間隔、単調間隔の順です。実際の設定と FSRS-6 の重みを引き継いでください。dateChrono の fractionalDays: false は UTC 暦日の差を使います。API だけを移行する段階では、影響を検証せずに時間の計算方針まで変えないでください。

import { 
const dateChrono: Chrono<{
    readonly time: SRSSchema<{
        input: Date;
        output: Date;
    }>;
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly fields: {
        readonly card: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
            };
        }>;
        readonly revlog: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
                reviewTime: Date;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
                reviewTime: Date;
            };
        }>;
    };
}>
dateChrono
,
function defineScheduler<const M extends AnyModel, const C extends AnyChrono>(definition: {
    readonly model: M;
    readonly chrono: C;
}): CustomScheduler<SchedulerNameOf<M>, SchedulerEnvFor<M, C, readonly []>, M, C>
defineScheduler
,
type Rating = 0 | 1 | 2 | 3 | 4
const Rating: Readonly<{
    readonly Manual: 0;
    readonly Again: 1;
    readonly Hard: 2;
    readonly Good: 3;
    readonly Easy: 4;
}>
Rating
,
type State = 0 | 1 | 2 | 3
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
} from 'ts-fsrs'
import {
const schedulerDesiredRetentionMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly desiredRetention: number | Record<Grade, number>;
        };
        output: {
            readonly desiredRetention: Record<Grade, number>;
        };
    }>;
}>
schedulerDesiredRetentionMiddleware
,
const schedulerFuzzingMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays?: boolean;
        };
        output: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays: boolean;
        };
    }>;
    readonly cardInitInput: SRSSchema<{
        input: {
            readonly cardId?: string | number;
        };
        output: {
            readonly cardId?: string | number;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly cardId: string | number;
            readonly reps: number;
        };
        output: {
            readonly cardId: string | number;
            readonly reps: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly cardId: string | number;
        };
        output: {
            readonly cardId: string | number;
        };
    }>;
}>
schedulerFuzzingMiddleware
,
const schedulerLearningStepsMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly learningSteps: readonly StepUnit[];
            readonly relearningSteps: readonly StepUnit[];
            readonly enableShortTerm: boolean;
            readonly graduatingInterval?: number;
            readonly allowModelOverride?: boolean;
        };
        output: {
            readonly learningSteps: readonly StepUnit[];
            readonly relearningSteps: readonly StepUnit[];
            readonly enableShortTerm: boolean;
            readonly graduatingInterval: number;
            readonly allowModelOverride: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly learningStep?: number;
        };
        output: {
            readonly learningStep: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly learningStep?: number;
        };
        output: {
            readonly learningStep: number;
        };
    }>;
}>
schedulerLearningStepsMiddleware
,
const schedulerMaximumIntervalMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly maximumInterval?: number;
        };
        output: {
            readonly maximumInterval: number;
        };
    }>;
}>

Register after learning steps so explicit steps can keep their exact delay.

schedulerMaximumIntervalMiddleware
,
const schedulerMonotonicIntervalMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly maximumInterval?: number;
        };
        output: {
            readonly maximumInterval: number;
        };
    }>;
}>

Register after fuzzing and learning steps, when present.

schedulerMonotonicIntervalMiddleware
,
const schedulerScheduledDaysMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly scheduledDays: number;
        };
        output: {
            readonly scheduledDays: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly scheduledDays: number;
        };
        output: {
            readonly scheduledDays: number;
        };
    }>;
}>

Register before interval middleware so it records their final value on unwind.

schedulerScheduledDaysMiddleware
,
const schedulerStatsMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly clearStatsOnForget?: boolean | undefined;
        };
        output: {
            readonly clearStatsOnForget: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly reps: number;
            readonly lapses: number;
        };
        output: {
            readonly reps: number;
            readonly lapses: number;
        };
    }>;
}>
schedulerStatsMiddleware
,
} from 'ts-fsrs/middlewares' import {
const FSRS6Model: Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>
FSRS6Model
,
const migrateFSRS6Parameters: (parameters?: readonly number[]) => number[]
migrateFSRS6Parameters
} from 'ts-fsrs/models/fsrs-6'
// Keep the definition at application scope; create an instance per config. export const
const definition: CustomScheduler<"fsrs-6", ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>
definition
=
defineScheduler<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>>(definition: {
    ...;
}): CustomScheduler<...>
defineScheduler
({
model: Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>
model
:
const FSRS6Model: Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>
FSRS6Model
,
chrono: Chrono<{
    readonly time: SRSSchema<{
        input: Date;
        output: Date;
    }>;
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly fields: {
        readonly card: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
            };
        }>;
        readonly revlog: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
                reviewTime: Date;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
                reviewTime: Date;
            };
        }>;
    };
}>
chrono
:
const dateChrono: Chrono<{
    readonly time: SRSSchema<{
        input: Date;
        output: Date;
    }>;
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly fields: {
        readonly card: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
            };
        }>;
        readonly revlog: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
                reviewTime: Date;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
                reviewTime: Date;
            };
        }>;
    };
}>
dateChrono
,
}).
CustomScheduler<"fsrs-6", SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, Model<...>, Chrono<...>>.use: <readonly [Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly desiredRetention: number | Record<Grade, number>;
        };
        output: {
            readonly desiredRetention: Record<Grade, number>;
        };
    }>;
}>, Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays?: boolean;
        };
        output: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays: boolean;
        };
    }>;
    readonly cardInitInput: SRSSchema<{
        input: {
            readonly cardId?: string | number;
        };
        output: {
            readonly cardId?: string | number;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly cardId: string | number;
            readonly reps: number;
        };
        output: {
            readonly cardId: string | number;
            readonly reps: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly cardId: string | number;
        };
        output: {
            readonly cardId: string | number;
        };
    }>;
}>, ... 4 more ..., Middleware<...>]>(middlewares_0: Middleware<...>, middlewares_1: Middleware<...>, middlewares_2: Middleware<...>, middlewares_3: Middleware<...>, middlewares_4: Middleware<...>, middlewares_5: Middleware<...>, middlewares_6: Middleware<...>) => CustomScheduler<...>

Returns a new scheduler definition extended with the supplied middleware. The current scheduler and any cores created from it remain unchanged.

use
(
const schedulerDesiredRetentionMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly desiredRetention: number | Record<Grade, number>;
        };
        output: {
            readonly desiredRetention: Record<Grade, number>;
        };
    }>;
}>
schedulerDesiredRetentionMiddleware
,
const schedulerFuzzingMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays?: boolean;
        };
        output: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays: boolean;
        };
    }>;
    readonly cardInitInput: SRSSchema<{
        input: {
            readonly cardId?: string | number;
        };
        output: {
            readonly cardId?: string | number;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly cardId: string | number;
            readonly reps: number;
        };
        output: {
            readonly cardId: string | number;
            readonly reps: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly cardId: string | number;
        };
        output: {
            readonly cardId: string | number;
        };
    }>;
}>
schedulerFuzzingMiddleware
,
const schedulerStatsMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly clearStatsOnForget?: boolean | undefined;
        };
        output: {
            readonly clearStatsOnForget: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly reps: number;
            readonly lapses: number;
        };
        output: {
            readonly reps: number;
            readonly lapses: number;
        };
    }>;
}>
schedulerStatsMiddleware
,
const schedulerScheduledDaysMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly scheduledDays: number;
        };
        output: {
            readonly scheduledDays: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly scheduledDays: number;
        };
        output: {
            readonly scheduledDays: number;
        };
    }>;
}>

Register before interval middleware so it records their final value on unwind.

schedulerScheduledDaysMiddleware
,
const schedulerLearningStepsMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly learningSteps: readonly StepUnit[];
            readonly relearningSteps: readonly StepUnit[];
            readonly enableShortTerm: boolean;
            readonly graduatingInterval?: number;
            readonly allowModelOverride?: boolean;
        };
        output: {
            readonly learningSteps: readonly StepUnit[];
            readonly relearningSteps: readonly StepUnit[];
            readonly enableShortTerm: boolean;
            readonly graduatingInterval: number;
            readonly allowModelOverride: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly learningStep?: number;
        };
        output: {
            readonly learningStep: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly learningStep?: number;
        };
        output: {
            readonly learningStep: number;
        };
    }>;
}>
schedulerLearningStepsMiddleware
,
const schedulerMaximumIntervalMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly maximumInterval?: number;
        };
        output: {
            readonly maximumInterval: number;
        };
    }>;
}>

Register after learning steps so explicit steps can keep their exact delay.

schedulerMaximumIntervalMiddleware
,
const schedulerMonotonicIntervalMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly maximumInterval?: number;
        };
        output: {
            readonly maximumInterval: number;
        };
    }>;
}>

Register after fuzzing and learning steps, when present.

schedulerMonotonicIntervalMiddleware
) export const
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
=
const definition: CustomScheduler<"fsrs-6", ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>
definition
.
CustomScheduler<"fsrs-6", ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>.create: (ctx: {
    readonly config: Omit<SchedulerConfigInput<Model<{
        readonly name: "fsrs-6";
        readonly config: SRSSchema<{
            input: {
                readonly weights: number[];
                readonly enableShortTerm: boolean;
                readonly numRelearningSteps: number;
            };
            output: {
                readonly weights: number[];
                readonly enableShortTerm: boolean;
                readonly numRelearningSteps: number;
            };
        }>;
        readonly memoryState: SRSSchema<{
            input: {
                stability: number;
                difficulty: number;
            };
            output: {
                stability: number;
                difficulty: number;
            };
        }>;
        readonly algorithm: FSRS6Algorithm;
    }>, Chrono<...>, readonly []>, "fractionalDays" | ... 8 more ... | "clearStatsOnForget"> & {
        ...;
    };
}) => SchedulerCore<...>
create
({
config: Omit<SchedulerConfigInput<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, "fractionalDays" | ... 8 more ... | "clearStatsOnForget"> & {
    ...;
}
config
: {
weights:
function migrateFSRS6Parameters(parameters?: readonly number[]): number[]
migrateFSRS6Parameters
(), // Pass the user's existing FSRS-6 weights here.
fractionalDays: false, enableShortTerm: true, numRelearningSteps: 1, // Keep equal to relearningSteps.length. desiredRetention: 0.9, learningSteps: ['1m', '10m'], relearningSteps: ['10m'], enableFuzz: false, maximumInterval: 36500, clearStatsOnForget: false, // Match v5 forget's default counter behavior. }, }) export type
type Card = {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
Card
= ReturnType<typeof
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
.
SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.newCard: (options?: (Omit<{
    readonly now?: Date | undefined;
} & {
    readonly cardId?: string | number | undefined;
}, "now"> & {
    readonly now?: Date | undefined;
}) | undefined) => {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
newCard
>
export type
type Revlog = {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    learningStep: number;
    scheduledDays: number;
    reviewTime: Date;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    rating: Grade;
}
Revlog
= ReturnType<typeof
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
.
SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.review: (input: {
    readonly card: Readonly<Omit<Readonly<Omit<Readonly<{
        stability: number;
        difficulty: number;
    } & {
        dueAt: Date;
        lastReviewAt: Date | null;
    } & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
        readonly cardId: string | number;
        readonly reps: number;
        readonly learningStep?: number | undefined;
        readonly scheduledDays: number;
        readonly lapses: number;
    }, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>, keyof SchedulerCoreFields$1<...>> & SchedulerCoreFields$1<...>>;
    readonly grade: Grade;
    readonly now?: Date | undefined;
}) => {
    ...;
}
review
>['revlog']
// An application's stored v5 record, not a legacy library type. type
type StoredV5Card = {
    due: string;
    last_review?: string | null;
    stability: number;
    difficulty: number;
    state: State;
    scheduled_days: number;
    learning_steps: number;
    reps: number;
    lapses: number;
}
StoredV5Card
= {
due: string last_review?: string | null stability: number difficulty: number state:
type State = 0 | 1 | 2 | 3
State
scheduled_days: number learning_steps: number reps: number lapses: number } export function
function convertCard(stored: StoredV5Card, cardId: Card["cardId"]): Card
convertCard
(
stored:
type StoredV5Card = {
    due: string;
    last_review?: string | null;
    stability: number;
    difficulty: number;
    state: State;
    scheduled_days: number;
    learning_steps: number;
    reps: number;
    lapses: number;
}
StoredV5Card
,
cardId:
type Card = {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
Card
['cardId']
):
type Card = {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
Card
{
const scheduleStatus = { [
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
.New]: 'new',
[
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
.Learning]: 'learning',
[
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
.Review]: 'review',
[
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
.Relearning]: 'learning',
} as const return
const definition: CustomScheduler<"fsrs-6", ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>
definition
.
CustomScheduler<"fsrs-6", ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>.schema: SchedulerSchema<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>
schema
.
SchedulerSchema<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>.card: SRSSchema<{
    input: Readonly<Omit<Readonly<{
        stability: number;
        difficulty: number;
    } & {
        dueAt: Date;
        lastReviewAt: Date | null;
    } & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
        readonly cardId: string | number;
        readonly reps: number;
        readonly learningStep?: number | undefined;
        readonly scheduledDays: number;
        readonly lapses: number;
    }, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>;
    output: Mutable<...>;
}>
card
.
SRSSchema<{ input: Readonly<Omit<Readonly<{ stability: number; difficulty: number; } & { dueAt: Date; lastReviewAt: Date | null; } & SchedulerCoreFields$1<"new" | "learning" | "review">> & { ...; }, keyof SchedulerCoreFields$1<...>> & SchedulerCoreFields$1<...>>; output: Mutable<...>; }>.parse(input: unknown): Mutable<Omit<Mutable<{
    stability: number;
    difficulty: number;
} & {
    dueAt: Date;
    lastReviewAt: Date | null;
} & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
    readonly cardId: string | number;
    readonly reps: number;
    readonly learningStep: number;
    readonly scheduledDays: number;
    readonly lapses: number;
}, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>
parse
({
cardId, dueAt: new Date(stored.due), lastReviewAt: stored.state ===
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
.New || stored.last_review == null
? null : new Date(stored.last_review), stability: stored.stability, difficulty: stored.difficulty, state: stored.state, scheduleStatus: scheduleStatus[stored.state], scheduledDays: stored.scheduled_days, learningStep: stored.learning_steps, reps: stored.reps, lapses: stored.lapses, }) } export const storedCard = { due: '2026-01-03T00:00:00.000Z', last_review: '2026-01-01T00:00:00.000Z', stability: 2, difficulty: 5, state:
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
.Review,
scheduled_days: 2, learning_steps: 0, reps: 3, lapses: 0, } satisfies
type StoredV5Card = {
    due: string;
    last_review?: string | null;
    stability: number;
    difficulty: number;
    state: State;
    scheduled_days: number;
    learning_steps: number;
    reps: number;
    lapses: number;
}
StoredV5Card
const
const card: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
card
=
function convertCard(stored: StoredV5Card, cardId: Card["cardId"]): Card
convertCard
(storedCard, 'card-1')
// An overdue review: previous review, original due date and now are distinct. const now = new Date('2026-01-04T00:00:00.000Z') const
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
=
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
.
SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.review: (input: {
    readonly card: Readonly<Omit<Readonly<Omit<Readonly<{
        stability: number;
        difficulty: number;
    } & {
        dueAt: Date;
        lastReviewAt: Date | null;
    } & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
        readonly cardId: string | number;
        readonly reps: number;
        readonly learningStep?: number | undefined;
        readonly scheduledDays: number;
        readonly lapses: number;
    }, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>, keyof SchedulerCoreFields$1<...>> & SchedulerCoreFields$1<...>>;
    readonly grade: Grade;
    readonly now?: Date | undefined;
}) => {
    ...;
}
review
({ card, grade:
const Rating: Readonly<{
    readonly Manual: 0;
    readonly Again: 1;
    readonly Hard: 2;
    readonly Good: 3;
    readonly Easy: 4;
}>
Rating
.Good, now })
const
const restored: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
restored
=
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-6";
    readonly config: SRSSchema<{
        input: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
        output: {
            readonly weights: number[];
            readonly enableShortTerm: boolean;
            readonly numRelearningSteps: number;
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stability: number;
            difficulty: number;
        };
        output: {
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS6Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
.
SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-6"; readonly config: SRSSchema<{ input: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; output: { readonly weights: number[]; readonly enableShortTerm: boolean; readonly numRelearningSteps: number; }; }>; readonly memoryState: SRSSchema<{ input: { stability: number; difficulty: number; }; output: { stability: number; difficulty: number; }; }>; readonly algorithm: FSRS6Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.rollback: (input: {
    readonly card: Mutable<Omit<Mutable<Omit<Mutable<{
        stability: number;
        difficulty: number;
    } & {
        dueAt: Date;
        lastReviewAt: Date | null;
    } & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
        readonly cardId: string | number;
        readonly reps: number;
        readonly learningStep: number;
        readonly scheduledDays: number;
        readonly lapses: number;
    }, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>, keyof SchedulerCoreFields$1<...>> & SchedulerCoreFields$1<...>>;
    readonly revlog: Mutable<...>;
}) => {
    ...;
}
rollback
({
card: Mutable<Omit<Mutable<Omit<Mutable<{
    stability: number;
    difficulty: number;
} & {
    dueAt: Date;
    lastReviewAt: Date | null;
} & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
    readonly cardId: string | number;
    readonly reps: number;
    readonly learningStep: number;
    readonly scheduledDays: number;
    readonly lapses: number;
}, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>, keyof SchedulerCoreFields$1<...>> & SchedulerCoreFields$1<...>>
card
:
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
.
card: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
card
,
revlog: Mutable<Omit<Mutable<Omit<Mutable<{
    stability: number;
    difficulty: number;
} & {
    dueAt: Date;
    lastReviewAt: Date | null;
    reviewTime: Date;
} & SchedulerCoreFields$1<"new" | "learning" | "review"> & {
    readonly rating: Grade;
}> & {
    readonly cardId: string | number;
    readonly learningStep: number;
    readonly scheduledDays: number;
}, "rating" | keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review"> & {
    readonly rating: Grade;
}>, "rating" | keyof SchedulerCoreFields$1<...>> & SchedulerCoreFields$1<...> & {
    readonly rating: Grade;
}>
revlog
:
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
.
revlog: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    learningStep: number;
    scheduledDays: number;
    reviewTime: Date;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    rating: Grade;
}
revlog
,
}) console.log( JSON.stringify( { cardId:
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
.
card: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
card
.cardId,
rating:
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
.
revlog: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    learningStep: number;
    scheduledDays: number;
    reviewTime: Date;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    rating: Grade;
}
revlog
.rating,
reps:
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
.
card: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
card
.reps,
revlog: { lastReviewAt:
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
.
revlog: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    learningStep: number;
    scheduledDays: number;
    reviewTime: Date;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    rating: Grade;
}
revlog
.lastReviewAt,
dueAt:
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
.
revlog: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    learningStep: number;
    scheduledDays: number;
    reviewTime: Date;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    rating: Grade;
}
revlog
.dueAt,
reviewTime:
const result: {
    card: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        reps: number;
        learningStep: number;
        scheduledDays: number;
        lapses: number;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
    };
    revlog: {
        cardId: string | number;
        stability: number;
        difficulty: number;
        dueAt: Date;
        lastReviewAt: Date | null;
        learningStep: number;
        scheduledDays: number;
        reviewTime: Date;
        state: State;
        scheduleStatus: "new" | "learning" | "review";
        rating: Grade;
    };
}
result
.
revlog: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    learningStep: number;
    scheduledDays: number;
    reviewTime: Date;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    rating: Grade;
}
revlog
.reviewTime,
}, rollbackDueAt:
const restored: {
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
restored
.dueAt,
}, null, 2 ) )

ローカルの StoredV5Card 型はアプリケーションの保存形式を表し、legacy ライブラリからの import ではありません。データベースの既存の固定 ID を cardId に渡し、アプリケーション固有のフィールドは別途保持してください。合成されたカード schema が変換後の日付とフィールドを検証します。その他のアプリケーション固有のフィールドも、保存データを読み込む境界で検証してください。

設定の対応表

v5 の設定v6 の設定移行時の注意
wweightsこの段階ではユーザーの FSRS-6 の重みを維持します。サンプルは実行できるようにデフォルト値を使用します。
request_retentiondesiredRetention目標保持率の middleware が必要です。
maximum_intervalmaximumInterval最大間隔の middleware が必要です。単調間隔の方針もこの上限を使います。
enable_fuzzenableFuzzファジングの middleware が必要です。既存の設定を維持します。
enable_short_termenableShortTermFSRS-6 ではモデルの短期動作と明示的な学習ステップの両方に影響します。
learning_steps / relearning_stepslearningSteps / relearningSteps設定済みのステップ配列と学習進捗を維持します。
再学習ステップ数numRelearningStepsFSRS-6 では relearningSteps.length に設定します。
forget(..., reset_count)clearStatsOnForgetインスタンスの設定になりました。v5 のデフォルトは false、v6 の統計 middleware は true です。

カード、ログ、操作

v5 の保存フィールドv6 のフィールドまたは処理
dueカードの dueAt。Date にデシリアライズします。
elapsed_days / ログの last_elapsed_days削除されました。選択した chrono とタイムスタンプから経過日数を計算し、FSRS-7 のリプレイに古い丸め済みの値を使わないでください。
last_reviewlastReviewAt。未設定と null は null に統一します。
scheduled_daysmiddleware が提供する scheduledDays。
learning_stepsmiddleware が提供する learningStep。
stability、difficulty、state、reps、lapsesAPI 移行時に維持します。統計には middleware が必要です。
アプリケーションのレコード IDcardId。カードとログで同じ ID を使います。
対応するフィールドなしscheduleStatus:New → new、Learning/Relearning → learning、Review → review。
復習ログの reviewreviewTime。カードの期日ではなく実際の復習時刻です。
結果 { card, log }{ card, revlog }。

サンプルのようにスケジューラーから型を推論してください。カードとログのフィールドはモデル、chrono、middleware によって変わります。独自の v5 strategy は対応する middleware の hook に書き換える必要があります。名前の変更や旧スケジューラーの継承だけでは移行できません。

v5 の操作(比較用)v6 の操作
createEmptyCard(now)scheduler.newCard({ now, cardId })
f.next(card, now, grade)scheduler.review({ card, now, grade })
f.repeat(card, now)scheduler.preview({ card, now }) は Symbol.iterator で iterator を提供する iterable を返し、for...of で走査できます。各候補には { grade, card, revlog } が含まれます。評点をキーにしたオブジェクトへのアクセスではなく、candidate.grade で選択します。
f.rollback(card, log)返された v6 ログを使って scheduler.rollback({ card, revlog })。
f.forget(card, now, reset_count)scheduler.forget({ card, now })。{ card, log } ではなくカードを返します。手動操作はアプリケーションで記録します。
f.get_retrievability(card, now, false)lastReviewAt のある学習済みカードでは scheduler.model.forgettingCurve(card, scheduler.chrono.difference(card.lastReviewAt, now))。戻り値は数値です。新規カードは別途処理し、パーセント表示はアプリケーションで整形します。

review() は新しいデータを返します。result.card と result.revlog の両方を保存してください。新しいログは dueAt、lastReviewAt など、取り消しに必要な復習前のフィールドを保持します。旧ログの due を改名するだけで v6 の取り消しに使わないでください。旧ログには復習前のスナップショットが不足している場合があります。元の履歴は維持し、モデルのリプレイ用には評点と実際の復習時刻だけを変換します。バージョンをまたぐ取り消しには、アプリケーションの独立したスナップショットか取り消し境界が必要です。

モデルと設定が同じでも、すべての学習ステップやファジングで v5 と同一の間隔になる保証はありません。書き込みを切り替える前に New、Learning、Review、Relearning の代表カードを検証してください。DefaultScheduler も利用できますが、デフォルトモデルは FSRS-7 です。この段階で使う場合は FSRS-6 を明示してください。

ログの 3 つの時刻は単なる改名ではない

dateChrono のフィールド名は reviewTime であり、reviewAt ではありません。前回の復習が 1 月 1 日、元の期日が 1 月 3 日、実際の復習が 1 月 4 日という延滞の例を考えます。

意味v5 の通常の復習ログv6 の revlog
前回の復習時刻:1 月 1 日due(旧実装は card.last_review || card.due を記録)lastReviewAt
元の期日:1 月 3 日この学習済みカードでは独立した記録なしdueAt
今回の実際の復習時刻:1 月 4 日reviewreviewTime

lastReviewAt は今回の復習より前の、前回の復習時刻です。入力と出力で型が異なります。

  • 入力の DateRevlogInputFields では lastReviewAt?: Date | null と省略可能です。省略または undefined の場合、schema は null に正規化します。dueAt と reviewTime は有効な Date が必須です。
  • 出力の DateRevlogFields には lastReviewAt: Date | null が必ず存在します。通常の学習済みカードのログには復習前の card.lastReviewAt を記録し、前回の復習がない新規カードの初回ログでは null になります。
  • null から欠落した前回復習時刻を復元することはできません。旧ログにその情報がなければ、schema の検証に通っても正確な取り消しは保証されません。dueAt や reviewTime で代用しないでください。

復習が返すカードの lastReviewAt は 1 月 4 日になり、dueAt は新たな期日になります。一方、ログは復習前の時刻を保持します。そのカードと v6 ログで取り消すと、dueAt は 1 月 3 日、lastReviewAt は 1 月 1 日に戻ります。前倒しや延滞でも同様です。経過日数の計算に使う時刻と、取り消しのために保存する期日を分離したのが chrono リファクタリングの理由です。上のサンプルは 3 フィールドを出力します。

一律に oldLog.due → revlog.dueAt と変換してはいけません。通常の v5 学習済みカードの due は前回の復習時刻です。last_review のない初回復習では元の期日になり、Manual/忘却のログには別の意味があります。リセット済みの v5 New カードにも古い last_review が残る場合があるため、カード変換サンプルでは State.New の lastReviewAt を null にします。

モデルの記憶だけを再構築する場合は、rating と review → reviewTime の変換で十分です。バージョンをまたいで正確に取り消すには、両方の時刻と middleware フィールドを含む元の復習前カードのスナップショットが必要です。それがなければ取り消し境界を設けてください。前倒し復習、学習ステップ、ファジング、手動調整があると、review や再計算した間隔から未記録の期日を復元することはできません。

2. FSRS-6 から FSRS-7 に移行する

重みと状態はモデル固有

FSRS-6 は 21 個、FSRS-7 は独立した 34 個の重みを使用します。FSRS7_DEFAULT_WEIGHTS を使うか、FSRS-7 の重みを再学習してください。FSRS-6 のベクトルに値を追加したり、その学習済み重みを流用したりしないでください。migrateFSRS7Parameters は空の入力にデフォルト値を提供しますが、旧モデルのパラメーター配置を変換するものではありません。

FSRS-7 の記憶状態は stability、stabilityFast、difficulty です。3 つとも履歴から再構築してください。再構築した結果は現在の card の記憶状態(memoryState)だけに適用します。過去の revlog は読み取り専用の入力として扱い、記録済みの記憶状態を含め、データの変更・上書き・後付けをしてはいけません。

学習済みカードに stabilityFast = 0.8 * stability を設定してはいけません。FSRS-6 の安定性と難易度を維持したまま高速成分だけを追加する方法も使えません。モデル初期化時の係数は、更新を重ねた記憶状態の移行式ではありません。

fractionalDays: true にして、10 分間隔の復習などを小数日数のまま保持してください。FSRS-7 の高速トレースはモデル固有です。学習ステップの middleware を使用した場合、enableShortTerm: false が無効にするのは明示的な学習ステップだけで、高速トレースは無効になりません。FSRS-7 モデルには FSRS-6 の numRelearningSteps に対応する設定はありません。

FSRS-7 のデフォルトステップと学習状態

DefaultScheduler は FSRS-7 でも FSRS-3~6 と同じ学習ステップ ['1m', '10m'] と再学習ステップ ['10m'] をデフォルトとし、allowModelOverride を有効にして、より長いモデル間隔で現在のステップを上書きします。明示した設定はデフォルト値で上書きされません。enableShortTerm は引き続き true がデフォルトで、明示的なステップを制御します。FSRS-7 の高速トレースや間隔による状態分類は無効にしません。

schedulerLearningStepsMiddleware が登録されている場合、ステップが無効・空・完了済みでも、最終スケジュール間隔で状態が決まります。

復習前の状態最終間隔が graduatingInterval(デフォルト 1 日)未満最終間隔が graduatingInterval 以上
New / LearningLearning、scheduleStatus: learningReview、scheduleStatus: review
Review / RelearningRelearning、scheduleStatus: learningReview、scheduleStatus: review

ステップを設定している場合、適用されるステップがない Hard、Good、Easy は少なくとも graduatingInterval 後に予定されます。そのため、ステップを終えたときや、Review のカードに Hard、Good、Easy を付けたときは常に Review になります。allowModelOverride が有効な場合、モデルの間隔を使う Again は、間隔が graduatingInterval に制限されても常に Learning/Relearning になります。

ステップを設定していない場合、Learning がなくなる、Again だけが Relearning を生む、Easy は必ず Review になる、という前提は使えません。一般に、Easy は明示的なステップをスキップし、Good は適用可能な次のステップがある場合のみ進み、長さ 0 のステップはステップなしとして扱います。learningStep はカードが Learning/Relearning に留まる間だけ次のステップを保持し、カードが Review に移ると 0 に戻ります。これはステップ番号であり、復習回数ではありません。

状態遷移は middleware の責務で、FSRS7Model の責務ではありません。モデルと chrono だけのスケジューラーは、間隔が 1 日未満でも Review を返します。旧動作を前提としたキュー、状態フィルター、統計の処理も更新してください。

「記憶をリプレイし、スケジュールを維持する」の完全な移行サンプルは、デフォルトに頼らず既存の学習ステップを明示して維持します。製品として完全にモデル主導のステップを選ぶ場合は learningSteps: [] と relearningSteps: [] を明示してください。学習ステップ middleware を削除するという意味ではありません。方針の変更はカードフィールドの移行と分けて検証してください。

想起確率の呼び出しを移行する

scheduler.model.forgettingCurve(state, elapsedDays) で数値の想起確率を取得し、scheduler.model.algorithm.curve(elapsedDays, state) で両成分を確認できます。どちらも stability、stabilityFast、difficulty を含む FSRS-7 の完全な状態が必要で、FSRS-6 の安定性 1 値だけは渡せません。

または ts-fsrs/models/fsrs-7 から独立した curve を import し、curve(weights, elapsedDays, state) を呼び出せます。アルゴリズムのインスタンスは不要です。有効な 34 個の FSRS-7 の重みを渡してください。この関数は重みを検証しません。保存データやユーザー入力は事前に schema・パラメーターチェックで検証し、curve の呼び出しを妥当性チェックに使わないでください。再構築した記憶状態と同じ FSRS-7 の重みを使い、elapsedDays の小数部分を保持してください。

曲線の戻り値は retrievability、derivative、fast、slow、fastWeight、slowWeight、total を含みます。total = fastWeight + slowWeight は混合係数の和であり、全体の想起確率ではありません。旧確率呼び出しの代わりには retrievability を使い、パーセント表示はアプリケーションで整形してください。アルゴリズムインスタンスの曲線結果にも total が含まれます。これらの API は確率と成分の計算用であり、カード移行、ログ生成、期日の設定は行いません。

記憶をリプレイし、スケジュールを維持する

以下は単独で実行できます。v6 形式に変換済みの FSRS-6 カードを受け取り、保存ログの review を reviewTime に変換して Reschedule.replay() を呼びます。旧モデルの initialState は渡しません。移行時は記憶状態だけを再構築し、既存カードの次回復習日時(dueAt)は変更しません。そのカードを次に復習した時点で、FSRS-7 が新しい次回復習日時を計算します。ID、最終復習時刻、学習進捗、統計も維持されます。

import { 
const dateChrono: Chrono<{
    readonly time: SRSSchema<{
        input: Date;
        output: Date;
    }>;
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly fields: {
        readonly card: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
            };
        }>;
        readonly revlog: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
                reviewTime: Date;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
                reviewTime: Date;
            };
        }>;
    };
}>
dateChrono
,
function defineScheduler<const M extends AnyModel, const C extends AnyChrono>(definition: {
    readonly model: M;
    readonly chrono: C;
}): CustomScheduler<SchedulerNameOf<M>, SchedulerEnvFor<M, C, readonly []>, M, C>
defineScheduler
,
type Rating = 0 | 1 | 2 | 3 | 4
const Rating: Readonly<{
    readonly Manual: 0;
    readonly Again: 1;
    readonly Hard: 2;
    readonly Good: 3;
    readonly Easy: 4;
}>
Rating
,
type State = 0 | 1 | 2 | 3
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
} from 'ts-fsrs'
import {
const schedulerDesiredRetentionMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly desiredRetention: number | Record<Grade, number>;
        };
        output: {
            readonly desiredRetention: Record<Grade, number>;
        };
    }>;
}>
schedulerDesiredRetentionMiddleware
,
const schedulerFuzzingMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays?: boolean;
        };
        output: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays: boolean;
        };
    }>;
    readonly cardInitInput: SRSSchema<{
        input: {
            readonly cardId?: string | number;
        };
        output: {
            readonly cardId?: string | number;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly cardId: string | number;
            readonly reps: number;
        };
        output: {
            readonly cardId: string | number;
            readonly reps: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly cardId: string | number;
        };
        output: {
            readonly cardId: string | number;
        };
    }>;
}>
schedulerFuzzingMiddleware
,
const schedulerLearningStepsMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly learningSteps: readonly StepUnit[];
            readonly relearningSteps: readonly StepUnit[];
            readonly enableShortTerm: boolean;
            readonly graduatingInterval?: number;
            readonly allowModelOverride?: boolean;
        };
        output: {
            readonly learningSteps: readonly StepUnit[];
            readonly relearningSteps: readonly StepUnit[];
            readonly enableShortTerm: boolean;
            readonly graduatingInterval: number;
            readonly allowModelOverride: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly learningStep?: number;
        };
        output: {
            readonly learningStep: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly learningStep?: number;
        };
        output: {
            readonly learningStep: number;
        };
    }>;
}>
schedulerLearningStepsMiddleware
,
const schedulerMaximumIntervalMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly maximumInterval?: number;
        };
        output: {
            readonly maximumInterval: number;
        };
    }>;
}>

Register after learning steps so explicit steps can keep their exact delay.

schedulerMaximumIntervalMiddleware
,
const schedulerMonotonicIntervalMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly maximumInterval?: number;
        };
        output: {
            readonly maximumInterval: number;
        };
    }>;
}>

Register after fuzzing and learning steps, when present.

schedulerMonotonicIntervalMiddleware
,
const schedulerScheduledDaysMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly scheduledDays: number;
        };
        output: {
            readonly scheduledDays: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly scheduledDays: number;
        };
        output: {
            readonly scheduledDays: number;
        };
    }>;
}>

Register before interval middleware so it records their final value on unwind.

schedulerScheduledDaysMiddleware
,
const schedulerStatsMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly clearStatsOnForget?: boolean | undefined;
        };
        output: {
            readonly clearStatsOnForget: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly reps: number;
            readonly lapses: number;
        };
        output: {
            readonly reps: number;
            readonly lapses: number;
        };
    }>;
}>
schedulerStatsMiddleware
,
} from 'ts-fsrs/middlewares' import {
const FSRS7_DEFAULT_WEIGHTS: number[]
FSRS7_DEFAULT_WEIGHTS
,
const FSRS7Model: Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>
FSRS7Model
} from 'ts-fsrs/models/fsrs-7'
import {
class Reschedule<Scheduler extends AnySchedulerCore>

Replays a review history on top of an existing srs-kit scheduler core.

The scheduler instance keeps the model and chronology coupled, allowing the review-time, card and memory-state types to be inferred from the selected core.

Reschedule
} from 'ts-fsrs/reschedule'
export const
const definition: CustomScheduler<"fsrs-7", ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>
definition
=
defineScheduler<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>>(definition: {
    ...;
}): CustomScheduler<...>
defineScheduler
({
model: Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>
model
:
const FSRS7Model: Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>
FSRS7Model
,
chrono: Chrono<{
    readonly time: SRSSchema<{
        input: Date;
        output: Date;
    }>;
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly fields: {
        readonly card: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
            };
        }>;
        readonly revlog: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
                reviewTime: Date;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
                reviewTime: Date;
            };
        }>;
    };
}>
chrono
:
const dateChrono: Chrono<{
    readonly time: SRSSchema<{
        input: Date;
        output: Date;
    }>;
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly fields: {
        readonly card: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
            };
        }>;
        readonly revlog: SRSSchema<{
            input: {
                dueAt: Date;
                lastReviewAt?: (Date | null) | undefined;
                reviewTime: Date;
            };
            output: {
                dueAt: Date;
                lastReviewAt: Date | null;
                reviewTime: Date;
            };
        }>;
    };
}>
dateChrono
,
}).
CustomScheduler<"fsrs-7", SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, Model<...>, Chrono<...>>.use: <readonly [Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly desiredRetention: number | Record<Grade, number>;
        };
        output: {
            readonly desiredRetention: Record<Grade, number>;
        };
    }>;
}>, Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays?: boolean;
        };
        output: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays: boolean;
        };
    }>;
    readonly cardInitInput: SRSSchema<{
        input: {
            readonly cardId?: string | number;
        };
        output: {
            readonly cardId?: string | number;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly cardId: string | number;
            readonly reps: number;
        };
        output: {
            readonly cardId: string | number;
            readonly reps: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly cardId: string | number;
        };
        output: {
            readonly cardId: string | number;
        };
    }>;
}>, ... 4 more ..., Middleware<...>]>(middlewares_0: Middleware<...>, middlewares_1: Middleware<...>, middlewares_2: Middleware<...>, middlewares_3: Middleware<...>, middlewares_4: Middleware<...>, middlewares_5: Middleware<...>, middlewares_6: Middleware<...>) => CustomScheduler<...>

Returns a new scheduler definition extended with the supplied middleware. The current scheduler and any cores created from it remain unchanged.

use
(
const schedulerDesiredRetentionMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly desiredRetention: number | Record<Grade, number>;
        };
        output: {
            readonly desiredRetention: Record<Grade, number>;
        };
    }>;
}>
schedulerDesiredRetentionMiddleware
,
const schedulerFuzzingMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays?: boolean;
        };
        output: {
            readonly enableFuzz: boolean;
            readonly maximumInterval: number;
            readonly fractionalDays: boolean;
        };
    }>;
    readonly cardInitInput: SRSSchema<{
        input: {
            readonly cardId?: string | number;
        };
        output: {
            readonly cardId?: string | number;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly cardId: string | number;
            readonly reps: number;
        };
        output: {
            readonly cardId: string | number;
            readonly reps: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly cardId: string | number;
        };
        output: {
            readonly cardId: string | number;
        };
    }>;
}>
schedulerFuzzingMiddleware
,
const schedulerStatsMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly clearStatsOnForget?: boolean | undefined;
        };
        output: {
            readonly clearStatsOnForget: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly reps: number;
            readonly lapses: number;
        };
        output: {
            readonly reps: number;
            readonly lapses: number;
        };
    }>;
}>
schedulerStatsMiddleware
,
const schedulerScheduledDaysMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly fractionalDays?: boolean | undefined;
        };
        output: {
            readonly fractionalDays: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly scheduledDays: number;
        };
        output: {
            readonly scheduledDays: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly scheduledDays: number;
        };
        output: {
            readonly scheduledDays: number;
        };
    }>;
}>

Register before interval middleware so it records their final value on unwind.

schedulerScheduledDaysMiddleware
,
const schedulerLearningStepsMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly learningSteps: readonly StepUnit[];
            readonly relearningSteps: readonly StepUnit[];
            readonly enableShortTerm: boolean;
            readonly graduatingInterval?: number;
            readonly allowModelOverride?: boolean;
        };
        output: {
            readonly learningSteps: readonly StepUnit[];
            readonly relearningSteps: readonly StepUnit[];
            readonly enableShortTerm: boolean;
            readonly graduatingInterval: number;
            readonly allowModelOverride: boolean;
        };
    }>;
    readonly card: SRSSchema<{
        input: {
            readonly learningStep?: number;
        };
        output: {
            readonly learningStep: number;
        };
    }>;
    readonly revlog: SRSSchema<{
        input: {
            readonly learningStep?: number;
        };
        output: {
            readonly learningStep: number;
        };
    }>;
}>
schedulerLearningStepsMiddleware
,
const schedulerMaximumIntervalMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly maximumInterval?: number;
        };
        output: {
            readonly maximumInterval: number;
        };
    }>;
}>

Register after learning steps so explicit steps can keep their exact delay.

schedulerMaximumIntervalMiddleware
,
const schedulerMonotonicIntervalMiddleware: Middleware<symbol, {
    readonly config: SRSSchema<{
        input: {
            readonly maximumInterval?: number;
        };
        output: {
            readonly maximumInterval: number;
        };
    }>;
}>

Register after fuzzing and learning steps, when present.

schedulerMonotonicIntervalMiddleware
) export const
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
=
const definition: CustomScheduler<"fsrs-7", ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>
definition
.
CustomScheduler<"fsrs-7", ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>.create: (ctx: {
    readonly config: Omit<SchedulerConfigInput<Model<{
        readonly name: "fsrs-7";
        readonly config: SRSSchema<{
            input: {
                readonly weights: readonly number[];
            };
            output: {
                readonly weights: readonly number[];
            };
        }>;
        readonly memoryState: SRSSchema<{
            input: {
                stabilityFast: number;
                stability: number;
                difficulty: number;
            };
            output: {
                stabilityFast: number;
                stability: number;
                difficulty: number;
            };
        }>;
        readonly algorithm: FSRS7Algorithm;
    }>, Chrono<...>, readonly []>, "fractionalDays" | ... 8 more ... | "clearStatsOnForget"> & {
        ...;
    };
}) => SchedulerCore<...>
create
({
config: Omit<SchedulerConfigInput<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, "fractionalDays" | ... 8 more ... | "clearStatsOnForget"> & {
    ...;
}
config
: {
weights:
const FSRS7_DEFAULT_WEIGHTS: number[]
FSRS7_DEFAULT_WEIGHTS
,
fractionalDays: true, enableShortTerm: true, // Controls learning steps, not the model's fast trace. desiredRetention: 0.9, // Preserve existing steps explicitly instead of inheriting preset defaults. learningSteps: ['1m', '10m'], relearningSteps: ['10m'], enableFuzz: false, maximumInterval: 36500, clearStatsOnForget: false, }, }) type
type Card = {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
Card
= ReturnType<typeof
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
.
SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.newCard: (options?: (Omit<{
    readonly now?: Date | undefined;
} & {
    readonly cardId?: string | number | undefined;
}, "now"> & {
    readonly now?: Date | undefined;
}) | undefined) => {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
newCard
>
type
type PreviousCard = {
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
}
PreviousCard
= Omit<
type Card = {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
Card
, 'stabilityFast'>
type
type StoredReview = {
    rating: Rating;
    review: string;
}
StoredReview
= { rating:
type Rating = 0 | 1 | 2 | 3 | 4
Rating
; review: string }
// completeHistory means all reviews since creation or the latest memory reset. // Decide this from application records, not from the number of rows alone. export function
function migrateMemory(card: PreviousCard, logs: readonly StoredReview[], completeHistory: boolean): Card
migrateMemory
(
card:
type PreviousCard = {
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
}
PreviousCard
,
logs: readonly
type StoredReview = {
    rating: Rating;
    review: string;
}
StoredReview
[],
completeHistory: boolean ):
type Card = {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
Card
{
if (card.state ===
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
.New) {
const { stability, stabilityFast, difficulty } =
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
.
SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>.newCard: (options?: (Omit<{
    readonly now?: Date | undefined;
} & {
    readonly cardId?: string | number | undefined;
}, "now"> & {
    readonly now?: Date | undefined;
}) | undefined) => {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
newCard
({
cardId: card.cardId, }) return
const definition: CustomScheduler<"fsrs-7", ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>
definition
.
CustomScheduler<"fsrs-7", ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>.schema: SchedulerSchema<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>
schema
.
SchedulerSchema<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>.card: SRSSchema<{
    input: Readonly<Omit<Readonly<{
        stabilityFast: number;
        stability: number;
        difficulty: number;
    } & {
        dueAt: Date;
        lastReviewAt: Date | null;
    } & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
        readonly cardId: string | number;
        readonly reps: number;
        readonly learningStep?: number | undefined;
        readonly scheduledDays: number;
        readonly lapses: number;
    }, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>;
    output: Mutable<...>;
}>
card
.
SRSSchema<{ input: Readonly<Omit<Readonly<{ stabilityFast: number; stability: number; difficulty: number; } & { dueAt: Date; lastReviewAt: Date | null; } & SchedulerCoreFields$1<"new" | "learning" | "review">> & { ...; }, keyof SchedulerCoreFields$1<...>> & SchedulerCoreFields$1<...>>; output: Mutable<...>; }>.parse(input: unknown): Mutable<Omit<Mutable<{
    stabilityFast: number;
    stability: number;
    difficulty: number;
} & {
    dueAt: Date;
    lastReviewAt: Date | null;
} & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
    readonly cardId: string | number;
    readonly reps: number;
    readonly learningStep: number;
    readonly scheduledDays: number;
    readonly lapses: number;
}, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>
parse
({
...card, stability, stabilityFast, difficulty, }) } if (!completeHistory) throw new Error('Keep this card on FSRS-6: incomplete history') const history = logs .filter(({ rating }) => rating !==
const Rating: Readonly<{
    readonly Manual: 0;
    readonly Again: 1;
    readonly Hard: 2;
    readonly Good: 3;
    readonly Easy: 4;
}>
Rating
.Manual)
.map(({ rating, review }) => ({ rating, reviewTime: new Date(review), })) // replay validates dates/ratings and sorts the non-manual history. // Empty or Manual-only histories throw instead of clearing memory. const { memoryState } = new
new Reschedule<SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>>(scheduler: SchedulerCore<...>): Reschedule<...>

Replays a review history on top of an existing srs-kit scheduler core.

The scheduler instance keeps the model and chronology coupled, allowing the review-time, card and memory-state types to be inferred from the selected core.

Reschedule
(
const scheduler: SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>
scheduler
).
Reschedule<SchedulerCore<SchedulerCoreEnv<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>, ModelCore<...>, ChronoCore<...>>>.replay(input: ReplayInput<{
    stabilityFast: number;
    stability: number;
    difficulty: number;
}, Date>, options?: RescheduleOptions): ReplayResult<{
    stabilityFast: number;
    stability: number;
    difficulty: number;
}>

Rebuilds model memory from the review history and returns the memory state after each review.

replay
({
ReplayInput<{ stabilityFast: number; stability: number; difficulty: number; }, Date>.history: readonly RescheduleReview<Date>[]
history
})
const latestReview = history.reduce( (latest, { reviewTime }) => Math.max(latest, reviewTime.getTime()), -Infinity ) if (card.lastReviewAt?.getTime() !== latestReview) { throw new Error( 'Keep this card on FSRS-6: history does not match lastReviewAt' ) } // Only the model memory changes; dueAt, counters and learning progress survive. return
const definition: CustomScheduler<"fsrs-7", ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>
definition
.
CustomScheduler<"fsrs-7", ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>, Model<...>, Chrono<...>>.schema: SchedulerSchema<ExtendSchedulerEnv<SchedulerEnvFor<Model<{
    readonly name: "fsrs-7";
    readonly config: SRSSchema<{
        input: {
            readonly weights: readonly number[];
        };
        output: {
            readonly weights: readonly number[];
        };
    }>;
    readonly memoryState: SRSSchema<{
        input: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
        output: {
            stabilityFast: number;
            stability: number;
            difficulty: number;
        };
    }>;
    readonly algorithm: FSRS7Algorithm;
}>, Chrono<...>, readonly []>, readonly [...]>>
schema
.
SchedulerSchema<ExtendSchedulerEnv<SchedulerEnvFor<Model<{ readonly name: "fsrs-7"; readonly config: SRSSchema<{ input: { readonly weights: readonly number[]; }; output: { readonly weights: readonly number[]; }; }>; readonly memoryState: SRSSchema<{ input: { stabilityFast: number; stability: number; difficulty: number; }; output: { stabilityFast: number; stability: number; difficulty: number; }; }>; readonly algorithm: FSRS7Algorithm; }>, Chrono<...>, readonly []>, readonly [...]>>.card: SRSSchema<{
    input: Readonly<Omit<Readonly<{
        stabilityFast: number;
        stability: number;
        difficulty: number;
    } & {
        dueAt: Date;
        lastReviewAt: Date | null;
    } & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
        readonly cardId: string | number;
        readonly reps: number;
        readonly learningStep?: number | undefined;
        readonly scheduledDays: number;
        readonly lapses: number;
    }, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>;
    output: Mutable<...>;
}>
card
.
SRSSchema<{ input: Readonly<Omit<Readonly<{ stabilityFast: number; stability: number; difficulty: number; } & { dueAt: Date; lastReviewAt: Date | null; } & SchedulerCoreFields$1<"new" | "learning" | "review">> & { ...; }, keyof SchedulerCoreFields$1<...>> & SchedulerCoreFields$1<...>>; output: Mutable<...>; }>.parse(input: unknown): Mutable<Omit<Mutable<{
    stabilityFast: number;
    stability: number;
    difficulty: number;
} & {
    dueAt: Date;
    lastReviewAt: Date | null;
} & SchedulerCoreFields$1<"new" | "learning" | "review">> & {
    readonly cardId: string | number;
    readonly reps: number;
    readonly learningStep: number;
    readonly scheduledDays: number;
    readonly lapses: number;
}, keyof SchedulerCoreFields$1<"new" | "learning" | "review">> & SchedulerCoreFields$1<"new" | "learning" | "review">>
parse
({ ...card, ...memoryState })
} export const previousCard:
type PreviousCard = {
    state: State;
    scheduleStatus: "new" | "learning" | "review";
    cardId: string | number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
}
PreviousCard
= {
cardId: 'card-1', dueAt: new Date('2026-01-05T00:00:00.000Z'), lastReviewAt: new Date('2026-01-02T00:00:00.000Z'), stability: 2, difficulty: 5, state:
const State: Readonly<{
    readonly New: 0;
    readonly Learning: 1;
    readonly Review: 2;
    readonly Relearning: 3;
}>
State
.Review,
scheduleStatus: 'review', scheduledDays: 3, learningStep: 0, reps: 3, lapses: 0, } export const storedHistory:
type StoredReview = {
    rating: Rating;
    review: string;
}
StoredReview
[] = [
{ rating:
const Rating: Readonly<{
    readonly Manual: 0;
    readonly Again: 1;
    readonly Hard: 2;
    readonly Good: 3;
    readonly Easy: 4;
}>
Rating
.Good, review: '2026-01-01T00:00:00.000Z' },
{ rating:
const Rating: Readonly<{
    readonly Manual: 0;
    readonly Again: 1;
    readonly Hard: 2;
    readonly Good: 3;
    readonly Easy: 4;
}>
Rating
.Good, review: '2026-01-01T00:10:00.000Z' },
{ rating:
const Rating: Readonly<{
    readonly Manual: 0;
    readonly Again: 1;
    readonly Hard: 2;
    readonly Good: 3;
    readonly Easy: 4;
}>
Rating
.Good, review: '2026-01-02T00:00:00.000Z' },
] const
const migrated: {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
migrated
=
function migrateMemory(card: PreviousCard, logs: readonly StoredReview[], completeHistory: boolean): Card
migrateMemory
(previousCard, storedHistory, true)
console.log( JSON.stringify( { model: 'FSRS-7', cardId:
const migrated: {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
migrated
.cardId,
dueAt:
const migrated: {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
migrated
.dueAt,
reps:
const migrated: {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
migrated
.reps,
stabilityFast:
const migrated: {
    cardId: string | number;
    stabilityFast: number;
    stability: number;
    difficulty: number;
    dueAt: Date;
    lastReviewAt: Date | null;
    reps: number;
    learningStep: number;
    scheduledDays: number;
    lapses: number;
    state: State;
    scheduleStatus: "new" | "learning" | "review";
}
migrated
.stabilityFast,
}, null, 2 ) )
カード・履歴の状態処理
新規カード(State.New)。意図的にリセットしたカードも含むFSRS-7 のデフォルト記憶状態を設定し、スケジュールと統計を維持します。
作成時または最新の記憶リセット以降の完全な履歴がある学習済みカードFSRS-7 の重みでリプレイし、スケジュールを維持して再構築した状態を保存します。
履歴の欠落・切り詰めがあるカードと対応設定を FSRS-6 に残します。暗黙に初期化したり、行数だけで完全性を判断したりしません。
学習済みカードの履歴が空、または Manual のみリプレイは例外を投げます。カードは FSRS-6 に残します。
不正な日付・評点、または最後の復習が lastReviewAt と一致しないそのカードの移行を止め、記録を確認します。

replay() は有効な復習を時刻順に並べ、Rating.Manual を除外します。ただし手動の忘却・リセットイベントは解釈しません。アプリケーションが最新の記憶リセット以降の完全な履歴を選び、それ以前の復習を除外する必要があります。期日を手動調整しただけなら記憶リセットではありません。同時刻の復習は記録順を維持してください。dateChrono ではデシリアライズ後の時刻は有効な Date である必要があります。最初の復習は未初期化の記憶状態から、経過日数ゼロで開始します。

サンプルの履歴完全性フラグはアプリケーションで判断します。最後の時刻が一致していても、それ以前の復習が欠落していない証明にはなりません。移行できないカードは保存されたモデルバージョンに応じて FSRS-6 に振り分け、FSRS-7 に渡さないでください。

意図的に期日も再計算する場合

new Reschedule(scheduler).reschedule({ history, initialCard }) はスケジュール方針全体を通してリプレイし、{ card, results } を返します。期日、学習進捗、統計が変わる場合があります。initialCard には、既存の cardId と最初の復習時刻を指定した FSRS-7 の新規カードを使ってください。学習済みカードに完全な履歴を重ねてリプレイしてはいけません。生成されるログは再計算結果であり、元の履歴の置き換えではありません。このガイドでは replay() を標準とし、移行時に既存カードの次回復習日時を変更しません。

保存と検証

  1. カード、元のログ、設定をバックアップします。モデルバージョンと実際にリプレイに使った重みを、カードまたは確実に関連付けられた設定に保存します。
  2. stabilityFast と v6 フィールドを保存できるようにし、再構築した記憶、モデルバージョン、対応する重み・設定の参照を一緒に更新します。FSRS-6 の状態と FSRS-7 の重みを混在させず、アプリケーションの ID とメタデータを維持します。
  3. 元のログを保持し、取り消しの境界を設けます。旧モデルのログを FSRS-7 の rollback() に直接渡してはいけません。新しい FSRS-7 ログには stabilityFast と復習前のフィールドを保存します。
  4. 代表カードで復習、プレビュー、取り消し、同日復習、学習ステップ、設定したファジングを検証します。記憶だけの移行では期日、ID、学習進捗、統計が変わらないことを確認し、失敗を解消してから該当カードのモデルを切り替えます。

モデルの詳細は FSRS-7、API の構成はスケジューラー定義、Chrono、middlewareを参照してください。