@vue-skuilder/db 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{contentSource-Cplhv3bJ.d.ts → contentSource-C-0t0y0V.d.ts} +7 -0
- package/dist/{contentSource-kI9_jwTu.d.cts → contentSource-jSkcOt2s.d.cts} +7 -0
- package/dist/core/index.d.cts +29 -4
- package/dist/core/index.d.ts +29 -4
- package/dist/core/index.js +132 -39
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +130 -39
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-DrBqOUa3.d.ts → dataLayerProvider-BB0oi9T0.d.ts} +1 -1
- package/dist/{dataLayerProvider-CiA2Rr0v.d.cts → dataLayerProvider-BDClIrFC.d.cts} +1 -1
- package/dist/impl/couch/index.d.cts +2 -2
- package/dist/impl/couch/index.d.ts +2 -2
- package/dist/impl/couch/index.js +128 -39
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +128 -39
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.cts +2 -2
- package/dist/impl/static/index.d.ts +2 -2
- package/dist/impl/static/index.js +128 -39
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +128 -39
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/index.d.cts +115 -81
- package/dist/index.d.ts +115 -81
- package/dist/index.js +371 -251
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +369 -251
- package/dist/index.mjs.map +1 -1
- package/docs/navigators-architecture.md +29 -13
- package/package.json +3 -3
- package/src/core/interfaces/contentSource.ts +7 -0
- package/src/core/navigators/SrsDebugger.ts +53 -0
- package/src/core/navigators/generators/prescribed.ts +76 -9
- package/src/core/navigators/generators/srs.ts +81 -37
- package/src/core/navigators/index.ts +5 -0
- package/src/study/SessionController.ts +260 -249
- package/src/study/SessionDebugger.ts +15 -25
- package/src/study/SessionOverlay.ts +108 -13
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { U as UserDBInterface, C as CourseDBInterface, b as CoursesDBInterface, c as ClassroomDBInterface, A as AdminDBInterface, a as UserDBReader, d as CourseInfo, S as StudySessionItem, D as DataLayerResult, e as ContentNavigationStrategyData, f as ContentNavigator, R as ReplanHints, G as GeneratorResult } from '../../contentSource-
|
|
2
|
-
import { D as DataLayerProvider } from '../../dataLayerProvider-
|
|
1
|
+
import { U as UserDBInterface, C as CourseDBInterface, b as CoursesDBInterface, c as ClassroomDBInterface, A as AdminDBInterface, a as UserDBReader, d as CourseInfo, S as StudySessionItem, D as DataLayerResult, e as ContentNavigationStrategyData, f as ContentNavigator, R as ReplanHints, G as GeneratorResult } from '../../contentSource-jSkcOt2s.cjs';
|
|
2
|
+
import { D as DataLayerProvider } from '../../dataLayerProvider-BDClIrFC.cjs';
|
|
3
3
|
import { S as StaticCourseManifest } from '../../types-BFUa1pa3.cjs';
|
|
4
4
|
import { CourseConfig, CourseElo, DataShape } from '@vue-skuilder/common';
|
|
5
5
|
import { S as SkuilderCourseData, Q as QualifiedCardID, T as TagStub, a as Tag } from '../../types-legacy-4tlwHnXo.cjs';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { U as UserDBInterface, C as CourseDBInterface, b as CoursesDBInterface, c as ClassroomDBInterface, A as AdminDBInterface, a as UserDBReader, d as CourseInfo, S as StudySessionItem, D as DataLayerResult, e as ContentNavigationStrategyData, f as ContentNavigator, R as ReplanHints, G as GeneratorResult } from '../../contentSource-
|
|
2
|
-
import { D as DataLayerProvider } from '../../dataLayerProvider-
|
|
1
|
+
import { U as UserDBInterface, C as CourseDBInterface, b as CoursesDBInterface, c as ClassroomDBInterface, A as AdminDBInterface, a as UserDBReader, d as CourseInfo, S as StudySessionItem, D as DataLayerResult, e as ContentNavigationStrategyData, f as ContentNavigator, R as ReplanHints, G as GeneratorResult } from '../../contentSource-C-0t0y0V.js';
|
|
2
|
+
import { D as DataLayerProvider } from '../../dataLayerProvider-BB0oi9T0.js';
|
|
3
3
|
import { S as StaticCourseManifest } from '../../types-CHgpWQAY.js';
|
|
4
4
|
import { CourseConfig, CourseElo, DataShape } from '@vue-skuilder/common';
|
|
5
5
|
import { S as SkuilderCourseData, Q as QualifiedCardID, T as TagStub, a as Tag } from '../../types-legacy-4tlwHnXo.js';
|
|
@@ -1443,6 +1443,30 @@ Example:
|
|
|
1443
1443
|
}
|
|
1444
1444
|
});
|
|
1445
1445
|
|
|
1446
|
+
// src/core/navigators/SrsDebugger.ts
|
|
1447
|
+
var SrsDebugger_exports = {};
|
|
1448
|
+
__export(SrsDebugger_exports, {
|
|
1449
|
+
captureSrsBacklog: () => captureSrsBacklog,
|
|
1450
|
+
clearSrsBacklogDebug: () => clearSrsBacklogDebug,
|
|
1451
|
+
getSrsBacklogDebug: () => getSrsBacklogDebug
|
|
1452
|
+
});
|
|
1453
|
+
function captureSrsBacklog(snapshot) {
|
|
1454
|
+
snapshots.set(snapshot.courseId, snapshot);
|
|
1455
|
+
}
|
|
1456
|
+
function getSrsBacklogDebug() {
|
|
1457
|
+
return [...snapshots.values()].sort((a, b) => b.timestamp - a.timestamp);
|
|
1458
|
+
}
|
|
1459
|
+
function clearSrsBacklogDebug() {
|
|
1460
|
+
snapshots.clear();
|
|
1461
|
+
}
|
|
1462
|
+
var snapshots;
|
|
1463
|
+
var init_SrsDebugger = __esm({
|
|
1464
|
+
"src/core/navigators/SrsDebugger.ts"() {
|
|
1465
|
+
"use strict";
|
|
1466
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
1467
|
+
}
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1446
1470
|
// src/core/navigators/generators/CompositeGenerator.ts
|
|
1447
1471
|
var CompositeGenerator_exports = {};
|
|
1448
1472
|
__export(CompositeGenerator_exports, {
|
|
@@ -1810,7 +1834,7 @@ function shuffleInPlace(arr) {
|
|
|
1810
1834
|
function pickTopByScore(cards, limit) {
|
|
1811
1835
|
return [...cards].sort((a, b) => b.score - a.score || a.cardId.localeCompare(b.cardId)).slice(0, limit);
|
|
1812
1836
|
}
|
|
1813
|
-
var DEFAULT_FRESHNESS_WINDOW, DEFAULT_MAX_DIRECT_PER_RUN, DEFAULT_MAX_SUPPORT_PER_RUN, DEFAULT_HIERARCHY_DEPTH, DEFAULT_MIN_COUNT, DEFAULT_PRACTICE_MIN_COUNT, DEFAULT_MAX_PRACTICE_PER_RUN, BASE_TARGET_SCORE, BASE_SUPPORT_SCORE, DISCOVERED_SUPPORT_SCORE, BASE_PRACTICE_SCORE, MAX_TARGET_MULTIPLIER, MAX_SUPPORT_MULTIPLIER, PRESCRIBED_DEBUG_VERSION, PrescribedCardsGenerator;
|
|
1837
|
+
var DEFAULT_FRESHNESS_WINDOW, DEFAULT_MAX_DIRECT_PER_RUN, DEFAULT_MAX_SUPPORT_PER_RUN, DEFAULT_HIERARCHY_DEPTH, DEFAULT_MIN_COUNT, DEFAULT_PRACTICE_MIN_COUNT, DEFAULT_MAX_PRACTICE_PER_RUN, BASE_TARGET_SCORE, BASE_SUPPORT_SCORE, DISCOVERED_SUPPORT_SCORE, BASE_PRACTICE_SCORE, PRACTICE_BASE_MULT, MAX_PRACTICE_MULTIPLIER, PRACTICE_STALENESS_BUMP_PER_DAY, MAX_TARGET_MULTIPLIER, MAX_SUPPORT_MULTIPLIER, PRESCRIBED_DEBUG_VERSION, PrescribedCardsGenerator;
|
|
1814
1838
|
var init_prescribed = __esm({
|
|
1815
1839
|
"src/core/navigators/generators/prescribed.ts"() {
|
|
1816
1840
|
"use strict";
|
|
@@ -1827,6 +1851,9 @@ var init_prescribed = __esm({
|
|
|
1827
1851
|
BASE_SUPPORT_SCORE = 0.8;
|
|
1828
1852
|
DISCOVERED_SUPPORT_SCORE = 12;
|
|
1829
1853
|
BASE_PRACTICE_SCORE = 1;
|
|
1854
|
+
PRACTICE_BASE_MULT = 2;
|
|
1855
|
+
MAX_PRACTICE_MULTIPLIER = 4;
|
|
1856
|
+
PRACTICE_STALENESS_BUMP_PER_DAY = 0.5;
|
|
1830
1857
|
MAX_TARGET_MULTIPLIER = 8;
|
|
1831
1858
|
MAX_SUPPORT_MULTIPLIER = 4;
|
|
1832
1859
|
PRESCRIBED_DEBUG_VERSION = "testversion-prescribed-v3";
|
|
@@ -1886,6 +1913,8 @@ var init_prescribed = __esm({
|
|
|
1886
1913
|
const emitted = [];
|
|
1887
1914
|
const emittedIds = /* @__PURE__ */ new Set();
|
|
1888
1915
|
const groupRuntimes = [];
|
|
1916
|
+
const priorPracticeDebt = progress.practiceDebt ?? {};
|
|
1917
|
+
const nextPracticeDebt = {};
|
|
1889
1918
|
for (const group of this.config.groups) {
|
|
1890
1919
|
const runtime = this.buildGroupRuntimeState({
|
|
1891
1920
|
group,
|
|
@@ -1943,10 +1972,13 @@ var init_prescribed = __esm({
|
|
|
1943
1972
|
userTagElo,
|
|
1944
1973
|
userGlobalElo,
|
|
1945
1974
|
activeIds,
|
|
1946
|
-
seenIds
|
|
1975
|
+
seenIds,
|
|
1976
|
+
priorPracticeDebt,
|
|
1977
|
+
nextPracticeDebt
|
|
1947
1978
|
});
|
|
1948
1979
|
emitted.push(...directCards, ...supportCards, ...discoveredSupportCards, ...practiceCards);
|
|
1949
1980
|
}
|
|
1981
|
+
nextState.practiceDebt = nextPracticeDebt;
|
|
1950
1982
|
const hintSummary = this.buildSupportHintSummary(groupRuntimes);
|
|
1951
1983
|
const hints = Object.keys(hintSummary.boostTags).length > 0 ? {
|
|
1952
1984
|
boostTags: hintSummary.boostTags,
|
|
@@ -2280,9 +2312,16 @@ var init_prescribed = __esm({
|
|
|
2280
2312
|
* `practiceMinCount`), this resolves cards carrying that tag and emits them
|
|
2281
2313
|
* into the candidate pool. It exists because global-ELO retrieval
|
|
2282
2314
|
* systematically fails to fetch the (low-ELO) drill cards for a
|
|
2283
|
-
* freshly-introduced skill — putting them in the pool here
|
|
2284
|
-
*
|
|
2285
|
-
*
|
|
2315
|
+
* freshly-introduced skill — putting them in the pool here guarantees presence.
|
|
2316
|
+
*
|
|
2317
|
+
* Emphasis is a **practice-debt pressure** (parallel to SRS backlog pressure):
|
|
2318
|
+
* cards score `base × multiplier`, where the multiplier starts at
|
|
2319
|
+
* PRACTICE_BASE_MULT (so a few reps land promptly post-intro, competing with
|
|
2320
|
+
* pressured reviews) and escalates by how long the debt has stayed open
|
|
2321
|
+
* (per-tag, time-based via `priorPracticeDebt`/`nextPracticeDebt`), clamped at
|
|
2322
|
+
* MAX_PRACTICE_MULTIPLIER. The debt is durable and self-discharges the instant
|
|
2323
|
+
* the skill reaches `practiceMinCount` — so this no longer relies on the
|
|
2324
|
+
* session-scoped intro boost to actually surface.
|
|
2286
2325
|
*
|
|
2287
2326
|
* Fully data-driven: the unlock relation comes from the hierarchy config and
|
|
2288
2327
|
* practice-status from per-tag ELO. No card-id or tag-namespace hard-coding.
|
|
@@ -2297,7 +2336,9 @@ var init_prescribed = __esm({
|
|
|
2297
2336
|
userTagElo,
|
|
2298
2337
|
userGlobalElo,
|
|
2299
2338
|
activeIds,
|
|
2300
|
-
seenIds
|
|
2339
|
+
seenIds,
|
|
2340
|
+
priorPracticeDebt,
|
|
2341
|
+
nextPracticeDebt
|
|
2301
2342
|
} = args;
|
|
2302
2343
|
const patterns = group.practiceTagPatterns ?? [];
|
|
2303
2344
|
if (patterns.length === 0) return [];
|
|
@@ -2307,6 +2348,20 @@ var init_prescribed = __esm({
|
|
|
2307
2348
|
(tag) => patterns.some((p) => matchesTagPattern(tag, p)) && this.isUnlockedGatedSkill(tag, hierarchyConfigs, userTagElo, userGlobalElo) && (userTagElo[tag]?.count ?? 0) < practiceMinCount
|
|
2308
2349
|
);
|
|
2309
2350
|
if (practiceTags.length === 0) return [];
|
|
2351
|
+
const now = Date.now();
|
|
2352
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
2353
|
+
const tagMultiplier = /* @__PURE__ */ new Map();
|
|
2354
|
+
for (const tag of practiceTags) {
|
|
2355
|
+
const firstOwedAt = priorPracticeDebt[tag] ?? isoNow();
|
|
2356
|
+
nextPracticeDebt[tag] = firstOwedAt;
|
|
2357
|
+
const staleDays = Math.max(0, (now - new Date(firstOwedAt).getTime()) / DAY_MS);
|
|
2358
|
+
const mult = clamp(
|
|
2359
|
+
PRACTICE_BASE_MULT + staleDays * PRACTICE_STALENESS_BUMP_PER_DAY,
|
|
2360
|
+
PRACTICE_BASE_MULT,
|
|
2361
|
+
MAX_PRACTICE_MULTIPLIER
|
|
2362
|
+
);
|
|
2363
|
+
tagMultiplier.set(tag, mult);
|
|
2364
|
+
}
|
|
2310
2365
|
const practiceCardIds = this.findDiscoveredSupportCards({
|
|
2311
2366
|
supportTags: practiceTags,
|
|
2312
2367
|
cardsByTag,
|
|
@@ -2322,18 +2377,25 @@ var init_prescribed = __esm({
|
|
|
2322
2377
|
const cards = [];
|
|
2323
2378
|
for (const cardId of practiceCardIds) {
|
|
2324
2379
|
emittedIds.add(cardId);
|
|
2380
|
+
let mult = PRACTICE_BASE_MULT;
|
|
2381
|
+
for (const tag of practiceTags) {
|
|
2382
|
+
if (cardsByTag.get(tag)?.includes(cardId) ?? false) {
|
|
2383
|
+
mult = Math.max(mult, tagMultiplier.get(tag) ?? PRACTICE_BASE_MULT);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
const score = BASE_PRACTICE_SCORE * mult;
|
|
2325
2387
|
cards.push({
|
|
2326
2388
|
cardId,
|
|
2327
2389
|
courseId,
|
|
2328
|
-
score
|
|
2390
|
+
score,
|
|
2329
2391
|
provenance: [
|
|
2330
2392
|
{
|
|
2331
2393
|
strategy: "prescribed",
|
|
2332
2394
|
strategyName: this.strategyName || this.name,
|
|
2333
2395
|
strategyId: this.strategyId || "NAVIGATION_STRATEGY-prescribed",
|
|
2334
2396
|
action: "generated",
|
|
2335
|
-
score
|
|
2336
|
-
reason: `mode=practice;group=${group.id};underPracticedSkills=${practiceTags.length};practiceTags=${practiceTags.slice(0, 8).join("|")}${practiceTags.length > 8 ? "|\u2026" : ""};testversion=${PRESCRIBED_DEBUG_VERSION}`
|
|
2397
|
+
score,
|
|
2398
|
+
reason: `mode=practice;group=${group.id};debtMult=\xD7${mult.toFixed(2)};underPracticedSkills=${practiceTags.length};practiceTags=${practiceTags.slice(0, 8).join("|")}${practiceTags.length > 8 ? "|\u2026" : ""};testversion=${PRESCRIBED_DEBUG_VERSION}`
|
|
2337
2399
|
}
|
|
2338
2400
|
]
|
|
2339
2401
|
});
|
|
@@ -2506,15 +2568,16 @@ var srs_exports = {};
|
|
|
2506
2568
|
__export(srs_exports, {
|
|
2507
2569
|
default: () => SRSNavigator
|
|
2508
2570
|
});
|
|
2509
|
-
var import_moment3, DEFAULT_HEALTHY_BACKLOG,
|
|
2571
|
+
var import_moment3, DEFAULT_HEALTHY_BACKLOG, MAX_BACKLOG_MULTIPLIER, SRSNavigator;
|
|
2510
2572
|
var init_srs = __esm({
|
|
2511
2573
|
"src/core/navigators/generators/srs.ts"() {
|
|
2512
2574
|
"use strict";
|
|
2513
2575
|
import_moment3 = __toESM(require("moment"), 1);
|
|
2514
2576
|
init_navigators();
|
|
2577
|
+
init_SrsDebugger();
|
|
2515
2578
|
init_logger();
|
|
2516
2579
|
DEFAULT_HEALTHY_BACKLOG = 20;
|
|
2517
|
-
|
|
2580
|
+
MAX_BACKLOG_MULTIPLIER = 2;
|
|
2518
2581
|
SRSNavigator = class extends ContentNavigator {
|
|
2519
2582
|
/** Human-readable name for CardGenerator interface */
|
|
2520
2583
|
name;
|
|
@@ -2581,9 +2644,18 @@ var init_srs = __esm({
|
|
|
2581
2644
|
}
|
|
2582
2645
|
}
|
|
2583
2646
|
}
|
|
2584
|
-
const
|
|
2647
|
+
const backlogMultiplier = this.computeBacklogMultiplier(dueReviews.length);
|
|
2648
|
+
const notDue = reviews.filter((r) => !now.isAfter(import_moment3.default.utc(r.reviewTime)));
|
|
2649
|
+
let nextDueIn = null;
|
|
2650
|
+
if (notDue.length > 0) {
|
|
2651
|
+
const next = notDue.reduce(
|
|
2652
|
+
(a, b) => import_moment3.default.utc(a.reviewTime).isBefore(import_moment3.default.utc(b.reviewTime)) ? a : b
|
|
2653
|
+
);
|
|
2654
|
+
const until = import_moment3.default.duration(import_moment3.default.utc(next.reviewTime).diff(now));
|
|
2655
|
+
nextDueIn = until.asHours() < 1 ? `${Math.round(until.asMinutes())}m` : until.asHours() < 24 ? `${Math.round(until.asHours())}h` : `${Math.round(until.asDays())}d`;
|
|
2656
|
+
}
|
|
2585
2657
|
if (dueReviews.length > 0) {
|
|
2586
|
-
const pressureNote =
|
|
2658
|
+
const pressureNote = backlogMultiplier > 1 ? ` [backlog pressure: \xD7${backlogMultiplier.toFixed(2)}]` : ` [healthy backlog]`;
|
|
2587
2659
|
logger.info(
|
|
2588
2660
|
`[SRS] Course ${courseId}: ${dueReviews.length} reviews due now (of ${reviews.length} scheduled)${pressureNote}`
|
|
2589
2661
|
);
|
|
@@ -2602,7 +2674,7 @@ var init_srs = __esm({
|
|
|
2602
2674
|
logger.info(`[SRS] Course ${courseId}: No reviews scheduled`);
|
|
2603
2675
|
}
|
|
2604
2676
|
const scored = dueReviews.map((review) => {
|
|
2605
|
-
const { score, reason } = this.computeUrgencyScore(review, now,
|
|
2677
|
+
const { score, reason } = this.computeUrgencyScore(review, now, backlogMultiplier);
|
|
2606
2678
|
return {
|
|
2607
2679
|
cardId: review.cardId,
|
|
2608
2680
|
courseId: review.courseId,
|
|
@@ -2620,30 +2692,42 @@ var init_srs = __esm({
|
|
|
2620
2692
|
]
|
|
2621
2693
|
};
|
|
2622
2694
|
});
|
|
2623
|
-
|
|
2695
|
+
const sorted = scored.sort((a, b) => b.score - a.score);
|
|
2696
|
+
captureSrsBacklog({
|
|
2697
|
+
courseId,
|
|
2698
|
+
scheduledTotal: reviews.length,
|
|
2699
|
+
dueNow: dueReviews.length,
|
|
2700
|
+
healthyBacklog: this.healthyBacklog,
|
|
2701
|
+
backlogMultiplier,
|
|
2702
|
+
maxBacklogMultiplier: MAX_BACKLOG_MULTIPLIER,
|
|
2703
|
+
topReviewScore: sorted.length > 0 ? sorted[0].score : null,
|
|
2704
|
+
nextDueIn,
|
|
2705
|
+
timestamp: Date.now()
|
|
2706
|
+
});
|
|
2707
|
+
return { cards: sorted.slice(0, limit) };
|
|
2624
2708
|
}
|
|
2625
2709
|
/**
|
|
2626
|
-
* Compute backlog pressure based on number of due reviews.
|
|
2710
|
+
* Compute the multiplicative backlog pressure based on number of due reviews.
|
|
2627
2711
|
*
|
|
2628
|
-
*
|
|
2629
|
-
* and
|
|
2712
|
+
* ×1.0 at or below the healthy threshold (no boost), increasing linearly above
|
|
2713
|
+
* it and maxing out at MAX_BACKLOG_MULTIPLIER at 3× the healthy backlog.
|
|
2630
2714
|
*
|
|
2631
|
-
* Examples (with default healthyBacklog=20):
|
|
2632
|
-
* - 10 due reviews →
|
|
2633
|
-
* - 20 due reviews →
|
|
2634
|
-
* - 40 due reviews →
|
|
2635
|
-
* - 60 due reviews →
|
|
2715
|
+
* Examples (with default healthyBacklog=20, MAX_BACKLOG_MULTIPLIER=2.0):
|
|
2716
|
+
* - 10 due reviews → ×1.00 (healthy)
|
|
2717
|
+
* - 20 due reviews → ×1.00 (at threshold)
|
|
2718
|
+
* - 40 due reviews → ×1.50 (2x threshold)
|
|
2719
|
+
* - 60 due reviews → ×2.00 (3x threshold, maxed)
|
|
2636
2720
|
*
|
|
2637
2721
|
* @param dueCount - Number of reviews currently due
|
|
2638
|
-
* @returns
|
|
2722
|
+
* @returns Multiplier applied to review urgency (1.0 to MAX_BACKLOG_MULTIPLIER)
|
|
2639
2723
|
*/
|
|
2640
|
-
|
|
2724
|
+
computeBacklogMultiplier(dueCount) {
|
|
2641
2725
|
if (dueCount <= this.healthyBacklog) {
|
|
2642
|
-
return
|
|
2726
|
+
return 1;
|
|
2643
2727
|
}
|
|
2644
2728
|
const excess = dueCount - this.healthyBacklog;
|
|
2645
|
-
const
|
|
2646
|
-
return Math.min(
|
|
2729
|
+
const multiplier = 1 + excess / this.healthyBacklog * ((MAX_BACKLOG_MULTIPLIER - 1) / 2);
|
|
2730
|
+
return Math.min(MAX_BACKLOG_MULTIPLIER, multiplier);
|
|
2647
2731
|
}
|
|
2648
2732
|
/**
|
|
2649
2733
|
* Compute urgency score for a review card.
|
|
@@ -2658,19 +2742,20 @@ var init_srs = __esm({
|
|
|
2658
2742
|
* - 30 days (720h) → ~0.56
|
|
2659
2743
|
* - 180 days → ~0.30
|
|
2660
2744
|
*
|
|
2661
|
-
* 3. Backlog pressure = global
|
|
2662
|
-
*
|
|
2663
|
-
* - At 2x healthy: +0.25
|
|
2664
|
-
* - At 3x+ healthy: +0.50 (max)
|
|
2745
|
+
* 3. Backlog pressure = global *multiplier* when review backlog exceeds the
|
|
2746
|
+
* healthy threshold (×1.0 healthy → up to MAX_BACKLOG_MULTIPLIER at 3×).
|
|
2665
2747
|
*
|
|
2666
|
-
* Combined: base 0.5 +
|
|
2667
|
-
*
|
|
2748
|
+
* Combined: (base 0.5 + urgency factors * 0.45) × backlog multiplier.
|
|
2749
|
+
* Per-card range before pressure: ~0.57–0.95. NOT clamped to 1.0 — under a
|
|
2750
|
+
* heavy backlog reviews scale onto the open scale to compete with (and exceed)
|
|
2751
|
+
* new cards; what keeps them from running away is the bounded multiplier, not
|
|
2752
|
+
* a hard ceiling.
|
|
2668
2753
|
*
|
|
2669
2754
|
* @param review - The scheduled card to score
|
|
2670
2755
|
* @param now - Current time
|
|
2671
|
-
* @param
|
|
2756
|
+
* @param backlogMultiplier - Pre-computed backlog multiplier (1.0 to MAX_BACKLOG_MULTIPLIER)
|
|
2672
2757
|
*/
|
|
2673
|
-
computeUrgencyScore(review, now,
|
|
2758
|
+
computeUrgencyScore(review, now, backlogMultiplier) {
|
|
2674
2759
|
const scheduledAt = import_moment3.default.utc(review.scheduledAt);
|
|
2675
2760
|
const due = import_moment3.default.utc(review.reviewTime);
|
|
2676
2761
|
const intervalHours = Math.max(1, due.diff(scheduledAt, "hours"));
|
|
@@ -2680,15 +2765,15 @@ var init_srs = __esm({
|
|
|
2680
2765
|
const overdueContribution = Math.min(1, Math.max(0, relativeOverdue));
|
|
2681
2766
|
const urgency = overdueContribution * 0.5 + recencyFactor * 0.5;
|
|
2682
2767
|
const baseScore = 0.5 + urgency * 0.45;
|
|
2683
|
-
const score =
|
|
2768
|
+
const score = baseScore * backlogMultiplier;
|
|
2684
2769
|
const reasonParts = [
|
|
2685
2770
|
`${Math.round(hoursOverdue)}h overdue`,
|
|
2686
2771
|
`interval: ${Math.round(intervalHours)}h`,
|
|
2687
2772
|
`relative: ${relativeOverdue.toFixed(2)}`,
|
|
2688
2773
|
`recency: ${recencyFactor.toFixed(2)}`
|
|
2689
2774
|
];
|
|
2690
|
-
if (
|
|
2691
|
-
reasonParts.push(`backlog:
|
|
2775
|
+
if (backlogMultiplier > 1) {
|
|
2776
|
+
reasonParts.push(`backlog: \xD7${backlogMultiplier.toFixed(2)}`);
|
|
2692
2777
|
}
|
|
2693
2778
|
reasonParts.push("review");
|
|
2694
2779
|
const reason = reasonParts.join(", ");
|
|
@@ -4740,6 +4825,7 @@ var init_3 = __esm({
|
|
|
4740
4825
|
"./Pipeline.ts": () => Promise.resolve().then(() => (init_Pipeline(), Pipeline_exports)),
|
|
4741
4826
|
"./PipelineAssembler.ts": () => Promise.resolve().then(() => (init_PipelineAssembler(), PipelineAssembler_exports)),
|
|
4742
4827
|
"./PipelineDebugger.ts": () => Promise.resolve().then(() => (init_PipelineDebugger(), PipelineDebugger_exports)),
|
|
4828
|
+
"./SrsDebugger.ts": () => Promise.resolve().then(() => (init_SrsDebugger(), SrsDebugger_exports)),
|
|
4743
4829
|
"./defaults.ts": () => Promise.resolve().then(() => (init_defaults(), defaults_exports)),
|
|
4744
4830
|
"./diversityRerank.ts": () => Promise.resolve().then(() => (init_diversityRerank(), diversityRerank_exports)),
|
|
4745
4831
|
"./filters/WeightedFilter.ts": () => Promise.resolve().then(() => (init_WeightedFilter(), WeightedFilter_exports)),
|
|
@@ -4772,12 +4858,14 @@ __export(navigators_exports, {
|
|
|
4772
4858
|
NavigatorRole: () => NavigatorRole,
|
|
4773
4859
|
NavigatorRoles: () => NavigatorRoles,
|
|
4774
4860
|
Navigators: () => Navigators,
|
|
4861
|
+
clearSrsBacklogDebug: () => clearSrsBacklogDebug,
|
|
4775
4862
|
diversityRerank: () => diversityRerank,
|
|
4776
4863
|
getActivePipeline: () => getActivePipeline,
|
|
4777
4864
|
getCardOrigin: () => getCardOrigin,
|
|
4778
4865
|
getRegisteredNavigator: () => getRegisteredNavigator,
|
|
4779
4866
|
getRegisteredNavigatorNames: () => getRegisteredNavigatorNames,
|
|
4780
4867
|
getRegisteredNavigatorRole: () => getRegisteredNavigatorRole,
|
|
4868
|
+
getSrsBacklogDebug: () => getSrsBacklogDebug,
|
|
4781
4869
|
hasRegisteredNavigator: () => hasRegisteredNavigator,
|
|
4782
4870
|
initializeNavigatorRegistry: () => initializeNavigatorRegistry,
|
|
4783
4871
|
isFilter: () => isFilter,
|
|
@@ -4859,6 +4947,7 @@ var init_navigators = __esm({
|
|
|
4859
4947
|
"use strict";
|
|
4860
4948
|
init_diversityRerank();
|
|
4861
4949
|
init_PipelineDebugger();
|
|
4950
|
+
init_SrsDebugger();
|
|
4862
4951
|
init_logger();
|
|
4863
4952
|
init_();
|
|
4864
4953
|
init_2();
|