@vue-skuilder/db 0.1.16 → 0.1.18

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 (80) hide show
  1. package/dist/{userDB-DNa0XPtn.d.ts → classroomDB-BgfrVb8d.d.ts} +357 -103
  2. package/dist/{userDB-BqwxtJ_7.d.mts → classroomDB-CTOenngH.d.cts} +358 -104
  3. package/dist/core/index.d.cts +230 -0
  4. package/dist/core/index.d.ts +161 -23
  5. package/dist/core/index.js +1964 -154
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +1925 -121
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-BV5iZqt_.d.ts → dataLayerProvider-CZxC9GtB.d.ts} +1 -1
  10. package/dist/{dataLayerProvider-VlngD19_.d.mts → dataLayerProvider-D6PoCwS6.d.cts} +1 -1
  11. package/dist/impl/couch/{index.d.mts → index.d.cts} +46 -5
  12. package/dist/impl/couch/index.d.ts +44 -3
  13. package/dist/impl/couch/index.js +1971 -171
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +1933 -134
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/{index.d.mts → index.d.cts} +5 -6
  18. package/dist/impl/static/index.d.ts +2 -3
  19. package/dist/impl/static/index.js +1614 -119
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +1585 -92
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/{index-Bmll7Xse.d.mts → index-D-Fa4Smt.d.cts} +1 -1
  24. package/dist/{index.d.mts → index.d.cts} +97 -13
  25. package/dist/index.d.ts +90 -6
  26. package/dist/index.js +2085 -153
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +2031 -106
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/pouch/index.js +3 -3
  31. package/dist/{types-Dbp5DaRR.d.mts → types-CzPDLAK6.d.cts} +1 -1
  32. package/dist/util/packer/{index.d.mts → index.d.cts} +3 -3
  33. package/dist/util/packer/index.js.map +1 -1
  34. package/dist/util/packer/index.mjs.map +1 -1
  35. package/docs/brainstorm-navigation-paradigm.md +369 -0
  36. package/docs/navigators-architecture.md +265 -0
  37. package/docs/todo-evolutionary-orchestration.md +310 -0
  38. package/docs/todo-nominal-tag-types.md +121 -0
  39. package/docs/todo-pipeline-optimization.md +117 -0
  40. package/docs/todo-strategy-authoring.md +401 -0
  41. package/docs/todo-strategy-state-storage.md +278 -0
  42. package/eslint.config.mjs +1 -1
  43. package/package.json +9 -4
  44. package/src/core/interfaces/contentSource.ts +88 -4
  45. package/src/core/interfaces/navigationStrategyManager.ts +0 -5
  46. package/src/core/navigators/CompositeGenerator.ts +268 -0
  47. package/src/core/navigators/Pipeline.ts +205 -0
  48. package/src/core/navigators/PipelineAssembler.ts +194 -0
  49. package/src/core/navigators/elo.ts +104 -15
  50. package/src/core/navigators/filters/eloDistance.ts +132 -0
  51. package/src/core/navigators/filters/index.ts +6 -0
  52. package/src/core/navigators/filters/types.ts +115 -0
  53. package/src/core/navigators/generators/index.ts +2 -0
  54. package/src/core/navigators/generators/types.ts +107 -0
  55. package/src/core/navigators/hardcodedOrder.ts +111 -12
  56. package/src/core/navigators/hierarchyDefinition.ts +266 -0
  57. package/src/core/navigators/index.ts +345 -3
  58. package/src/core/navigators/interferenceMitigator.ts +367 -0
  59. package/src/core/navigators/relativePriority.ts +267 -0
  60. package/src/core/navigators/srs.ts +195 -0
  61. package/src/impl/couch/classroomDB.ts +51 -0
  62. package/src/impl/couch/courseDB.ts +117 -39
  63. package/src/impl/static/courseDB.ts +0 -4
  64. package/src/study/SessionController.ts +149 -1
  65. package/src/study/TagFilteredContentSource.ts +255 -0
  66. package/src/study/index.ts +1 -0
  67. package/src/util/dataDirectory.test.ts +51 -22
  68. package/src/util/logger.ts +0 -1
  69. package/tests/core/navigators/CompositeGenerator.test.ts +455 -0
  70. package/tests/core/navigators/Pipeline.test.ts +405 -0
  71. package/tests/core/navigators/PipelineAssembler.test.ts +351 -0
  72. package/tests/core/navigators/SRSNavigator.test.ts +344 -0
  73. package/tests/core/navigators/eloDistanceFilter.test.ts +192 -0
  74. package/tests/core/navigators/navigators.test.ts +710 -0
  75. package/tsconfig.json +1 -1
  76. package/vitest.config.ts +29 -0
  77. package/dist/core/index.d.mts +0 -92
  78. /package/dist/{SyncStrategy-CyATpyLQ.d.mts → SyncStrategy-CyATpyLQ.d.cts} +0 -0
  79. /package/dist/pouch/{index.d.mts → index.d.cts} +0 -0
  80. /package/dist/{types-legacy-6ettoclI.d.mts → types-legacy-6ettoclI.d.cts} +0 -0
@@ -0,0 +1,401 @@
1
+ # NavigationStrategy Authoring Tools
2
+
3
+ ## Status: UI COMPLETE ✅ (CLI/MCP Optional)
4
+
5
+ ## Goal
6
+
7
+ Provide tools for course authors to create and configure NavigationStrategy documents
8
+ without direct database manipulation. This includes UI components, CLI commands, and
9
+ potentially MCP tools.
10
+
11
+ **UI implementation completed.** CLI and MCP remain as optional future enhancements for
12
+ scriptable/agent-based workflows.
13
+
14
+ ## Current State (Updated)
15
+
16
+ ### What Exists ✅
17
+
18
+ | Component | Location | Status |
19
+ |-----------|----------|--------|
20
+ | `HardcodedOrderConfigForm.vue` | `packages/edit-ui/src/components/NavigationStrategy/` | ✅ Complete |
21
+ | `HierarchyConfigForm.vue` | `packages/edit-ui/src/components/NavigationStrategy/` | ✅ Complete |
22
+ | `InterferenceConfigForm.vue` | `packages/edit-ui/src/components/NavigationStrategy/` | ✅ Complete |
23
+ | `RelativePriorityConfigForm.vue` | `packages/edit-ui/src/components/NavigationStrategy/` | ✅ Complete |
24
+ | `NavigationStrategyEditor.vue` | `packages/edit-ui/src/components/NavigationStrategy/` | ✅ All strategy types supported |
25
+ | `NavigationStrategyList.vue` | `packages/edit-ui/src/components/NavigationStrategy/` | ✅ Compact layout, edit/delete |
26
+ | `CourseDB.addNavigationStrategy()` | `packages/db/src/impl/couch/courseDB.ts` | ✅ DB write method |
27
+ | `CourseDB.updateNavigationStrategy()` | `packages/db/src/impl/couch/courseDB.ts` | ✅ DB update method |
28
+ | `CourseDB.getAllNavigationStrategies()` | `packages/db/src/impl/couch/courseDB.ts` | ✅ DB read method |
29
+
30
+ ### What's Complete
31
+
32
+ - ✅ UI forms for all four strategy types (Hardcoded, Hierarchy, Interference, RelativePriority)
33
+ - ✅ Dual input modes (Visual Editor / JSON Editor) for each form
34
+ - ✅ Tag loading from course database
35
+ - ✅ Real-time validation with error messages
36
+ - ✅ Edit functionality for existing strategies
37
+ - ✅ Compact, non-modal two-column layout
38
+ - ✅ Responsive design (desktop and mobile)
39
+ - ✅ Full CRUD operations (Create, Read, Update, Delete)
40
+
41
+ ### What's Optional (Future Enhancements)
42
+
43
+ - ⏸️ CLI commands for strategy creation (scriptable workflows)
44
+ - ⏸️ MCP tools for agent-based authoring (LLM-guided config generation)
45
+ - ⏸️ Strategy behavior preview/simulation
46
+
47
+ ## Strategy Types and Their Configs
48
+
49
+ ### 1. HierarchyDefinition
50
+
51
+ ```typescript
52
+ interface HierarchyConfig {
53
+ prerequisites: {
54
+ [tagId: string]: TagPrerequisite[];
55
+ };
56
+ delegateStrategy?: string; // default: "elo"
57
+ }
58
+
59
+ interface TagPrerequisite {
60
+ tag: string;
61
+ masteryThreshold?: {
62
+ minElo?: number;
63
+ minCount?: number;
64
+ };
65
+ }
66
+ ```
67
+
68
+ **Example:**
69
+ ```json
70
+ {
71
+ "prerequisites": {
72
+ "cvc-words": [
73
+ { "tag": "letter-sounds", "masteryThreshold": { "minCount": 10, "minElo": 1050 } }
74
+ ],
75
+ "blends": [
76
+ { "tag": "cvc-words", "masteryThreshold": { "minCount": 20 } }
77
+ ]
78
+ },
79
+ "delegateStrategy": "elo"
80
+ }
81
+ ```
82
+
83
+ **UI Requirements:**
84
+ - Tag selector (from course's available tags)
85
+ - Prerequisite builder (tag → requires tags with thresholds)
86
+ - Threshold inputs (minCount, minElo)
87
+ - Delegate strategy selector
88
+
89
+ ---
90
+
91
+ ### 2. InterferenceMitigator
92
+
93
+ ```typescript
94
+ interface InterferenceConfig {
95
+ interferenceSets: InterferenceGroup[];
96
+ maturityThreshold?: {
97
+ minCount?: number;
98
+ minElo?: number;
99
+ minElapsedDays?: number;
100
+ };
101
+ defaultDecay?: number;
102
+ delegateStrategy?: string;
103
+ }
104
+
105
+ interface InterferenceGroup {
106
+ tags: string[];
107
+ decay?: number;
108
+ }
109
+ ```
110
+
111
+ **Example:**
112
+ ```json
113
+ {
114
+ "interferenceSets": [
115
+ { "tags": ["letter-b", "letter-d", "letter-p"], "decay": 0.9 },
116
+ { "tags": ["letter-m", "letter-n"], "decay": 0.7 }
117
+ ],
118
+ "maturityThreshold": { "minCount": 15, "minElapsedDays": 3 },
119
+ "defaultDecay": 0.8
120
+ }
121
+ ```
122
+
123
+ **UI Requirements:**
124
+ - Interference group builder (add/remove groups)
125
+ - Tag multi-selector for each group
126
+ - Decay slider (0-1) per group
127
+ - Maturity threshold inputs
128
+ - Delegate strategy selector
129
+
130
+ ---
131
+
132
+ ### 3. RelativePriority
133
+
134
+ ```typescript
135
+ interface RelativePriorityConfig {
136
+ tagPriorities: { [tagId: string]: number };
137
+ defaultPriority?: number;
138
+ combineMode?: 'max' | 'average' | 'min';
139
+ priorityInfluence?: number;
140
+ delegateStrategy?: string;
141
+ }
142
+ ```
143
+
144
+ **Example:**
145
+ ```json
146
+ {
147
+ "tagPriorities": {
148
+ "letter-s": 0.95,
149
+ "letter-t": 0.90,
150
+ "letter-a": 0.88,
151
+ "letter-x": 0.10,
152
+ "letter-z": 0.05
153
+ },
154
+ "defaultPriority": 0.5,
155
+ "combineMode": "max",
156
+ "priorityInfluence": 0.5
157
+ }
158
+ ```
159
+
160
+ **UI Requirements:**
161
+ - Tag list with priority sliders (0-1)
162
+ - Default priority input
163
+ - Combine mode selector (max/average/min)
164
+ - Priority influence slider
165
+ - Delegate strategy selector
166
+
167
+ ---
168
+
169
+ ## Implementation Options
170
+
171
+ ### Option A: Extend NavigationStrategyEditor.vue
172
+
173
+ **Pros:**
174
+ - Single location for all strategy editing
175
+ - Consistent with existing UI patterns
176
+ - User-facing, accessible to course authors
177
+
178
+ **Cons:**
179
+ - More complex Vue component
180
+ - Requires form validation logic
181
+
182
+ **Implementation:**
183
+ 1. Add strategy type selector dropdown
184
+ 2. Conditional rendering of config forms based on type
185
+ 3. Form validation before save
186
+ 4. Serialize config to `serializedData` JSON
187
+
188
+ ---
189
+
190
+ ### Option B: CLI Commands
191
+
192
+ **Pros:**
193
+ - Scriptable, automatable
194
+ - Good for bulk operations
195
+ - Can be used by agents
196
+
197
+ **Cons:**
198
+ - Not user-friendly for non-technical authors
199
+ - Requires terminal access
200
+
201
+ **Implementation:**
202
+ ```bash
203
+ # Create a hierarchy strategy
204
+ yarn cli strategy:create <courseId> \
205
+ --type hierarchy \
206
+ --name "Phonics Progression" \
207
+ --config ./hierarchy-config.json
208
+
209
+ # Create an interference strategy
210
+ yarn cli strategy:create <courseId> \
211
+ --type interference \
212
+ --config ./interference-config.json
213
+
214
+ # List strategies
215
+ yarn cli strategy:list <courseId>
216
+
217
+ # Set default strategy
218
+ yarn cli strategy:set-default <courseId> <strategyId>
219
+
220
+ # Delete strategy
221
+ yarn cli strategy:delete <courseId> <strategyId>
222
+ ```
223
+
224
+ **Files to modify:**
225
+ - `packages/cli/src/commands/` — Add strategy commands
226
+ - `packages/cli/src/index.ts` — Register new commands
227
+
228
+ ---
229
+
230
+ ### Option C: MCP Tools
231
+
232
+ **Pros:**
233
+ - Agent-accessible for automated course building
234
+ - Consistent with existing MCP pattern
235
+ - Can leverage LLM for config generation
236
+
237
+ **Cons:**
238
+ - Requires MCP client
239
+ - Not directly user-facing
240
+
241
+ **Implementation:**
242
+ Add to `packages/mcp/src/tools/`:
243
+
244
+ ```typescript
245
+ // create_navigation_strategy tool
246
+ {
247
+ name: 'create_navigation_strategy',
248
+ description: 'Create a navigation strategy for the course',
249
+ inputSchema: {
250
+ type: 'object',
251
+ properties: {
252
+ strategyType: { enum: ['hierarchy', 'interference', 'relativePriority'] },
253
+ name: { type: 'string' },
254
+ description: { type: 'string' },
255
+ config: { type: 'object' }
256
+ }
257
+ }
258
+ }
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Implementation Completed: UI-First Approach ✅
264
+
265
+ **Phase 1: UI Forms (COMPLETED)**
266
+ - ✅ All four strategy configuration forms implemented
267
+ - ✅ Visual editor with guided inputs
268
+ - ✅ JSON editor for power users
269
+ - ✅ Tag selectors, sliders, multi-selects
270
+ - ✅ Validation and error messages
271
+ - ✅ Edit/Create/Delete functionality
272
+ - ✅ Compact two-column layout (list + form)
273
+
274
+ **Phase 2 (Optional): CLI Commands**
275
+ - Scriptable strategy creation from JSON files
276
+ - Bulk operations and automation
277
+ - Terminal-based workflows
278
+ - **Status:** Not started, optional enhancement
279
+
280
+ **Phase 3 (Optional): MCP Tools**
281
+ - Agent-accessible strategy authoring
282
+ - LLM-guided config generation
283
+ - Extends existing MCP infrastructure
284
+ - **Status:** Not started, optional enhancement
285
+
286
+ ---
287
+
288
+ ## Validation Requirements
289
+
290
+ All authoring tools should validate:
291
+
292
+ 1. **Tag existence**: All referenced tags exist in the course
293
+ 2. **Circular dependencies** (Hierarchy): No circular prerequisite chains
294
+ 3. **Config completeness**: Required fields present
295
+ 4. **Value ranges**: Scores/thresholds in valid ranges (e.g., 0-1)
296
+ 5. **Delegate validity**: Delegate strategy type exists
297
+
298
+ ```typescript
299
+ interface ValidationResult {
300
+ valid: boolean;
301
+ errors: Array<{
302
+ field: string;
303
+ message: string;
304
+ }>;
305
+ warnings: Array<{
306
+ field: string;
307
+ message: string;
308
+ }>;
309
+ }
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Files Created/Modified
315
+
316
+ ### UI Implementation (COMPLETED ✅)
317
+
318
+ | File | Action | Description | Status |
319
+ |------|--------|-------------|--------|
320
+ | `packages/edit-ui/src/components/NavigationStrategy/HardcodedOrderConfigForm.vue` | CREATED | Card ID list form | ✅ |
321
+ | `packages/edit-ui/src/components/NavigationStrategy/HierarchyConfigForm.vue` | CREATED | Prerequisite gating config | ✅ |
322
+ | `packages/edit-ui/src/components/NavigationStrategy/InterferenceConfigForm.vue` | CREATED | Interference groups config | ✅ |
323
+ | `packages/edit-ui/src/components/NavigationStrategy/RelativePriorityConfigForm.vue` | CREATED | Tag priorities config | ✅ |
324
+ | `packages/edit-ui/src/components/NavigationStrategy/NavigationStrategyEditor.vue` | MODIFIED | Two-column layout, all strategy types | ✅ |
325
+ | `packages/edit-ui/src/components/NavigationStrategy/NavigationStrategyList.vue` | MODIFIED | Compact density, simplified display | ✅ |
326
+
327
+ ### CLI Implementation (OPTIONAL, NOT STARTED)
328
+
329
+ | File | Action | Description | Status |
330
+ |------|--------|-------------|--------|
331
+ | `packages/cli/src/commands/strategy.ts` | CREATE | Strategy management commands | ⏸️ |
332
+ | `packages/cli/src/utils/strategy-validation.ts` | CREATE | Config validation utilities | ⏸️ |
333
+ | `packages/cli/src/index.ts` | MODIFY | Register strategy commands | ⏸️ |
334
+
335
+ ### MCP Implementation (OPTIONAL, NOT STARTED)
336
+
337
+ | File | Action | Description | Status |
338
+ |------|--------|-------------|--------|
339
+ | `packages/mcp/src/tools/create-navigation-strategy.ts` | CREATE | Create strategy tool | ⏸️ |
340
+ | `packages/mcp/src/tools/update-navigation-strategy.ts` | CREATE | Update strategy tool | ⏸️ |
341
+ | `packages/mcp/src/tools/list-navigation-strategies.ts` | CREATE | List strategies tool | ⏸️ |
342
+ | `packages/mcp/src/tools/delete-navigation-strategy.ts` | CREATE | Delete strategy tool | ⏸️ |
343
+ | `packages/mcp/src/types/tools.ts` | MODIFY | Add strategy schemas | ⏸️ |
344
+ | `packages/mcp/src/tools/index.ts` | MODIFY | Export new tools | ⏸️ |
345
+
346
+ ---
347
+
348
+ ## Key Features Delivered
349
+
350
+ ### Dual Input Modes
351
+ All forms support two modes for flexibility:
352
+ - **Visual Editor**: Guided UI with selectors, sliders, and inputs
353
+ - **JSON Editor**: Direct JSON editing with real-time validation
354
+
355
+ ### Strategy-Specific Capabilities
356
+
357
+ **HierarchyConfigForm:**
358
+ - Add/remove prerequisite rules
359
+ - Tag selectors for gated tags and their prerequisites
360
+ - Mastery thresholds (minCount, minElo)
361
+ - Delegate strategy selector
362
+
363
+ **InterferenceConfigForm:**
364
+ - Add/remove interference groups
365
+ - Multi-tag selector for each group
366
+ - Per-group decay sliders (0-1)
367
+ - Maturity threshold configuration (minCount, minElo, minElapsedDays)
368
+ - Default decay setting
369
+
370
+ **RelativePriorityConfigForm:**
371
+ - Individual priority sliders for all course tags
372
+ - Default priority configuration
373
+ - Combine mode selector (max/average/min)
374
+ - Priority influence slider
375
+
376
+ ### User Experience
377
+ - **Compact Layout**: Two-column design (strategy list + form)
378
+ - **Always Visible**: No modal dialogs, form always accessible
379
+ - **Responsive**: Works on desktop and mobile
380
+ - **Real-time Validation**: Inline error messages
381
+ - **Tag Loading**: Automatically fetches course tags
382
+ - **Edit Support**: Click any strategy to edit
383
+
384
+ ---
385
+
386
+ ## Access
387
+
388
+ The NavigationStrategy editor is accessible in:
389
+ - **studio-ui**: `/course-editor` route → "Navigation" tab
390
+ - **platform-ui**: `/edit/:courseId` route → "Navigation" tab
391
+
392
+ ---
393
+
394
+ ## Related Files
395
+
396
+ - `packages/db/src/core/types/contentNavigationStrategy.ts` — Strategy data schema
397
+ - `packages/db/src/impl/couch/courseDB.ts` — DB methods for strategies
398
+ - `packages/db/src/core/navigators/hierarchyDefinition.ts` — Config interface reference
399
+ - `packages/db/src/core/navigators/interferenceMitigator.ts` — Config interface reference
400
+ - `packages/db/src/core/navigators/relativePriority.ts` — Config interface reference
401
+ - `packages/edit-ui/src/components/NavigationStrategy/` — UI components (all forms)
@@ -0,0 +1,278 @@
1
+ # TODO: Strategy-Specific State Storage in UserDB
2
+
3
+ ## Status: NOT STARTED
4
+
5
+ ## Goal
6
+
7
+ Enable NavigationStrategies (ContentNavigators) to persist their own state data in the
8
+ user's database, allowing strategies to maintain context across sessions.
9
+
10
+ ## Current State
11
+
12
+ ### What Strategies Can Read
13
+
14
+ | Data | Method | Notes |
15
+ |------|--------|-------|
16
+ | User's global ELO | `user.getCourseRegDoc(courseId).elo.global` | ✅ Available |
17
+ | User's per-tag ELO | `user.getCourseRegDoc(courseId).elo.tags` | ✅ Available |
18
+ | Seen cards | `user.getSeenCards(courseId)` | ✅ Card IDs only |
19
+ | Active cards | `user.getActiveCards()` | ✅ Available |
20
+ | Pending reviews | `user.getPendingReviews(courseId)` | ✅ ScheduledCard objects |
21
+ | Card history | `user.putCardRecord()` returns `CardHistory` | 🟡 Only after write |
22
+
23
+ ### What Strategies Cannot Do
24
+
25
+ - **Store arbitrary state**: No namespaced storage for strategy-specific data
26
+ - **Track temporal patterns**: No easy way to record "when did I last introduce tag X?"
27
+ - **Persist learning context**: Strategy state is lost between sessions
28
+
29
+ ## Use Cases
30
+
31
+ ### 1. InterferenceMitigator
32
+
33
+ **Need**: Track when interfering concepts were last introduced together.
34
+
35
+ ```typescript
36
+ // Desired: Store last introduction time per tag
37
+ {
38
+ "lastIntroduction": {
39
+ "letter-b": "2024-01-15T10:30:00Z",
40
+ "letter-d": "2024-01-16T14:20:00Z"
41
+ }
42
+ }
43
+ ```
44
+
45
+ ### 2. Minimal Pairs Strategy (Future)
46
+
47
+ **Need**: Track discrimination training progress between confusable pairs.
48
+
49
+ ```typescript
50
+ // Desired: Store discrimination scores per pair
51
+ {
52
+ "pairScores": {
53
+ "b-d": { "correct": 15, "total": 20, "lastPracticed": "..." },
54
+ "m-n": { "correct": 8, "total": 10, "lastPracticed": "..." }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### 3. Adaptive Pacing Strategy (Future)
60
+
61
+ **Need**: Track user's engagement patterns and optimal session timing.
62
+
63
+ ```typescript
64
+ // Desired: Store engagement metrics
65
+ {
66
+ "sessionMetrics": {
67
+ "avgAccuracyByTimeOfDay": { "morning": 0.85, "afternoon": 0.78 },
68
+ "optimalSessionLength": 180, // seconds
69
+ "fatigueThreshold": 12 // cards before accuracy drops
70
+ }
71
+ }
72
+ ```
73
+
74
+ ## Proposed Solutions
75
+
76
+ ### Option A: Extend CourseRegistration
77
+
78
+ Add a `strategyState` field to the existing `CourseRegistration` document.
79
+
80
+ **Schema:**
81
+ ```typescript
82
+ interface CourseRegistration {
83
+ // ... existing fields ...
84
+
85
+ /**
86
+ * Strategy-specific state, keyed by strategy ID or type.
87
+ * Each strategy owns its own namespace.
88
+ */
89
+ strategyState?: {
90
+ [strategyKey: string]: unknown;
91
+ };
92
+ }
93
+ ```
94
+
95
+ **Pros:**
96
+ - No new document types
97
+ - Lives alongside other course-specific user data
98
+ - Already synced via existing mechanisms
99
+
100
+ **Cons:**
101
+ - Potential for large documents if strategies store lots of data
102
+ - All strategies share one document (contention on updates)
103
+
104
+ ---
105
+
106
+ ### Option B: Separate Strategy State Documents
107
+
108
+ Create a new document type for strategy state.
109
+
110
+ **Schema:**
111
+ ```typescript
112
+ interface StrategyStateDoc {
113
+ _id: `STRATEGY_STATE-${courseId}-${strategyKey}`;
114
+ docType: DocType.STRATEGY_STATE;
115
+ courseId: string;
116
+ strategyKey: string; // e.g., "interferenceMitigator" or strategy instance ID
117
+ data: unknown;
118
+ updatedAt: string;
119
+ }
120
+ ```
121
+
122
+ **Pros:**
123
+ - Clean separation
124
+ - No document size concerns
125
+ - Independent updates (no contention)
126
+
127
+ **Cons:**
128
+ - New document type to manage
129
+ - More queries to fetch state
130
+
131
+ ---
132
+
133
+ ### Option C: Generic Key-Value Store in UserDB
134
+
135
+ Add generic methods for namespaced storage.
136
+
137
+ **Interface:**
138
+ ```typescript
139
+ interface UserDBWriter {
140
+ // ... existing methods ...
141
+
142
+ /**
143
+ * Store data in a namespaced location.
144
+ * @param namespace - Unique namespace (e.g., "strategy:interferenceMitigator:course123")
145
+ * @param data - Arbitrary JSON-serializable data
146
+ */
147
+ putNamespacedData(namespace: string, data: unknown): Promise<void>;
148
+
149
+ /**
150
+ * Retrieve namespaced data.
151
+ * @param namespace - The namespace to retrieve
152
+ * @returns The stored data, or null if not found
153
+ */
154
+ getNamespacedData<T>(namespace: string): Promise<T | null>;
155
+
156
+ /**
157
+ * Delete namespaced data.
158
+ * @param namespace - The namespace to delete
159
+ */
160
+ deleteNamespacedData(namespace: string): Promise<void>;
161
+ }
162
+ ```
163
+
164
+ **Pros:**
165
+ - Most flexible
166
+ - Reusable beyond strategies
167
+ - Clean API
168
+
169
+ **Cons:**
170
+ - Very generic (might be too permissive)
171
+ - Namespace collision risk
172
+
173
+ ---
174
+
175
+ ## Recommended Approach
176
+
177
+ **Option B (Separate Documents)** with a convenience wrapper:
178
+
179
+ ```typescript
180
+ // In ContentNavigator base class or a mixin
181
+ abstract class ContentNavigator {
182
+ // ... existing methods ...
183
+
184
+ /**
185
+ * Get this strategy's persisted state for the current course.
186
+ */
187
+ protected async getStrategyState<T>(): Promise<T | null> {
188
+ const key = `STRATEGY_STATE-${this.course.getCourseID()}-${this.strategyKey}`;
189
+ try {
190
+ return await this.user.get<T>(key);
191
+ } catch (e) {
192
+ return null; // Not found
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Persist this strategy's state for the current course.
198
+ */
199
+ protected async putStrategyState<T>(data: T): Promise<void> {
200
+ const key = `STRATEGY_STATE-${this.course.getCourseID()}-${this.strategyKey}`;
201
+ const existing = await this.getStrategyState<T>();
202
+ await this.user.put({
203
+ _id: key,
204
+ _rev: existing?._rev,
205
+ docType: DocType.STRATEGY_STATE,
206
+ courseId: this.course.getCourseID(),
207
+ strategyKey: this.strategyKey,
208
+ data,
209
+ updatedAt: new Date().toISOString(),
210
+ });
211
+ }
212
+
213
+ /**
214
+ * Unique key for this strategy instance.
215
+ * Override in subclasses if multiple instances need separate state.
216
+ */
217
+ protected get strategyKey(): string {
218
+ return this.constructor.name; // e.g., "InterferenceMitigatorNavigator"
219
+ }
220
+ }
221
+ ```
222
+
223
+ ## Implementation Plan
224
+
225
+ ### Phase 1: Add DocType and Interface
226
+
227
+ 1. Add `STRATEGY_STATE` to `DocType` enum in `packages/db/src/core/types/types-legacy.ts`
228
+ 2. Define `StrategyStateDoc` interface
229
+ 3. Add prefix to `DocTypePrefixes`
230
+
231
+ ### Phase 2: Add UserDB Methods
232
+
233
+ 1. Add `getStrategyState()` and `putStrategyState()` to `UserDBInterface`
234
+ 2. Implement in `CouchUserDB`
235
+
236
+ ### Phase 3: Add ContentNavigator Helpers
237
+
238
+ 1. Add protected helper methods to `ContentNavigator` base class
239
+ 2. Document usage pattern
240
+
241
+ ### Phase 4: Update Strategies
242
+
243
+ 1. Update `InterferenceMitigatorNavigator` to use state storage
244
+ 2. Add tests for state persistence
245
+
246
+ ## Files to Create/Modify
247
+
248
+ | File | Action | Description |
249
+ |------|--------|-------------|
250
+ | `packages/db/src/core/types/types-legacy.ts` | MODIFY | Add STRATEGY_STATE DocType |
251
+ | `packages/db/src/core/types/strategyState.ts` | CREATE | StrategyStateDoc interface |
252
+ | `packages/db/src/core/interfaces/userDB.ts` | MODIFY | Add state storage methods |
253
+ | `packages/db/src/impl/couch/userDB.ts` | MODIFY | Implement state storage |
254
+ | `packages/db/src/core/navigators/index.ts` | MODIFY | Add helper methods to base class |
255
+ | `packages/db/src/core/navigators/interferenceMitigator.ts` | MODIFY | Use state storage |
256
+
257
+ ## Test Cases
258
+
259
+ 1. **Store and retrieve**: Write state, read it back, verify equality
260
+ 2. **Update existing**: Write state, update it, verify new value
261
+ 3. **Separate namespaces**: Two strategies store data, each gets their own
262
+ 4. **Cross-session persistence**: Store state, simulate new session, verify data persists
263
+ 5. **Missing state**: Read state that doesn't exist, get null (not error)
264
+
265
+ ## Open Questions
266
+
267
+ 1. **State migration**: How to handle strategy state when strategy config changes?
268
+ 2. **State cleanup**: When a strategy is deleted, should its state be cleaned up?
269
+ 3. **State size limits**: Should we enforce maximum state size?
270
+ 4. **Sync behavior**: How does state sync across devices for multi-device users?
271
+
272
+ ## Related Files
273
+
274
+ - `packages/db/src/core/interfaces/userDB.ts` — UserDB interface
275
+ - `packages/db/src/impl/couch/userDB.ts` — UserDB implementation
276
+ - `packages/db/src/core/navigators/index.ts` — ContentNavigator base class
277
+ - `packages/db/src/core/types/types-legacy.ts` — DocType enum
278
+ - `packages/db/docs/navigators-architecture.md` — Architecture overview
package/eslint.config.mjs CHANGED
@@ -3,7 +3,7 @@ import backendConfig from '../../eslint.config.backend.mjs';
3
3
  export default [
4
4
  ...backendConfig,
5
5
  {
6
- ignores: ['node_modules/**', 'dist/**', 'eslint.config.mjs', 'tsup.config.ts'],
6
+ ignores: ['node_modules/**', 'dist/**', 'eslint.config.mjs', 'tsup.config.ts', 'vitest.config.ts'],
7
7
  },
8
8
  {
9
9
  languageOptions: {