@vue-skuilder/db 0.2.7 → 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 +67 -4
- package/dist/core/index.d.ts +67 -4
- package/dist/core/index.js +201 -39
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +198 -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 +195 -39
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +195 -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 +195 -39
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +195 -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 +440 -251
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +437 -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/Pipeline.ts +93 -1
- package/src/core/navigators/PipelineDebugger.ts +11 -1
- 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 +9 -0
- package/src/study/SessionController.ts +260 -249
- package/src/study/SessionDebugger.ts +15 -25
- package/src/study/SessionOverlay.ts +108 -13
|
@@ -3,6 +3,7 @@ import type { ScheduledCard } from '../../types/user';
|
|
|
3
3
|
import type { CourseDBInterface } from '../../interfaces/courseDB';
|
|
4
4
|
import type { UserDBInterface } from '../../interfaces/userDB';
|
|
5
5
|
import { ContentNavigator } from '../index';
|
|
6
|
+
import { captureSrsBacklog } from '../SrsDebugger';
|
|
6
7
|
import type { ContentNavigationStrategyData } from '../../types/contentNavigationStrategy';
|
|
7
8
|
import type { CardGenerator, GeneratorContext, GeneratorResult } from './types';
|
|
8
9
|
import { logger } from '@db/util/logger';
|
|
@@ -41,10 +42,19 @@ import { logger } from '@db/util/logger';
|
|
|
41
42
|
const DEFAULT_HEALTHY_BACKLOG = 20;
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
|
-
* Maximum backlog pressure
|
|
45
|
-
*
|
|
45
|
+
* Maximum backlog pressure as a *multiplier* on review urgency.
|
|
46
|
+
*
|
|
47
|
+
* Backlog pressure is multiplicative (×1.0 at/below healthy, scaling up as the
|
|
48
|
+
* due pile grows, maxing here at 3× healthy backlog). It replaces an older
|
|
49
|
+
* additive +0..+0.5 term that was a [0,1]-era modifier — once review scores
|
|
50
|
+
* stopped being clamped to 1.0 and new cards could be boosted well past it
|
|
51
|
+
* (e.g. an intro ×5 → 7+), a flat +0.5 was both too small to compete and mostly
|
|
52
|
+
* eaten by the old 1.0 clamp. A multiplier scales review priority onto the same
|
|
53
|
+
* open scale the boosted new cards live on, so a heavy backlog can genuinely
|
|
54
|
+
* lift reviews into competition. Tunable — verify review vs new ordering in the
|
|
55
|
+
* dbg overlay's "review backpressure" panel.
|
|
46
56
|
*/
|
|
47
|
-
const
|
|
57
|
+
const MAX_BACKLOG_MULTIPLIER = 2.0;
|
|
48
58
|
|
|
49
59
|
/**
|
|
50
60
|
* Configuration for the SRS strategy.
|
|
@@ -158,14 +168,31 @@ export default class SRSNavigator extends ContentNavigator implements CardGenera
|
|
|
158
168
|
}
|
|
159
169
|
}
|
|
160
170
|
|
|
161
|
-
// Compute backlog pressure - applies globally to all reviews
|
|
162
|
-
const
|
|
171
|
+
// Compute backlog pressure (multiplicative) - applies globally to all reviews
|
|
172
|
+
const backlogMultiplier = this.computeBacklogMultiplier(dueReviews.length);
|
|
173
|
+
|
|
174
|
+
// Time until the next not-yet-due review (for the debug overlay): shows
|
|
175
|
+
// reviews are *coming* even when none are due right now.
|
|
176
|
+
const notDue = reviews.filter((r) => !now.isAfter(moment.utc(r.reviewTime)));
|
|
177
|
+
let nextDueIn: string | null = null;
|
|
178
|
+
if (notDue.length > 0) {
|
|
179
|
+
const next = notDue.reduce((a, b) =>
|
|
180
|
+
moment.utc(a.reviewTime).isBefore(moment.utc(b.reviewTime)) ? a : b
|
|
181
|
+
);
|
|
182
|
+
const until = moment.duration(moment.utc(next.reviewTime).diff(now));
|
|
183
|
+
nextDueIn =
|
|
184
|
+
until.asHours() < 1
|
|
185
|
+
? `${Math.round(until.asMinutes())}m`
|
|
186
|
+
: until.asHours() < 24
|
|
187
|
+
? `${Math.round(until.asHours())}h`
|
|
188
|
+
: `${Math.round(until.asDays())}d`;
|
|
189
|
+
}
|
|
163
190
|
|
|
164
191
|
// Log review status for transparency
|
|
165
192
|
if (dueReviews.length > 0) {
|
|
166
193
|
const pressureNote =
|
|
167
|
-
|
|
168
|
-
? ` [backlog pressure:
|
|
194
|
+
backlogMultiplier > 1
|
|
195
|
+
? ` [backlog pressure: ×${backlogMultiplier.toFixed(2)}]`
|
|
169
196
|
: ` [healthy backlog]`;
|
|
170
197
|
logger.info(
|
|
171
198
|
`[SRS] Course ${courseId}: ${dueReviews.length} reviews due now (of ${reviews.length} scheduled)${pressureNote}`
|
|
@@ -192,7 +219,7 @@ export default class SRSNavigator extends ContentNavigator implements CardGenera
|
|
|
192
219
|
}
|
|
193
220
|
|
|
194
221
|
const scored = dueReviews.map((review) => {
|
|
195
|
-
const { score, reason } = this.computeUrgencyScore(review, now,
|
|
222
|
+
const { score, reason } = this.computeUrgencyScore(review, now, backlogMultiplier);
|
|
196
223
|
|
|
197
224
|
return {
|
|
198
225
|
cardId: review.cardId,
|
|
@@ -213,41 +240,56 @@ export default class SRSNavigator extends ContentNavigator implements CardGenera
|
|
|
213
240
|
});
|
|
214
241
|
|
|
215
242
|
// Sort by score descending and limit
|
|
243
|
+
const sorted = scored.sort((a, b) => b.score - a.score);
|
|
244
|
+
|
|
245
|
+
// Capture backlog state for the live session overlay (see SrsDebugger).
|
|
246
|
+
captureSrsBacklog({
|
|
247
|
+
courseId,
|
|
248
|
+
scheduledTotal: reviews.length,
|
|
249
|
+
dueNow: dueReviews.length,
|
|
250
|
+
healthyBacklog: this.healthyBacklog,
|
|
251
|
+
backlogMultiplier,
|
|
252
|
+
maxBacklogMultiplier: MAX_BACKLOG_MULTIPLIER,
|
|
253
|
+
topReviewScore: sorted.length > 0 ? sorted[0].score : null,
|
|
254
|
+
nextDueIn,
|
|
255
|
+
timestamp: Date.now(),
|
|
256
|
+
});
|
|
257
|
+
|
|
216
258
|
// [perf] parked: SRSgen / getPendingReviews timing
|
|
217
|
-
// const srsResult = { cards:
|
|
259
|
+
// const srsResult = { cards: sorted.slice(0, limit) };
|
|
218
260
|
// logger.info(
|
|
219
261
|
// `[perf][SRSgen] total=${(performance.now() - tSrs0).toFixed(0)}ms ` +
|
|
220
262
|
// `(pendingReviews=${(tReviews - tSrs0).toFixed(0)}) ` +
|
|
221
263
|
// `[scheduled=${reviews.length} due=${dueReviews.length}]`
|
|
222
264
|
// );
|
|
223
|
-
return { cards:
|
|
265
|
+
return { cards: sorted.slice(0, limit) };
|
|
224
266
|
}
|
|
225
267
|
|
|
226
268
|
/**
|
|
227
|
-
* Compute backlog pressure based on number of due reviews.
|
|
269
|
+
* Compute the multiplicative backlog pressure based on number of due reviews.
|
|
228
270
|
*
|
|
229
|
-
*
|
|
230
|
-
* and
|
|
271
|
+
* ×1.0 at or below the healthy threshold (no boost), increasing linearly above
|
|
272
|
+
* it and maxing out at MAX_BACKLOG_MULTIPLIER at 3× the healthy backlog.
|
|
231
273
|
*
|
|
232
|
-
* Examples (with default healthyBacklog=20):
|
|
233
|
-
* - 10 due reviews →
|
|
234
|
-
* - 20 due reviews →
|
|
235
|
-
* - 40 due reviews →
|
|
236
|
-
* - 60 due reviews →
|
|
274
|
+
* Examples (with default healthyBacklog=20, MAX_BACKLOG_MULTIPLIER=2.0):
|
|
275
|
+
* - 10 due reviews → ×1.00 (healthy)
|
|
276
|
+
* - 20 due reviews → ×1.00 (at threshold)
|
|
277
|
+
* - 40 due reviews → ×1.50 (2x threshold)
|
|
278
|
+
* - 60 due reviews → ×2.00 (3x threshold, maxed)
|
|
237
279
|
*
|
|
238
280
|
* @param dueCount - Number of reviews currently due
|
|
239
|
-
* @returns
|
|
281
|
+
* @returns Multiplier applied to review urgency (1.0 to MAX_BACKLOG_MULTIPLIER)
|
|
240
282
|
*/
|
|
241
|
-
private
|
|
283
|
+
private computeBacklogMultiplier(dueCount: number): number {
|
|
242
284
|
if (dueCount <= this.healthyBacklog) {
|
|
243
|
-
return 0;
|
|
285
|
+
return 1.0;
|
|
244
286
|
}
|
|
245
287
|
|
|
246
|
-
// Linear
|
|
288
|
+
// Linear in excess: ×1 at healthy, reaching MAX at 3× healthy (excess = 2×healthy).
|
|
247
289
|
const excess = dueCount - this.healthyBacklog;
|
|
248
|
-
const
|
|
290
|
+
const multiplier = 1 + (excess / this.healthyBacklog) * ((MAX_BACKLOG_MULTIPLIER - 1) / 2);
|
|
249
291
|
|
|
250
|
-
return Math.min(
|
|
292
|
+
return Math.min(MAX_BACKLOG_MULTIPLIER, multiplier);
|
|
251
293
|
}
|
|
252
294
|
|
|
253
295
|
/**
|
|
@@ -263,22 +305,23 @@ export default class SRSNavigator extends ContentNavigator implements CardGenera
|
|
|
263
305
|
* - 30 days (720h) → ~0.56
|
|
264
306
|
* - 180 days → ~0.30
|
|
265
307
|
*
|
|
266
|
-
* 3. Backlog pressure = global
|
|
267
|
-
*
|
|
268
|
-
* - At 2x healthy: +0.25
|
|
269
|
-
* - At 3x+ healthy: +0.50 (max)
|
|
308
|
+
* 3. Backlog pressure = global *multiplier* when review backlog exceeds the
|
|
309
|
+
* healthy threshold (×1.0 healthy → up to MAX_BACKLOG_MULTIPLIER at 3×).
|
|
270
310
|
*
|
|
271
|
-
* Combined: base 0.5 +
|
|
272
|
-
*
|
|
311
|
+
* Combined: (base 0.5 + urgency factors * 0.45) × backlog multiplier.
|
|
312
|
+
* Per-card range before pressure: ~0.57–0.95. NOT clamped to 1.0 — under a
|
|
313
|
+
* heavy backlog reviews scale onto the open scale to compete with (and exceed)
|
|
314
|
+
* new cards; what keeps them from running away is the bounded multiplier, not
|
|
315
|
+
* a hard ceiling.
|
|
273
316
|
*
|
|
274
317
|
* @param review - The scheduled card to score
|
|
275
318
|
* @param now - Current time
|
|
276
|
-
* @param
|
|
319
|
+
* @param backlogMultiplier - Pre-computed backlog multiplier (1.0 to MAX_BACKLOG_MULTIPLIER)
|
|
277
320
|
*/
|
|
278
321
|
private computeUrgencyScore(
|
|
279
322
|
review: ScheduledCard,
|
|
280
323
|
now: moment.Moment,
|
|
281
|
-
|
|
324
|
+
backlogMultiplier: number
|
|
282
325
|
): { score: number; reason: string } {
|
|
283
326
|
const scheduledAt = moment.utc(review.scheduledAt);
|
|
284
327
|
const due = moment.utc(review.reviewTime);
|
|
@@ -299,10 +342,11 @@ export default class SRSNavigator extends ContentNavigator implements CardGenera
|
|
|
299
342
|
const overdueContribution = Math.min(1.0, Math.max(0, relativeOverdue));
|
|
300
343
|
const urgency = overdueContribution * 0.5 + recencyFactor * 0.5;
|
|
301
344
|
|
|
302
|
-
// Final score: base 0.5 +
|
|
303
|
-
//
|
|
345
|
+
// Final score: per-card urgency (base 0.5 + contribution) scaled by the
|
|
346
|
+
// global backlog multiplier. No 1.0 clamp — reviews compete on the open
|
|
347
|
+
// scale; the bounded multiplier (not a ceiling) caps the lift.
|
|
304
348
|
const baseScore = 0.5 + urgency * 0.45;
|
|
305
|
-
const score =
|
|
349
|
+
const score = baseScore * backlogMultiplier;
|
|
306
350
|
|
|
307
351
|
// Build reason string with all contributing factors
|
|
308
352
|
const reasonParts = [
|
|
@@ -312,8 +356,8 @@ export default class SRSNavigator extends ContentNavigator implements CardGenera
|
|
|
312
356
|
`recency: ${recencyFactor.toFixed(2)}`,
|
|
313
357
|
];
|
|
314
358
|
|
|
315
|
-
if (
|
|
316
|
-
reasonParts.push(`backlog:
|
|
359
|
+
if (backlogMultiplier > 1) {
|
|
360
|
+
reasonParts.push(`backlog: ×${backlogMultiplier.toFixed(2)}`);
|
|
317
361
|
}
|
|
318
362
|
|
|
319
363
|
reasonParts.push('review');
|
|
@@ -19,11 +19,20 @@ import type { GeneratorResult, ReplanHints } from './generators/types';
|
|
|
19
19
|
export {
|
|
20
20
|
pipelineDebugAPI,
|
|
21
21
|
mountPipelineDebugger,
|
|
22
|
+
getActivePipeline,
|
|
22
23
|
type PipelineRunReport,
|
|
23
24
|
type GeneratorSummary,
|
|
24
25
|
type FilterImpact,
|
|
25
26
|
} from './PipelineDebugger';
|
|
26
27
|
|
|
28
|
+
// Re-export the commit-free forecast capability surface.
|
|
29
|
+
export type { PipelineForecaster } from './Pipeline';
|
|
30
|
+
export {
|
|
31
|
+
getSrsBacklogDebug,
|
|
32
|
+
clearSrsBacklogDebug,
|
|
33
|
+
type SrsBacklogDebug,
|
|
34
|
+
} from './SrsDebugger';
|
|
35
|
+
|
|
27
36
|
import { LearnableWeight } from '../types/contentNavigationStrategy';
|
|
28
37
|
export type { ContentNavigationStrategyData, LearnableWeight } from '../types/contentNavigationStrategy';
|
|
29
38
|
import type { ContentNavigationStrategyData } from '../types/contentNavigationStrategy';
|