@vue-skuilder/db 0.1.17 → 0.1.20

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.
Files changed (92) hide show
  1. package/dist/{userDB-BqwxtJ_7.d.mts → classroomDB-CZdMBiTU.d.ts} +427 -104
  2. package/dist/{userDB-DNa0XPtn.d.ts → classroomDB-PxDZTky3.d.cts} +427 -104
  3. package/dist/core/index.d.cts +304 -0
  4. package/dist/core/index.d.ts +237 -25
  5. package/dist/core/index.js +2246 -118
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +2235 -114
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-VlngD19_.d.mts → dataLayerProvider-D0MoZMjH.d.cts} +1 -1
  10. package/dist/{dataLayerProvider-BV5iZqt_.d.ts → dataLayerProvider-D8o6ZnKW.d.ts} +1 -1
  11. package/dist/impl/couch/{index.d.mts → index.d.cts} +47 -5
  12. package/dist/impl/couch/index.d.ts +46 -4
  13. package/dist/impl/couch/index.js +2250 -134
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +2212 -97
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/{index.d.mts → index.d.cts} +6 -6
  18. package/dist/impl/static/index.d.ts +5 -5
  19. package/dist/impl/static/index.js +1950 -143
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +1922 -117
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/{index-Bmll7Xse.d.mts → index-B_j6u5E4.d.cts} +1 -1
  24. package/dist/{index-CD8BZz2k.d.ts → index-Dj0SEgk3.d.ts} +1 -1
  25. package/dist/{index.d.mts → index.d.cts} +97 -13
  26. package/dist/index.d.ts +96 -12
  27. package/dist/index.js +2439 -180
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +2386 -135
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/pouch/index.js +3 -3
  32. package/dist/{types-Dbp5DaRR.d.mts → types-Bn0itutr.d.cts} +1 -1
  33. package/dist/{types-CewsN87z.d.ts → types-DQaXnuoc.d.ts} +1 -1
  34. package/dist/{types-legacy-6ettoclI.d.ts → types-legacy-DDY4N-Uq.d.cts} +3 -1
  35. package/dist/{types-legacy-6ettoclI.d.mts → types-legacy-DDY4N-Uq.d.ts} +3 -1
  36. package/dist/util/packer/{index.d.mts → index.d.cts} +3 -3
  37. package/dist/util/packer/index.d.ts +3 -3
  38. package/dist/util/packer/index.js.map +1 -1
  39. package/dist/util/packer/index.mjs.map +1 -1
  40. package/docs/brainstorm-navigation-paradigm.md +369 -0
  41. package/docs/navigators-architecture.md +370 -0
  42. package/docs/todo-evolutionary-orchestration.md +310 -0
  43. package/docs/todo-nominal-tag-types.md +121 -0
  44. package/docs/todo-strategy-authoring.md +401 -0
  45. package/eslint.config.mjs +1 -1
  46. package/package.json +9 -4
  47. package/src/core/index.ts +1 -0
  48. package/src/core/interfaces/contentSource.ts +88 -4
  49. package/src/core/interfaces/courseDB.ts +13 -0
  50. package/src/core/interfaces/navigationStrategyManager.ts +0 -5
  51. package/src/core/interfaces/userDB.ts +32 -0
  52. package/src/core/navigators/CompositeGenerator.ts +268 -0
  53. package/src/core/navigators/Pipeline.ts +318 -0
  54. package/src/core/navigators/PipelineAssembler.ts +194 -0
  55. package/src/core/navigators/elo.ts +104 -15
  56. package/src/core/navigators/filters/eloDistance.ts +132 -0
  57. package/src/core/navigators/filters/index.ts +9 -0
  58. package/src/core/navigators/filters/types.ts +115 -0
  59. package/src/core/navigators/filters/userTagPreference.ts +232 -0
  60. package/src/core/navigators/generators/index.ts +2 -0
  61. package/src/core/navigators/generators/types.ts +107 -0
  62. package/src/core/navigators/hardcodedOrder.ts +111 -12
  63. package/src/core/navigators/hierarchyDefinition.ts +266 -0
  64. package/src/core/navigators/index.ts +404 -3
  65. package/src/core/navigators/inferredPreference.ts +107 -0
  66. package/src/core/navigators/interferenceMitigator.ts +355 -0
  67. package/src/core/navigators/relativePriority.ts +255 -0
  68. package/src/core/navigators/srs.ts +195 -0
  69. package/src/core/navigators/userGoal.ts +136 -0
  70. package/src/core/types/strategyState.ts +84 -0
  71. package/src/core/types/types-legacy.ts +2 -0
  72. package/src/impl/common/BaseUserDB.ts +74 -7
  73. package/src/impl/couch/adminDB.ts +1 -2
  74. package/src/impl/couch/classroomDB.ts +51 -0
  75. package/src/impl/couch/courseDB.ts +147 -49
  76. package/src/impl/static/courseDB.ts +11 -4
  77. package/src/study/SessionController.ts +149 -1
  78. package/src/study/TagFilteredContentSource.ts +255 -0
  79. package/src/study/index.ts +1 -0
  80. package/src/util/dataDirectory.test.ts +51 -22
  81. package/src/util/logger.ts +0 -1
  82. package/tests/core/navigators/CompositeGenerator.test.ts +455 -0
  83. package/tests/core/navigators/Pipeline.test.ts +406 -0
  84. package/tests/core/navigators/PipelineAssembler.test.ts +351 -0
  85. package/tests/core/navigators/SRSNavigator.test.ts +344 -0
  86. package/tests/core/navigators/eloDistanceFilter.test.ts +192 -0
  87. package/tests/core/navigators/navigators.test.ts +710 -0
  88. package/tsconfig.json +1 -1
  89. package/vitest.config.ts +29 -0
  90. package/dist/core/index.d.mts +0 -92
  91. /package/dist/{SyncStrategy-CyATpyLQ.d.mts → SyncStrategy-CyATpyLQ.d.cts} +0 -0
  92. /package/dist/pouch/{index.d.mts → index.d.cts} +0 -0
@@ -0,0 +1,370 @@
1
+ # Navigation Strategy Architecture
2
+
3
+ ## Overview
4
+
5
+ The navigation strategy system selects and scores cards for study sessions. It uses a
6
+ **Pipeline architecture** where generators produce candidates and filters transform scores.
7
+
8
+ ## Core Concepts
9
+
10
+ ### WeightedCard
11
+
12
+ A card with a suitability score, audit trail, and pre-fetched data:
13
+
14
+ ```typescript
15
+ interface WeightedCard {
16
+ cardId: string;
17
+ courseId: string;
18
+ score: number; // 0-1 suitability score
19
+ provenance: StrategyContribution[]; // Audit trail
20
+ tags?: string[]; // Pre-fetched tags (hydrated by Pipeline)
21
+ }
22
+
23
+ interface StrategyContribution {
24
+ strategy: string; // Type: 'elo', 'srs', 'hierarchyDefinition'
25
+ strategyName: string; // Human-readable: "ELO (default)"
26
+ strategyId: string; // Document ID: 'NAVIGATION_STRATEGY-ELO-default'
27
+ action: 'generated' | 'passed' | 'boosted' | 'penalized';
28
+ score: number; // Score after this strategy
29
+ reason: string; // Human-readable explanation
30
+ }
31
+ ```
32
+
33
+ ### CardGenerator
34
+
35
+ Produces candidate cards with initial scores:
36
+
37
+ ```typescript
38
+ interface CardGenerator {
39
+ name: string;
40
+ getWeightedCards(limit: number, context: GeneratorContext): Promise<WeightedCard[]>;
41
+ }
42
+ ```
43
+
44
+ **Implementations:**
45
+ - `ELONavigator` — New cards scored by ELO proximity to user skill
46
+ - `SRSNavigator` — Review cards scored by overdueness and interval recency
47
+ - `HardcodedOrderNavigator` — Fixed sequence defined by course author
48
+ - `CompositeGenerator` — Merges multiple generators with frequency boost
49
+
50
+ ### CardFilter
51
+
52
+ Transforms card scores (pure function, no side effects):
53
+
54
+ ```typescript
55
+ interface CardFilter {
56
+ name: string;
57
+ transform(cards: WeightedCard[], context: FilterContext): Promise<WeightedCard[]>;
58
+ }
59
+ ```
60
+
61
+ Filters receive cards with pre-hydrated data (e.g., `card.tags`) from Pipeline, eliminating
62
+ redundant database queries.
63
+
64
+ **Implementations:**
65
+ - `HierarchyDefinitionNavigator` — Gates cards by prerequisite mastery (score=0 if locked)
66
+ - `InterferenceMitigatorNavigator` — Reduces scores for confusable content
67
+ - `RelativePriorityNavigator` — Boosts scores for high-utility content
68
+ - `UserTagPreferenceFilter` — Applies user-configured tag preferences (path constraints)
69
+ - `createEloDistanceFilter()` — Penalizes cards far from user's current ELO
70
+
71
+ ### Pipeline
72
+
73
+ Orchestrates generator, data hydration, and filters:
74
+
75
+ ```typescript
76
+ class Pipeline {
77
+ constructor(
78
+ generator: CardGenerator,
79
+ filters: CardFilter[],
80
+ user: UserDBInterface,
81
+ course: CourseDBInterface
82
+ )
83
+
84
+ async getWeightedCards(limit: number): Promise<WeightedCard[]> {
85
+ // Build shared context (user ELO, etc.)
86
+ const context = await this.buildContext();
87
+
88
+ // Generate candidates
89
+ let cards = await this.generator.getWeightedCards(fetchLimit, context);
90
+
91
+ // Hydrate shared data (tags, etc.) in single batch query
92
+ cards = await this.hydrateTags(cards);
93
+
94
+ // Apply filters sequentially
95
+ for (const filter of this.filters) {
96
+ cards = await filter.transform(cards, context);
97
+ }
98
+
99
+ return cards.filter(c => c.score > 0)
100
+ .sort((a, b) => b.score - a.score)
101
+ .slice(0, limit);
102
+ }
103
+ }
104
+ ```
105
+
106
+ **Responsibilities:**
107
+ - **Context building** — Fetches shared data (user ELO) once for all strategies
108
+ - **Data hydration** — Pre-fetches commonly needed data (tags) in batch queries
109
+ - **Filter orchestration** — Applies filters in sequence, accumulating provenance
110
+ - **Result selection** — Removes zero-scores, sorts, and returns top N
111
+
112
+ ## Pipeline Assembly
113
+
114
+ `PipelineAssembler` builds pipelines from strategy documents:
115
+
116
+ ```typescript
117
+ const assembler = new PipelineAssembler();
118
+ const { pipeline, warnings } = await assembler.assemble({
119
+ strategies: allStrategies,
120
+ user,
121
+ course,
122
+ });
123
+ ```
124
+
125
+ Assembly logic:
126
+ 1. Separate strategies into generators and filters by `NavigatorRole`
127
+ 2. Instantiate generators — wrap multiple in `CompositeGenerator`
128
+ 3. Instantiate filters — sorted alphabetically for determinism
129
+ 4. Return `Pipeline(generator, filters)`
130
+
131
+ If no strategies are configured, `courseDB.createNavigator()` returns a default pipeline:
132
+ ```typescript
133
+ Pipeline(
134
+ CompositeGenerator([ELONavigator, SRSNavigator]),
135
+ [eloDistanceFilter]
136
+ )
137
+ ```
138
+
139
+ ## Score Semantics
140
+
141
+ | Score | Meaning |
142
+ |-------|---------|
143
+ | 1.0 | Fully suitable |
144
+ | 0.5 | Neutral |
145
+ | 0.0 | Exclude (hard filter) |
146
+ | 0.x | Proportional suitability |
147
+
148
+ **All filters are multipliers.** This means:
149
+ - Filter order doesn't affect final scores (multiplication is commutative)
150
+ - Score 0 from any filter excludes the card
151
+ - Filters are applied alphabetically for determinism
152
+
153
+ ## Provenance Tracking
154
+
155
+ Each card's provenance shows how it was scored:
156
+
157
+ ```typescript
158
+ provenance: [
159
+ {
160
+ strategy: 'elo',
161
+ strategyName: 'ELO (default)',
162
+ strategyId: 'NAVIGATION_STRATEGY-ELO-default',
163
+ action: 'generated',
164
+ score: 0.85,
165
+ reason: 'ELO distance 75 (card: 1025, user: 1100), new card'
166
+ },
167
+ {
168
+ strategy: 'hierarchyDefinition',
169
+ strategyName: 'Hierarchy: Phonics Basics',
170
+ strategyId: 'NAVIGATION_STRATEGY-hierarchy-phonics',
171
+ action: 'passed',
172
+ score: 0.85,
173
+ reason: 'Prerequisites met, tags: letter-sounds'
174
+ },
175
+ {
176
+ strategy: 'eloDistance',
177
+ strategyName: 'ELO Distance Filter',
178
+ strategyId: 'ELO_DISTANCE_FILTER',
179
+ action: 'penalized',
180
+ score: 0.72,
181
+ reason: 'ELO distance 150 (card: 1150, user: 1000) → 0.85x'
182
+ }
183
+ ]
184
+ ```
185
+
186
+ Use `getCardOrigin(card)` to extract 'new', 'review', or 'failed' from provenance.
187
+
188
+ ## Creating New Strategies
189
+
190
+ ### Generator
191
+
192
+ ```typescript
193
+ class MyGenerator extends ContentNavigator implements CardGenerator {
194
+ name = 'My Generator';
195
+
196
+ async getWeightedCards(limit: number, context?: GeneratorContext): Promise<WeightedCard[]> {
197
+ const candidates = await this.findCandidates(limit);
198
+
199
+ return candidates.map(c => ({
200
+ cardId: c.id,
201
+ courseId: this.course.getCourseID(),
202
+ score: this.computeScore(c),
203
+ provenance: [{
204
+ strategy: 'myGenerator',
205
+ strategyName: this.name,
206
+ strategyId: this.strategyId || 'MY_GENERATOR',
207
+ action: 'generated',
208
+ score: this.computeScore(c),
209
+ reason: 'Explanation here, new card'
210
+ }]
211
+ }));
212
+ }
213
+
214
+ // Legacy methods - stub or implement for backward compat
215
+ async getNewCards() { return []; }
216
+ async getPendingReviews() { return []; }
217
+ }
218
+ ```
219
+
220
+ Register in `NavigatorRoles` as `NavigatorRole.GENERATOR`.
221
+
222
+ ### Filter
223
+
224
+ ```typescript
225
+ class MyFilter extends ContentNavigator implements CardFilter {
226
+ name = 'My Filter';
227
+
228
+ async transform(cards: WeightedCard[], context: FilterContext): Promise<WeightedCard[]> {
229
+ return cards.map(card => {
230
+ const multiplier = this.computeMultiplier(card, context);
231
+ const newScore = card.score * multiplier;
232
+ const action = multiplier < 1 ? 'penalized' : multiplier > 1 ? 'boosted' : 'passed';
233
+
234
+ return {
235
+ ...card,
236
+ score: newScore,
237
+ provenance: [...card.provenance, {
238
+ strategy: 'myFilter',
239
+ strategyName: this.name,
240
+ strategyId: this.strategyId || 'MY_FILTER',
241
+ action,
242
+ score: newScore,
243
+ reason: 'Explanation here'
244
+ }]
245
+ };
246
+ });
247
+ }
248
+
249
+ // Legacy methods - filters don't generate cards
250
+ async getWeightedCards() { throw new Error('Use transform() via Pipeline'); }
251
+ async getNewCards() { return []; }
252
+ async getPendingReviews() { return []; }
253
+ }
254
+ ```
255
+
256
+ Register in `NavigatorRoles` as `NavigatorRole.FILTER`.
257
+
258
+ ## Strategy State Storage
259
+
260
+ Strategies can persist user-scoped state (preferences, learned patterns, temporal tracking)
261
+ using the `STRATEGY_STATE` document type in the user database.
262
+
263
+ ### Goals vs Preferences vs Inferred
264
+
265
+ The system distinguishes three types of user-scoped navigation data:
266
+
267
+ | Type | Defines | Example | Affects ELO | Implementation |
268
+ |------|---------|---------|-------------|----------------|
269
+ | **Goal** | Destination (what to learn) | "Master ear-training" | Yes | `userGoal.ts` (stub) |
270
+ | **Preference** | Path (how to learn) | "Skip text-heavy cards" | No | `filters/userTagPreference.ts` |
271
+ | **Inferred** | Learned patterns | "User prefers visual" | No | `inferredPreference.ts` (stub) |
272
+
273
+ - **Goals** redefine the optimization target — they scope which content matters for progress
274
+ - **Preferences** constrain the path — they affect card selection without changing progress tracking
275
+ - **Inferred** preferences are learned from behavior — they act as soft suggestions
276
+
277
+ See stub files for detailed architectural intent on goals and inferred preferences.
278
+
279
+ ### Storage API
280
+
281
+ `ContentNavigator` provides protected helper methods:
282
+
283
+ ```typescript
284
+ // Get this strategy's persisted state for the current course
285
+ protected async getStrategyState<T>(): Promise<T | null>
286
+
287
+ // Persist this strategy's state for the current course
288
+ protected async putStrategyState<T>(data: T): Promise<void>
289
+
290
+ // Override to customize the storage key (default: constructor name)
291
+ protected get strategyKey(): string
292
+ ```
293
+
294
+ ### Document Format
295
+
296
+ ```typescript
297
+ interface StrategyStateDoc<T> {
298
+ _id: StrategyStateId; // "STRATEGY_STATE::{courseId}::{strategyKey}"
299
+ docType: DocType.STRATEGY_STATE;
300
+ courseId: string;
301
+ strategyKey: string;
302
+ data: T; // Strategy-specific payload
303
+ updatedAt: string; // ISO timestamp
304
+ }
305
+ ```
306
+
307
+ ### Example: User Tag Preferences
308
+
309
+ `UserTagPreferenceFilter` reads user preferences from strategy state:
310
+
311
+ ```typescript
312
+ interface UserTagPreferenceState {
313
+ /**
314
+ * Tag-specific multipliers.
315
+ * - 0 = exclude (card score = 0)
316
+ * - 0.5 = penalize by 50%
317
+ * - 1.0 = neutral/no effect
318
+ * - 2.0 = 2x preference boost
319
+ * - Higher = stronger preference
320
+ */
321
+ boost: Record<string, number>;
322
+ updatedAt: string;
323
+ }
324
+
325
+ // In filter's transform():
326
+ const prefs = await this.getStrategyState<UserTagPreferenceState>();
327
+ if (!prefs || Object.keys(prefs.boost).length === 0) {
328
+ return cards; // No preferences configured
329
+ }
330
+
331
+ // Apply multipliers (max wins when multiple tags match)
332
+ const multiplier = computeMultiplier(cardTags, prefs.boost);
333
+ return { ...card, score: card.score * multiplier };
334
+ ```
335
+
336
+ **UI Component**: `packages/common-ui/src/components/UserTagPreferences.vue`
337
+ - Slider-based interface (0-2 default range, expandable to 10)
338
+ - All sliders share global max for consistent visual comparison
339
+ - Writes to strategy state via `userDB.putStrategyState()`
340
+
341
+ ## File Reference
342
+
343
+ | File | Purpose |
344
+ |------|---------|
345
+ | `core/navigators/index.ts` | `ContentNavigator`, `WeightedCard`, `NavigatorRole` |
346
+ | `core/navigators/generators/types.ts` | `CardGenerator`, `GeneratorContext` |
347
+ | `core/navigators/filters/types.ts` | `CardFilter`, `FilterContext` |
348
+ | `core/navigators/Pipeline.ts` | Pipeline orchestration |
349
+ | `core/navigators/PipelineAssembler.ts` | Builds Pipeline from strategy docs |
350
+ | `core/navigators/CompositeGenerator.ts` | Merges multiple generators |
351
+ | `core/navigators/elo.ts` | ELO generator |
352
+ | `core/navigators/srs.ts` | SRS generator |
353
+ | `core/navigators/hardcodedOrder.ts` | Fixed-order generator |
354
+ | `core/navigators/hierarchyDefinition.ts` | Prerequisite filter |
355
+ | `core/navigators/interferenceMitigator.ts` | Interference filter |
356
+ | `core/navigators/relativePriority.ts` | Priority filter |
357
+ | `core/navigators/filters/eloDistance.ts` | ELO distance filter |
358
+ | `core/navigators/filters/userTagPreference.ts` | User tag preference filter |
359
+ | `common-ui/.../UserTagPreferences.vue` | UI for tag preference sliders |
360
+ | `core/navigators/userGoal.ts` | User goal navigator (stub) |
361
+ | `core/navigators/inferredPreference.ts` | Inferred preference navigator (stub) |
362
+ | `core/types/strategyState.ts` | `StrategyStateDoc`, `StrategyStateId` |
363
+ | `impl/couch/courseDB.ts` | `createNavigator()` entry point |
364
+
365
+ ## Related Documentation
366
+
367
+ - `todo-pipeline-optimization.md` — Batch tag hydration implementation (✅ completed)
368
+ - `todo-strategy-authoring.md` — UX and DX for authoring strategies
369
+ - `todo-evolutionary-orchestration.md` — Long-term adaptive strategy vision
370
+ - `devlog/1004` — Implementation details for tag hydration optimization
@@ -0,0 +1,310 @@
1
+ # TODO: Evolutionary Orchestration Vision
2
+
3
+ ## Status: FOUNDATIONAL WORK COMPLETE, ORCHESTRATION NOT STARTED
4
+
5
+ > **Prerequisite:** `packages/db/docs/todo-naive-orchestration.md` — Pipeline assembly must be
6
+ > working before evolutionary selection can be layered on top. This document describes the
7
+ > "grander vision"; naive orchestration is the foundation.
8
+
9
+ This document tracks progress toward the "grander vision" of dynamic orchestration with
10
+ evolutionary pressures, as outlined in `u.3.strategic-nuggets.md`.
11
+
12
+ ## The Vision (Summary)
13
+
14
+ A self-improving courseware system where:
15
+
16
+ 1. **N strategies exist** — Each a hypothesis about effective content sequencing
17
+ 2. **M users exist** — Each with learning goals and measurable outcomes
18
+ 3. **Multi-arm bandit selection** — Strategies are applied based on confidence in their utility
19
+ 4. **Evolutionary pressure** — Effective strategies propagate, ineffective ones decay
20
+ 5. **Self-healing content** — System identifies barriers and incentivizes remediation
21
+
22
+ ---
23
+
24
+ ## Progress Assessment
25
+
26
+ ### ✅ COMPLETE: Strategy Infrastructure
27
+
28
+ | Component | Status | Notes |
29
+ |-----------|--------|-------|
30
+ | `WeightedCard` API | ✅ Done | Unified scored candidate model |
31
+ | `ContentNavigator` base class | ✅ Done | Extensible strategy framework |
32
+ | Delegate pattern composition | ✅ Done | Strategies can wrap/chain |
33
+ | `HierarchyDefinition` strategy | ✅ Done | Prerequisite gating |
34
+ | `InterferenceMitigator` strategy | ✅ Done | Confusable concept separation |
35
+ | `RelativePriority` strategy | ✅ Done | Utility-based content ordering |
36
+ | `SessionController` integration | ✅ Done | `getWeightedCards()` is live |
37
+ | Pipeline assembly | ❌ Not done | See `todo-naive-orchestration.md` |
38
+
39
+ **What this enables:**
40
+ - Multiple strategies can coexist and be configured per-course
41
+ - Strategies return graded suitability scores (0-1), not just binary include/exclude
42
+ - Composition allows layering strategies: `Priority(Interference(Hierarchy(ELO)))`
43
+
44
+ **What's missing for basic operation:**
45
+ - No pipeline configuration in CourseConfig (`navigationPipeline` field)
46
+ - No assembly logic to chain configured strategies
47
+ - Currently falls back to hard-coded ELO navigator
48
+
49
+ ---
50
+
51
+ ### 🟡 PARTIAL: Strategy State & Context
52
+
53
+ | Component | Status | Notes |
54
+ |-----------|--------|-------|
55
+ | Read user ELO (global + per-tag) | ✅ Done | Via `CourseRegistration.elo` |
56
+ | Read user history | ✅ Done | `getSeenCards()`, `getPendingReviews()` |
57
+ | Write strategy-specific state | ❌ Not done | See `todo-strategy-state-storage.md` |
58
+ | Temporal tracking | ❌ Not done | "When was tag X last introduced?" |
59
+
60
+ **Gap for evolutionary vision:**
61
+ - Strategies cannot yet persist their own learning/state
62
+ - No mechanism for tracking strategy effectiveness over time
63
+
64
+ ---
65
+
66
+ ### ❌ NOT STARTED: Multi-Arm Bandit Selection
67
+
68
+ The core orchestration logic described in the vision:
69
+
70
+ ```
71
+ each of N strategies divides into cohorts via some hashId collision metric
72
+ eg: h(strategyHash + slowRotationSalt) xor userHash
73
+ if resultant Ones are < utilityConfidence percent of total bits,
74
+ then include the strategy for the user
75
+ ```
76
+
77
+ **What's needed:**
78
+
79
+ 1. **Strategy Registry** — Central list of available strategies with metadata
80
+ 2. **Utility Confidence** — Per-strategy confidence score (0-1)
81
+ 3. **Cohort Assignment** — Deterministic but rotatable user-to-strategy mapping
82
+ 4. **Salt Rotation** — Periodic rotation to prevent user lock-in
83
+ 5. **Outcome Measurement** — Track goal achievement per cohort
84
+
85
+ **Proposed architecture:**
86
+
87
+ ```typescript
88
+ interface StrategyRegistryEntry {
89
+ strategyId: string;
90
+ strategyType: string;
91
+ config: unknown;
92
+
93
+ // Evolutionary metadata
94
+ utilityConfidence: number; // 0-1, probability of usefulness
95
+ createdAt: string;
96
+ lastMeasuredAt: string;
97
+ cohortSalt: string; // Rotated periodically
98
+
99
+ // Outcome tracking
100
+ metrics: {
101
+ usersExposed: number;
102
+ goalAchievementRate: number;
103
+ engagementScore: number;
104
+ progressRate: number;
105
+ };
106
+ }
107
+
108
+ interface Orchestrator {
109
+ // Determine which strategies apply to this user for this session
110
+ selectStrategies(userId: string, courseId: string): Promise<StrategyRegistryEntry[]>;
111
+
112
+ // Record outcome for evolutionary learning
113
+ recordOutcome(userId: string, strategyIds: string[], outcome: Outcome): Promise<void>;
114
+
115
+ // Rotate cohort assignments
116
+ rotateSalt(): Promise<void>;
117
+
118
+ // Prune ineffective strategies
119
+ pruneStrategies(minConfidence: number): Promise<void>;
120
+ }
121
+ ```
122
+
123
+ ---
124
+
125
+ ### ❌ NOT STARTED: Parameterizable / Programmable Strategies
126
+
127
+ The vision describes:
128
+
129
+ ```
130
+ would like to extend into parameterizable / programmable strategies.
131
+ eg, should be able to specify deps like:
132
+ `grade-{n}` & `geometry` -> `grade-{n+1}` & `geometry`
133
+ ```
134
+
135
+ **What's needed:**
136
+
137
+ 1. **Template syntax** — Pattern matching for tag relationships
138
+ 2. **Variable binding** — Extract and apply variables in prerequisite rules
139
+ 3. **Rule engine** — Evaluate parameterized rules against user state
140
+
141
+ **Example:**
142
+ ```typescript
143
+ interface ParameterizedPrerequisite {
144
+ pattern: "grade-{n} & {subject}";
145
+ implies: "grade-{n+1} & {subject}";
146
+ constraints: {
147
+ n: { type: "number", min: 1, max: 12 },
148
+ subject: { type: "tag-category", category: "academic-subject" }
149
+ };
150
+ }
151
+ ```
152
+
153
+ ---
154
+
155
+ ### ❌ NOT STARTED: Self-Healing Content
156
+
157
+ The meta-consideration from the vision:
158
+
159
+ ```
160
+ - identifying learning 'barriers' where substantive portion of cohort gets stuck or abandons
161
+ - surfacing that info in a useful way
162
+ - author aims to diagnose and remedy by providing intermediate content
163
+ ```
164
+
165
+ **What's needed:**
166
+
167
+ 1. **Barrier Detection** — Identify cards/tags where many users get stuck
168
+ 2. **Cohort Analysis** — Compare progress across strategy cohorts
169
+ 3. **Alert System** — Surface barriers to authors
170
+ 4. **Intervention Hooks** — Enable targeted content insertion
171
+ 5. **Feedback Loop** — Measure intervention effectiveness
172
+
173
+ **Signals for barrier detection:**
174
+ - High failure rate on specific cards
175
+ - Long dwell time before mastery
176
+ - Drop-off (users abandon course at specific points)
177
+ - ELO stagnation on specific tags
178
+
179
+ ---
180
+
181
+ ## Roadmap to Full Vision
182
+
183
+ ### Phase A: Foundation (COMPLETE ✅)
184
+
185
+ - [x] WeightedCard API
186
+ - [x] ContentNavigator framework
187
+ - [x] Core strategies (Hierarchy, Interference, RelativePriority)
188
+ - [x] SessionController integration
189
+
190
+ ### Phase B: Strategy Ecosystem (IN PROGRESS 🟡)
191
+
192
+ - [ ] SRS as ContentNavigator (`todo-srs-navigator.md`)
193
+ - [ ] Strategy authoring tools (`todo-strategy-authoring.md`)
194
+ - [ ] Strategy state storage (`todo-strategy-state-storage.md`)
195
+ - [ ] Provenance/audit trail (`todo-provenance.md`)
196
+
197
+ ### Phase C: Measurement Infrastructure (NOT STARTED)
198
+
199
+ - [ ] Define "learning outcome" metrics
200
+ - [ ] Instrument strategy usage per user
201
+ - [ ] Track outcome-to-strategy correlation
202
+ - [ ] Build cohort comparison views
203
+
204
+ ### Phase D: Multi-Arm Bandit Orchestrator (NOT STARTED)
205
+
206
+ - [ ] Strategy registry with utility confidence
207
+ - [ ] Cohort assignment algorithm
208
+ - [ ] Salt rotation mechanism
209
+ - [ ] Confidence update rules (Bayesian or frequentist)
210
+ - [ ] Orchestrator integration with SessionController
211
+
212
+ ### Phase E: Evolutionary Pressure (NOT STARTED)
213
+
214
+ - [ ] Strategy creation incentives
215
+ - [ ] Automatic strategy pruning
216
+ - [ ] Strategy mutation/variation generation
217
+ - [ ] A/B testing framework
218
+
219
+ ### Phase F: Self-Healing (NOT STARTED)
220
+
221
+ - [ ] Barrier detection pipeline
222
+ - [ ] Author alerting system
223
+ - [ ] Intervention recommendation engine
224
+ - [ ] Content gap analysis
225
+
226
+ ### Phase G: Programmable Strategies (NOT STARTED)
227
+
228
+ - [ ] Template syntax design
229
+ - [ ] Rule engine implementation
230
+ - [ ] Variable binding in prerequisites
231
+ - [ ] Cross-course strategy generalization
232
+
233
+ ---
234
+
235
+ ## Key Design Decisions Ahead
236
+
237
+ ### 1. Centralized vs Decentralized Orchestration
238
+
239
+ **Option A: Central Orchestrator Service**
240
+ - Single service decides strategy application
241
+ - Cleaner cohort tracking
242
+ - Requires backend infrastructure
243
+
244
+ **Option B: Client-Side Orchestration**
245
+ - Each client computes its own strategy set
246
+ - Works offline
247
+ - Harder to track cohorts
248
+
249
+ **Recommendation:** Hybrid — client computes from synced registry, outcomes reported to backend.
250
+
251
+ ### 2. Confidence Update Mechanism
252
+
253
+ **Option A: Frequentist (Simple)**
254
+ - Track success/failure counts
255
+ - Confidence = success_rate with smoothing
256
+ - Easy to implement
257
+
258
+ **Option B: Bayesian (Principled)**
259
+ - Prior belief + observed evidence
260
+ - Thompson sampling for exploration/exploitation
261
+ - More complex but theoretically sound
262
+
263
+ **Recommendation:** Start with frequentist, migrate to Bayesian if needed.
264
+
265
+ ### 3. Strategy Granularity
266
+
267
+ **Question:** What counts as "a strategy" for evolutionary purposes?
268
+
269
+ - A specific `HierarchyDefinition` config? (Fine-grained)
270
+ - The `HierarchyDefinition` approach in general? (Coarse-grained)
271
+ - A specific prerequisite rule within a hierarchy? (Very fine-grained)
272
+
273
+ **Recommendation:** Start coarse (strategy type + major config), add granularity as measurement matures.
274
+
275
+ ### 4. Outcome Attribution
276
+
277
+ **Question:** If a user is exposed to multiple strategies, how do we attribute outcomes?
278
+
279
+ - All strategies get credit/blame equally?
280
+ - Weight by score contribution?
281
+ - Require isolated A/B cells?
282
+
283
+ **Recommendation:** Start with equal attribution, instrument for isolated testing later.
284
+
285
+ ---
286
+
287
+ ## Related Documents
288
+
289
+ - `u.3.strategic-nuggets.md` — Original vision statement
290
+ - `todo-srs-navigator.md` — SRS migration
291
+ - `todo-strategy-authoring.md` — Authoring tools
292
+ - `todo-strategy-state-storage.md` — State persistence
293
+ - `todo-provenance.md` — Audit trail for transparency
294
+ - `a.2.plan.md` — Detailed implementation plan
295
+ - `a.3.todo.md` — Task tracking
296
+ - `packages/db/docs/navigators-architecture.md` — Technical architecture
297
+
298
+ ---
299
+
300
+ ## Summary
301
+
302
+ **Where we are:** Solid foundation. Strategies exist, compose, return scores, and are integrated
303
+ with SessionController. The infrastructure supports the vision.
304
+
305
+ **What's next:** Build the measurement and orchestration layers. The strategies can now *exist*;
306
+ we need to make them *compete* and *evolve*.
307
+
308
+ **The gap:** The evolutionary pressure mechanisms (multi-arm bandit selection, outcome measurement,
309
+ confidence updates, self-healing) are not yet implemented. This is where the "magic" of the
310
+ vision lives, and it's entirely ahead of us.