@vue-skuilder/db 0.1.23 → 0.1.25
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-BP9hznNV.d.ts → contentSource-BmnmvH8C.d.ts} +268 -3
- package/dist/{contentSource-DsJadoBU.d.cts → contentSource-DfBbaLA-.d.cts} +268 -3
- package/dist/core/index.d.cts +310 -6
- package/dist/core/index.d.ts +310 -6
- package/dist/core/index.js +2606 -666
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +2564 -639
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-CHYrQ5pB.d.cts → dataLayerProvider-BeRXVMs5.d.cts} +1 -1
- package/dist/{dataLayerProvider-MDTxXq2l.d.ts → dataLayerProvider-CG9GfaAY.d.ts} +1 -1
- package/dist/impl/couch/index.d.cts +11 -3
- package/dist/impl/couch/index.d.ts +11 -3
- package/dist/impl/couch/index.js +2336 -656
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +2316 -631
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.cts +4 -4
- package/dist/impl/static/index.d.ts +4 -4
- package/dist/impl/static/index.js +2312 -632
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +2315 -630
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/{index-Dj0SEgk3.d.ts → index-BWvO-_rJ.d.ts} +1 -1
- package/dist/{index-B_j6u5E4.d.cts → index-Ba7hYbHj.d.cts} +1 -1
- package/dist/index.d.cts +278 -20
- package/dist/index.d.ts +278 -20
- package/dist/index.js +3603 -720
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3529 -674
- package/dist/index.mjs.map +1 -1
- package/dist/{types-DQaXnuoc.d.ts → types-CJrLM1Ew.d.ts} +1 -1
- package/dist/{types-Bn0itutr.d.cts → types-W8n-B6HG.d.cts} +1 -1
- package/dist/{types-legacy-DDY4N-Uq.d.cts → types-legacy-JXDxinpU.d.cts} +5 -1
- package/dist/{types-legacy-DDY4N-Uq.d.ts → types-legacy-JXDxinpU.d.ts} +5 -1
- package/dist/util/packer/index.d.cts +3 -3
- package/dist/util/packer/index.d.ts +3 -3
- package/docs/brainstorm-navigation-paradigm.md +40 -34
- package/docs/future-orchestration-vision.md +216 -0
- package/docs/navigators-architecture.md +210 -9
- package/docs/todo-review-urgency-adaptation.md +205 -0
- package/docs/todo-strategy-authoring.md +8 -6
- package/package.json +3 -3
- package/src/core/index.ts +2 -0
- package/src/core/interfaces/contentSource.ts +7 -0
- package/src/core/interfaces/userDB.ts +50 -0
- package/src/core/navigators/Pipeline.ts +132 -5
- package/src/core/navigators/PipelineAssembler.ts +21 -22
- package/src/core/navigators/PipelineDebugger.ts +426 -0
- package/src/core/navigators/filters/WeightedFilter.ts +141 -0
- package/src/core/navigators/filters/types.ts +4 -0
- package/src/core/navigators/generators/CompositeGenerator.ts +82 -19
- package/src/core/navigators/generators/elo.ts +14 -1
- package/src/core/navigators/generators/srs.ts +146 -18
- package/src/core/navigators/generators/types.ts +4 -0
- package/src/core/navigators/index.ts +203 -13
- package/src/core/orchestration/gradient.ts +133 -0
- package/src/core/orchestration/index.ts +210 -0
- package/src/core/orchestration/learning.ts +250 -0
- package/src/core/orchestration/recording.ts +92 -0
- package/src/core/orchestration/signal.ts +67 -0
- package/src/core/types/contentNavigationStrategy.ts +38 -0
- package/src/core/types/learningState.ts +77 -0
- package/src/core/types/types-legacy.ts +4 -0
- package/src/core/types/userOutcome.ts +51 -0
- package/src/courseConfigRegistration.ts +107 -0
- package/src/factory.ts +6 -0
- package/src/impl/common/BaseUserDB.ts +16 -0
- package/src/impl/couch/user-course-relDB.ts +12 -0
- package/src/study/MixerDebugger.ts +555 -0
- package/src/study/SessionController.ts +159 -20
- package/src/study/SessionDebugger.ts +442 -0
- package/src/study/SourceMixer.ts +36 -17
- package/src/study/TODO-session-scheduling.md +133 -0
- package/src/study/index.ts +2 -0
- package/src/study/services/EloService.ts +79 -4
- package/src/study/services/ResponseProcessor.ts +130 -72
- package/src/study/services/SrsService.ts +9 -0
- package/tests/core/navigators/Pipeline.test.ts +2 -0
- package/tests/core/navigators/PipelineAssembler.test.ts +4 -4
- package/docs/todo-evolutionary-orchestration.md +0 -310
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
The navigation strategy system selects and scores cards for study sessions. It uses a
|
|
6
6
|
**Pipeline architecture** where generators produce candidates and filters transform scores.
|
|
7
7
|
|
|
8
|
+
An **Evolutionary Orchestration** layer enables strategies to carry learnable weights that
|
|
9
|
+
automatically tune toward optimal values based on observed learning outcomes.
|
|
10
|
+
|
|
8
11
|
## Core Concepts
|
|
9
12
|
|
|
10
13
|
### WeightedCard
|
|
@@ -27,6 +30,7 @@ interface StrategyContribution {
|
|
|
27
30
|
action: 'generated' | 'passed' | 'boosted' | 'penalized';
|
|
28
31
|
score: number; // Score after this strategy
|
|
29
32
|
reason: string; // Human-readable explanation
|
|
33
|
+
deviation?: number; // User's deviation from peak weight (evolutionary)
|
|
30
34
|
}
|
|
31
35
|
```
|
|
32
36
|
|
|
@@ -42,11 +46,28 @@ interface CardGenerator {
|
|
|
42
46
|
```
|
|
43
47
|
|
|
44
48
|
**Implementations:**
|
|
45
|
-
- `ELONavigator` — New cards scored by ELO proximity to user skill
|
|
46
|
-
- `SRSNavigator` — Review cards scored by overdueness and
|
|
49
|
+
- `ELONavigator` — New cards scored by ELO proximity to user skill (scores 0.0-1.0)
|
|
50
|
+
- `SRSNavigator` — Review cards scored by overdueness, interval recency, and **backlog pressure** (scores 0.5-1.0)
|
|
47
51
|
- `HardcodedOrderNavigator` — Fixed sequence defined by course author
|
|
48
52
|
- `CompositeGenerator` — Merges multiple generators with frequency boost
|
|
49
53
|
|
|
54
|
+
#### SRS Backlog Pressure
|
|
55
|
+
|
|
56
|
+
The SRS generator implements a self-regulating **backlog pressure** mechanism that prevents review pile-up while maintaining healthy new/review balance:
|
|
57
|
+
|
|
58
|
+
- **Healthy backlog** (≤20 due reviews): No pressure boost, scores 0.5-0.95. New content (ELO) naturally dominates.
|
|
59
|
+
- **Elevated backlog** (40 due): +0.25 boost, scores 0.75-1.0. Reviews compete with new cards.
|
|
60
|
+
- **High backlog** (60+ due): +0.50 boost (max), scores 0.95-1.0. Reviews take priority.
|
|
61
|
+
|
|
62
|
+
This treats SRS scheduling times as **eligibility dates** rather than hard due dates—reviewing slightly later may be optimal. The system maintains a healthy backlog rather than always clearing to zero (avoiding "Anki death spiral").
|
|
63
|
+
|
|
64
|
+
Configuration via strategy `serializedData`:
|
|
65
|
+
```json
|
|
66
|
+
{ "healthyBacklog": 20 }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
See `todo-review-adaptation.md` for planned per-user adaptation extensions.
|
|
70
|
+
|
|
50
71
|
### CardFilter
|
|
51
72
|
|
|
52
73
|
Transforms card scores (pure function, no side effects):
|
|
@@ -82,7 +103,7 @@ class Pipeline {
|
|
|
82
103
|
)
|
|
83
104
|
|
|
84
105
|
async getWeightedCards(limit: number): Promise<WeightedCard[]> {
|
|
85
|
-
// Build shared context (user ELO, etc.)
|
|
106
|
+
// Build shared context (user ELO, orchestration context, etc.)
|
|
86
107
|
const context = await this.buildContext();
|
|
87
108
|
|
|
88
109
|
// Generate candidates
|
|
@@ -104,7 +125,7 @@ class Pipeline {
|
|
|
104
125
|
```
|
|
105
126
|
|
|
106
127
|
**Responsibilities:**
|
|
107
|
-
- **Context building** — Fetches shared data (user ELO) once for all strategies
|
|
128
|
+
- **Context building** — Fetches shared data (user ELO, orchestration context) once for all strategies
|
|
108
129
|
- **Data hydration** — Pre-fetches commonly needed data (tags) in batch queries
|
|
109
130
|
- **Filter orchestration** — Applies filters in sequence, accumulating provenance
|
|
110
131
|
- **Result selection** — Removes zero-scores, sorts, and returns top N
|
|
@@ -170,7 +191,8 @@ provenance: [
|
|
|
170
191
|
strategyId: 'NAVIGATION_STRATEGY-hierarchy-phonics',
|
|
171
192
|
action: 'passed',
|
|
172
193
|
score: 0.85,
|
|
173
|
-
reason: 'Prerequisites met, tags: letter-sounds'
|
|
194
|
+
reason: 'Prerequisites met, tags: letter-sounds',
|
|
195
|
+
deviation: 0.23 // User's position on bell curve for this strategy
|
|
174
196
|
},
|
|
175
197
|
{
|
|
176
198
|
strategy: 'eloDistance',
|
|
@@ -185,6 +207,171 @@ provenance: [
|
|
|
185
207
|
|
|
186
208
|
Use `getCardOrigin(card)` to extract 'new', 'review', or 'failed' from provenance.
|
|
187
209
|
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Evolutionary Orchestration
|
|
213
|
+
|
|
214
|
+
The orchestration layer enables strategies to **learn optimal weights** from observed
|
|
215
|
+
learning outcomes. Instead of fixed configuration, strategies carry **learnable weights**
|
|
216
|
+
that automatically tune toward effectiveness.
|
|
217
|
+
|
|
218
|
+
### LearnableWeight
|
|
219
|
+
|
|
220
|
+
Every strategy can carry a learnable weight:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
interface LearnableWeight {
|
|
224
|
+
weight: number; // Peak value, 1.0 = neutral, range [0.1, 3.0]
|
|
225
|
+
confidence: number; // 0-1, controls exploration width
|
|
226
|
+
sampleSize: number; // Total observations for this strategy
|
|
227
|
+
|
|
228
|
+
history?: Array<{ // Optional: for visualization
|
|
229
|
+
timestamp: string;
|
|
230
|
+
weight: number;
|
|
231
|
+
confidence: number;
|
|
232
|
+
gradient: number;
|
|
233
|
+
}>;
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Strategies extend with:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
interface ContentNavigationStrategyData {
|
|
241
|
+
// ... existing fields ...
|
|
242
|
+
learnable?: LearnableWeight; // Omitted = default weight 1.0
|
|
243
|
+
staticWeight?: boolean; // If true, not subject to learning
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Deviation-Based Weight Distribution
|
|
248
|
+
|
|
249
|
+
Each user experiences a **stable deviation** from the peak weight:
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
effectiveWeight(user, strategy) = peakWeight + deviation * spread
|
|
253
|
+
|
|
254
|
+
where:
|
|
255
|
+
deviation = hash(userId, strategyId, salt) → [-1, 1] // stable per user
|
|
256
|
+
spread = max(MIN_SPREAD, (1 - confidence) * MAX_SPREAD)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Key insight:** Deviation is constant for a user. As confidence grows and spread
|
|
260
|
+
shrinks, all users are pulled toward the optimal peak.
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// Example: Low confidence = wide exploration
|
|
264
|
+
{ weight: 1.0, confidence: 0.2 }
|
|
265
|
+
→ spread = 0.8 * MAX_SPREAD
|
|
266
|
+
→ users range from weight 0.6 to 1.4
|
|
267
|
+
|
|
268
|
+
// Example: High confidence = narrow convergence
|
|
269
|
+
{ weight: 1.2, confidence: 0.9 }
|
|
270
|
+
→ spread = 0.1 * MAX_SPREAD
|
|
271
|
+
→ users cluster around weight 1.15 to 1.25
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Outcome Recording
|
|
275
|
+
|
|
276
|
+
At the end of each learning period, user outcomes are recorded:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
interface UserOutcomeRecord {
|
|
280
|
+
_id: `USER_OUTCOME::${courseId}::${periodId}`;
|
|
281
|
+
docType: DocType.USER_OUTCOME;
|
|
282
|
+
userId: string;
|
|
283
|
+
courseId: string;
|
|
284
|
+
periodStart: string;
|
|
285
|
+
periodEnd: string;
|
|
286
|
+
strategyExposures: Array<{
|
|
287
|
+
strategyId: string;
|
|
288
|
+
deviation: number; // User's stable deviation for this strategy
|
|
289
|
+
}>;
|
|
290
|
+
outcomeValue: number; // 0-1 learning outcome signal
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The outcome signal combines multiple factors:
|
|
295
|
+
- Accuracy within target zone (not too easy, not too hard)
|
|
296
|
+
- ELO progression
|
|
297
|
+
- Session completion
|
|
298
|
+
|
|
299
|
+
### Gradient Learning
|
|
300
|
+
|
|
301
|
+
The system discovers optimal weights by correlating **deviation with outcomes**
|
|
302
|
+
across users:
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
+deviation → +outcome = positive gradient → increase peak weight
|
|
306
|
+
+deviation → -outcome = negative gradient → decrease peak weight
|
|
307
|
+
flat gradient = at optimum, increase confidence
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Linear regression on (deviation, outcome) pairs produces:
|
|
311
|
+
- **Gradient**: Direction to adjust peak
|
|
312
|
+
- **R²**: Signal quality (high = consistent effect)
|
|
313
|
+
- **Sample size**: Update confidence
|
|
314
|
+
|
|
315
|
+
### Weight Update Cycle
|
|
316
|
+
|
|
317
|
+
Periodically (or on-demand), the system updates strategy weights:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
async function runPeriodUpdate(courseId: string): Promise<void> {
|
|
321
|
+
for (const strategy of await getLearnableStrategies(courseId)) {
|
|
322
|
+
const observations = await aggregateOutcomesForGradient(strategy);
|
|
323
|
+
const { gradient, rSquared } = computeStrategyGradient(observations);
|
|
324
|
+
await updateStrategyWeight(strategy, gradient, rSquared);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
**Update rules:**
|
|
330
|
+
- Positive gradient → increase peak weight
|
|
331
|
+
- Negative gradient → decrease peak weight
|
|
332
|
+
- Consistent observations → increase confidence
|
|
333
|
+
- Noisy observations → decrease confidence
|
|
334
|
+
|
|
335
|
+
### Observability
|
|
336
|
+
|
|
337
|
+
The orchestration layer exposes API endpoints for monitoring:
|
|
338
|
+
|
|
339
|
+
| Endpoint | Purpose |
|
|
340
|
+
|----------|---------|
|
|
341
|
+
| `GET /orchestration/:courseId/state` | All learning states |
|
|
342
|
+
| `GET /orchestration/:courseId/weights` | Current weights summary |
|
|
343
|
+
| `GET /orchestration/:courseId/strategy/:id/history` | Weight trajectory over time |
|
|
344
|
+
| `GET /orchestration/:courseId/strategy/:id/scatter` | Deviation vs outcome data |
|
|
345
|
+
| `GET /orchestration/:courseId/strategy/:id/distribution` | Bell curve visualization |
|
|
346
|
+
| `POST /orchestration/:courseId/update` | Trigger period update |
|
|
347
|
+
|
|
348
|
+
An admin dashboard in platform-ui visualizes strategy weights, confidence,
|
|
349
|
+
gradient direction, and historical trajectories.
|
|
350
|
+
|
|
351
|
+
### Lifecycle
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
New strategy: Low confidence → wide spread → noisy gradient → big adjustments
|
|
355
|
+
Learning: Gradient visible → peak drifts → confidence grows → spread shrinks
|
|
356
|
+
Converged: High confidence → minimum spread → flat gradient → stable
|
|
357
|
+
Disturbed: Gradient reappears → peak drifts → adapts to new optimal
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Static vs Learnable
|
|
361
|
+
|
|
362
|
+
Set `staticWeight: true` for foundational strategies that should not be tuned:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
{
|
|
366
|
+
strategyType: 'hierarchyDefinition',
|
|
367
|
+
name: 'Core Prerequisites',
|
|
368
|
+
staticWeight: true, // Never tuned by orchestration
|
|
369
|
+
// learnable field ignored
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
188
375
|
## Creating New Strategies
|
|
189
376
|
|
|
190
377
|
### Generator
|
|
@@ -249,6 +436,8 @@ class MyFilter extends ContentNavigator implements CardFilter {
|
|
|
249
436
|
|
|
250
437
|
Register in `NavigatorRoles` as `NavigatorRole.FILTER`.
|
|
251
438
|
|
|
439
|
+
---
|
|
440
|
+
|
|
252
441
|
## Strategy State Storage
|
|
253
442
|
|
|
254
443
|
Strategies can persist user-scoped state (preferences, learned patterns, temporal tracking)
|
|
@@ -294,7 +483,7 @@ interface StrategyStateDoc<T> {
|
|
|
294
483
|
courseId: string;
|
|
295
484
|
strategyKey: string;
|
|
296
485
|
data: T; // Strategy-specific payload
|
|
297
|
-
updatedAt: string;
|
|
486
|
+
updatedAt: string;
|
|
298
487
|
}
|
|
299
488
|
```
|
|
300
489
|
|
|
@@ -332,6 +521,8 @@ return { ...card, score: card.score * multiplier };
|
|
|
332
521
|
- All sliders share global max for consistent visual comparison
|
|
333
522
|
- Writes to strategy state via `userDB.putStrategyState()`
|
|
334
523
|
|
|
524
|
+
---
|
|
525
|
+
|
|
335
526
|
## File Reference
|
|
336
527
|
|
|
337
528
|
| File | Purpose |
|
|
@@ -342,8 +533,8 @@ return { ...card, score: card.score * multiplier };
|
|
|
342
533
|
| `core/navigators/Pipeline.ts` | Pipeline orchestration |
|
|
343
534
|
| `core/navigators/PipelineAssembler.ts` | Builds Pipeline from strategy docs |
|
|
344
535
|
| `core/navigators/CompositeGenerator.ts` | Merges multiple generators |
|
|
345
|
-
| `core/navigators/elo.ts` | ELO generator |
|
|
346
|
-
| `core/navigators/srs.ts` | SRS generator |
|
|
536
|
+
| `core/navigators/generators/elo.ts` | ELO generator |
|
|
537
|
+
| `core/navigators/generators/srs.ts` | SRS generator (with backlog pressure) |
|
|
347
538
|
| `core/navigators/hardcodedOrder.ts` | Fixed-order generator |
|
|
348
539
|
| `core/navigators/hierarchyDefinition.ts` | Prerequisite filter |
|
|
349
540
|
| `core/navigators/interferenceMitigator.ts` | Interference filter |
|
|
@@ -355,9 +546,19 @@ return { ...card, score: card.score * multiplier };
|
|
|
355
546
|
| `core/navigators/inferredPreference.ts` | Inferred preference navigator (stub) |
|
|
356
547
|
| `core/types/strategyState.ts` | `StrategyStateDoc`, `StrategyStateId` |
|
|
357
548
|
| `impl/couch/courseDB.ts` | `createNavigator()` entry point |
|
|
549
|
+
| `core/orchestration/index.ts` | OrchestrationContext, deviation logic |
|
|
550
|
+
| `core/orchestration/gradient.ts` | Gradient computation |
|
|
551
|
+
| `core/orchestration/learning.ts` | Weight updates, period orchestration |
|
|
552
|
+
| `core/orchestration/signal.ts` | Outcome signal computation |
|
|
553
|
+
| `core/orchestration/recording.ts` | User outcome recording |
|
|
554
|
+
| `core/types/learningState.ts` | `StrategyLearningState` |
|
|
555
|
+
| `core/types/userOutcome.ts` | `UserOutcomeRecord` |
|
|
556
|
+
| `express/routes/orchestration.ts` | Observability API endpoints |
|
|
358
557
|
|
|
359
558
|
## Related Documentation
|
|
360
559
|
|
|
361
560
|
- `todo-strategy-authoring.md` — UX and DX for authoring strategies
|
|
362
|
-
- `todo-
|
|
561
|
+
- `todo-review-adaptation.md` — Planned per-user review urgency adaptation
|
|
562
|
+
- `future-orchestration-vision.md` — Long-term adaptive strategy vision (beyond current implementation)
|
|
363
563
|
- `devlog/1004` — Implementation details for tag hydration optimization
|
|
564
|
+
- `devlog/1032-orchestrator` — Evolutionary orchestration implementation details
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Future Work: Dynamic Review Urgency Adaptation
|
|
2
|
+
|
|
3
|
+
This document outlines planned extensions to the SRS backlog pressure system for adaptive review urgency tuning.
|
|
4
|
+
|
|
5
|
+
## Background
|
|
6
|
+
|
|
7
|
+
The SRS generator now implements **backlog pressure** — a mechanism that boosts review urgency when the number of due reviews exceeds a "healthy" threshold (default: 20). This ensures reviews don't pile up indefinitely while maintaining a healthy mix of new and review content.
|
|
8
|
+
|
|
9
|
+
See implementation: `core/navigators/generators/srs.ts`
|
|
10
|
+
|
|
11
|
+
This document describes two extension directions:
|
|
12
|
+
1. **Global/Orchestration Layer** — Course-wide tuning via learnable weights
|
|
13
|
+
2. **Per-User Adaptation** — Individual urgency multipliers based on review outcomes
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Extension 1: Orchestrator-Tuned Generator Weights
|
|
18
|
+
|
|
19
|
+
### Current State
|
|
20
|
+
|
|
21
|
+
The orchestration layer (`core/orchestration/`) already supports learnable weights:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
interface LearnableWeight {
|
|
25
|
+
weight: number; // Peak value, 1.0 = neutral
|
|
26
|
+
confidence: number; // 0-1, controls exploration width
|
|
27
|
+
sampleSize: number; // Observations collected
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
When a strategy has `learnable` set and `staticWeight: false`, the orchestrator:
|
|
32
|
+
1. Distributes users across a weight range (bell curve around peak)
|
|
33
|
+
2. Records learning outcomes per user
|
|
34
|
+
3. Correlates user deviation with outcomes via gradient estimation
|
|
35
|
+
4. Updates peak weight toward observed optimum
|
|
36
|
+
|
|
37
|
+
### Application to Review Balance
|
|
38
|
+
|
|
39
|
+
The orchestrator can tune the ELO vs SRS balance by experimenting with their weights.
|
|
40
|
+
|
|
41
|
+
In `CompositeGenerator.getWeightedCards()`, weights are already applied:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
let weight = gen.learnable?.weight ?? 1.0;
|
|
45
|
+
if (gen.learnable && !gen.staticWeight && context.orchestration) {
|
|
46
|
+
weight = context.orchestration.getEffectiveWeight(strategyId, gen.learnable);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If SRS has `learnable: { weight: 1.0, confidence: 0.5, sampleSize: 0 }`, the orchestrator will try variations (0.8, 1.2, etc.) and correlate with learning outcomes.
|
|
51
|
+
|
|
52
|
+
### Required Work
|
|
53
|
+
|
|
54
|
+
- [ ] Ensure default ELO and SRS strategy documents include `learnable` config
|
|
55
|
+
- [ ] Verify outcome correlation tracks success/failure per card source (generator provenance)
|
|
56
|
+
- [ ] Consider weight bounds (e.g., SRS weight ∈ [0.5, 2.0]) to prevent extreme tuning
|
|
57
|
+
- [ ] Test interaction with backlog pressure — may need to mark backlog-pressure-derived scores as non-learnable
|
|
58
|
+
|
|
59
|
+
### Risks
|
|
60
|
+
|
|
61
|
+
- Orchestrator experiments could override backlog pressure intent
|
|
62
|
+
- May need to separate "base urgency" (learnable) from "backlog pressure" (not learnable)
|
|
63
|
+
|
|
64
|
+
### Priority
|
|
65
|
+
|
|
66
|
+
**Low** — The backlog pressure mechanism should handle balance well. Orchestrator tuning is a refinement layer that can be enabled later.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Extension 2: Per-User Review Urgency Adaptation
|
|
71
|
+
|
|
72
|
+
### Concept
|
|
73
|
+
|
|
74
|
+
Users have different memory characteristics. A "forgetful" user needs more review pressure; a "retentive" user needs less. Rather than one-size-fits-all, maintain a per-user `reviewUrgencyMultiplier` that adapts based on review outcomes.
|
|
75
|
+
|
|
76
|
+
### Proposed Data Model
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
interface ReviewAdaptationState {
|
|
80
|
+
urgencyMultiplier: number; // Default 1.0, range [0.5, 2.0]
|
|
81
|
+
windowStart: string; // ISO timestamp for rolling window
|
|
82
|
+
windowFailures: number; // Failures in current window
|
|
83
|
+
windowSuccesses: number; // Successes in current window
|
|
84
|
+
lastUpdated: string; // ISO timestamp
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Update Rules
|
|
89
|
+
|
|
90
|
+
- On failed review: `multiplier = min(2.0, multiplier + 0.05)`
|
|
91
|
+
- On successful review: `multiplier = max(0.5, multiplier - 0.01)`
|
|
92
|
+
|
|
93
|
+
**Asymmetry rationale**:
|
|
94
|
+
- Failures increase pressure quickly (5x faster than decrease)
|
|
95
|
+
- Successes slowly reduce pressure (avoid death spiral from one good session)
|
|
96
|
+
|
|
97
|
+
### Equilibrium Analysis
|
|
98
|
+
|
|
99
|
+
At equilibrium: `0.05 * failRate = 0.01 * (1 - failRate)`
|
|
100
|
+
|
|
101
|
+
Solving: `failRate = 0.167` (16.7%)
|
|
102
|
+
|
|
103
|
+
This means:
|
|
104
|
+
- User with 16.7% failure rate → multiplier stable
|
|
105
|
+
- User with >16.7% failure rate → multiplier increases (more review pressure)
|
|
106
|
+
- User with <16.7% failure rate → multiplier decreases (less review pressure)
|
|
107
|
+
|
|
108
|
+
The equilibrium threshold can be tuned by adjusting increment/decrement rates.
|
|
109
|
+
|
|
110
|
+
### Application in Scoring
|
|
111
|
+
|
|
112
|
+
In SRS urgency calculation:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const userAdaptation = await this.getReviewAdaptation();
|
|
116
|
+
const adaptedUrgency = baseUrgency * userAdaptation.urgencyMultiplier;
|
|
117
|
+
const score = Math.min(1.0, 0.5 + adaptedUrgency * 0.45 + backlogPressure);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Note: multiplier affects base urgency, not backlog pressure. This preserves the global backlog escape mechanism.
|
|
121
|
+
|
|
122
|
+
### Storage
|
|
123
|
+
|
|
124
|
+
Use strategy state storage (per-user, per-course, per-strategy):
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const state = await this.getStrategyState<ReviewAdaptationState>();
|
|
128
|
+
await this.putStrategyState({ ...state, urgencyMultiplier: newValue });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Implementation Sketch
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// In srs.ts
|
|
135
|
+
|
|
136
|
+
private async getReviewAdaptation(): Promise<ReviewAdaptationState> {
|
|
137
|
+
const state = await this.getStrategyState<ReviewAdaptationState>();
|
|
138
|
+
return state || {
|
|
139
|
+
urgencyMultiplier: 1.0,
|
|
140
|
+
windowStart: new Date().toISOString(),
|
|
141
|
+
windowFailures: 0,
|
|
142
|
+
windowSuccesses: 0,
|
|
143
|
+
lastUpdated: new Date().toISOString(),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Called after review outcome recorded (needs integration hook)
|
|
148
|
+
async updateReviewAdaptation(success: boolean): Promise<void> {
|
|
149
|
+
const state = await this.getReviewAdaptation();
|
|
150
|
+
|
|
151
|
+
if (success) {
|
|
152
|
+
state.urgencyMultiplier = Math.max(0.5, state.urgencyMultiplier - 0.01);
|
|
153
|
+
state.windowSuccesses++;
|
|
154
|
+
} else {
|
|
155
|
+
state.urgencyMultiplier = Math.min(2.0, state.urgencyMultiplier + 0.05);
|
|
156
|
+
state.windowFailures++;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
state.lastUpdated = new Date().toISOString();
|
|
160
|
+
await this.putStrategyState(state);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Required Work
|
|
165
|
+
|
|
166
|
+
- [ ] Add `getReviewAdaptation()` method to SRSNavigator
|
|
167
|
+
- [ ] Integrate `updateReviewAdaptation()` with card response recording flow
|
|
168
|
+
- [ ] Decide: Should multiplier affect backlog pressure or just base urgency?
|
|
169
|
+
- [ ] Consider reset behavior on long breaks (user returning after 6 months)
|
|
170
|
+
- [ ] Consider course-specific vs global multipliers
|
|
171
|
+
- [ ] Add provenance entry explaining multiplier contribution
|
|
172
|
+
|
|
173
|
+
### Open Questions
|
|
174
|
+
|
|
175
|
+
1. **Who calls `updateReviewAdaptation`?** — Needs hook into card response recording (currently in `UserDB.recordInteraction()` or similar)
|
|
176
|
+
|
|
177
|
+
2. **Rolling window reset?** — Should the window reset periodically to allow recovery from bad streaks?
|
|
178
|
+
|
|
179
|
+
3. **Visibility?** — Should users see their current multiplier? Could be motivating or discouraging.
|
|
180
|
+
|
|
181
|
+
4. **Initial calibration?** — New users start at 1.0. Should there be an initial calibration period with more aggressive updates?
|
|
182
|
+
|
|
183
|
+
### Priority
|
|
184
|
+
|
|
185
|
+
**Medium** — High value for learning outcomes, moderate implementation complexity. Requires integration with card response recording flow.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Implementation Order
|
|
190
|
+
|
|
191
|
+
1. ✅ **Backlog Pressure** (implemented) — Fixes immediate balance issue
|
|
192
|
+
2. **Per-User Adaptation** — High value, moderate complexity
|
|
193
|
+
3. **Orchestrator Tuning** — Lower priority if backlog pressure works well
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Related Files
|
|
198
|
+
|
|
199
|
+
| File | Purpose |
|
|
200
|
+
|------|---------|
|
|
201
|
+
| `core/navigators/generators/srs.ts` | SRS urgency calculation, backlog pressure |
|
|
202
|
+
| `core/orchestration/index.ts` | Orchestration context, deviation logic |
|
|
203
|
+
| `core/types/contentNavigationStrategy.ts` | Strategy config, learnable weights |
|
|
204
|
+
| `core/types/strategyState.ts` | Per-user strategy state storage |
|
|
205
|
+
| `impl/couch/userDB.ts` | User interaction recording |
|
|
@@ -53,7 +53,6 @@ interface HierarchyConfig {
|
|
|
53
53
|
prerequisites: {
|
|
54
54
|
[tagId: string]: TagPrerequisite[];
|
|
55
55
|
};
|
|
56
|
-
delegateStrategy?: string; // default: "elo"
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
interface TagPrerequisite {
|
|
@@ -75,8 +74,7 @@ interface TagPrerequisite {
|
|
|
75
74
|
"blends": [
|
|
76
75
|
{ "tag": "cvc-words", "masteryThreshold": { "minCount": 20 } }
|
|
77
76
|
]
|
|
78
|
-
}
|
|
79
|
-
"delegateStrategy": "elo"
|
|
77
|
+
}
|
|
80
78
|
}
|
|
81
79
|
```
|
|
82
80
|
|
|
@@ -99,7 +97,6 @@ interface InterferenceConfig {
|
|
|
99
97
|
minElapsedDays?: number;
|
|
100
98
|
};
|
|
101
99
|
defaultDecay?: number;
|
|
102
|
-
delegateStrategy?: string;
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
interface InterferenceGroup {
|
|
@@ -137,7 +134,6 @@ interface RelativePriorityConfig {
|
|
|
137
134
|
defaultPriority?: number;
|
|
138
135
|
combineMode?: 'max' | 'average' | 'min';
|
|
139
136
|
priorityInfluence?: number;
|
|
140
|
-
delegateStrategy?: string;
|
|
141
137
|
}
|
|
142
138
|
```
|
|
143
139
|
|
|
@@ -398,4 +394,10 @@ The NavigationStrategy editor is accessible in:
|
|
|
398
394
|
- `packages/db/src/core/navigators/hierarchyDefinition.ts` — Config interface reference
|
|
399
395
|
- `packages/db/src/core/navigators/interferenceMitigator.ts` — Config interface reference
|
|
400
396
|
- `packages/db/src/core/navigators/relativePriority.ts` — Config interface reference
|
|
401
|
-
- `packages/edit-ui/src/components/NavigationStrategy/` — UI components (all forms)
|
|
397
|
+
- `packages/edit-ui/src/components/NavigationStrategy/` — UI components (all forms)
|
|
398
|
+
|
|
399
|
+
## Related Documentation
|
|
400
|
+
|
|
401
|
+
- `navigators-architecture.md` — Pipeline architecture and strategy framework
|
|
402
|
+
- `future-orchestration-vision.md` — Long-term adaptive strategy vision
|
|
403
|
+
- `devlog/1032-orchestrator` — Evolutionary orchestration implementation details
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.1.
|
|
7
|
+
"version": "0.1.25",
|
|
8
8
|
"description": "Database layer for vue-skuilder",
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"module": "dist/index.mjs",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@nilock2/pouchdb-authentication": "^1.0.2",
|
|
51
|
-
"@vue-skuilder/common": "0.1.
|
|
51
|
+
"@vue-skuilder/common": "0.1.25",
|
|
52
52
|
"cross-fetch": "^4.1.0",
|
|
53
53
|
"moment": "^2.29.4",
|
|
54
54
|
"pouchdb": "^9.0.0",
|
|
@@ -62,5 +62,5 @@
|
|
|
62
62
|
"vite": "^7.0.0",
|
|
63
63
|
"vitest": "^4.0.15"
|
|
64
64
|
},
|
|
65
|
-
"stableVersion": "0.1.
|
|
65
|
+
"stableVersion": "0.1.25"
|
|
66
66
|
}
|
package/src/core/index.ts
CHANGED
|
@@ -4,7 +4,9 @@ export * from './interfaces';
|
|
|
4
4
|
export * from './types/types-legacy';
|
|
5
5
|
export * from './types/user';
|
|
6
6
|
export * from './types/strategyState';
|
|
7
|
+
export * from './types/userOutcome';
|
|
7
8
|
export * from '../util/Loggable';
|
|
8
9
|
export * from './util';
|
|
9
10
|
export * from './navigators';
|
|
10
11
|
export * from './bulkImport';
|
|
12
|
+
export * from './orchestration';
|
|
@@ -4,6 +4,7 @@ import { StudentClassroomDB } from '../../impl/couch/classroomDB';
|
|
|
4
4
|
import { WeightedCard } from '../navigators';
|
|
5
5
|
import { TagFilter, hasActiveFilter } from '@vue-skuilder/common';
|
|
6
6
|
import { TagFilteredContentSource } from '../../study/TagFilteredContentSource';
|
|
7
|
+
import { OrchestrationContext } from '../orchestration';
|
|
7
8
|
|
|
8
9
|
export type StudySessionFailedItem = StudySessionFailedNewItem | StudySessionFailedReviewItem;
|
|
9
10
|
|
|
@@ -72,6 +73,12 @@ export interface StudyContentSource {
|
|
|
72
73
|
* @returns Cards sorted by score descending
|
|
73
74
|
*/
|
|
74
75
|
getWeightedCards(limit: number): Promise<WeightedCard[]>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get the orchestration context for this source.
|
|
79
|
+
* Used for recording learning outcomes.
|
|
80
|
+
*/
|
|
81
|
+
getOrchestrationContext?(): Promise<OrchestrationContext>;
|
|
75
82
|
}
|
|
76
83
|
// #endregion docs_StudyContentSource
|
|
77
84
|
|