@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.
- package/dist/{userDB-DNa0XPtn.d.ts → classroomDB-BgfrVb8d.d.ts} +357 -103
- package/dist/{userDB-BqwxtJ_7.d.mts → classroomDB-CTOenngH.d.cts} +358 -104
- package/dist/core/index.d.cts +230 -0
- package/dist/core/index.d.ts +161 -23
- package/dist/core/index.js +1964 -154
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +1925 -121
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-BV5iZqt_.d.ts → dataLayerProvider-CZxC9GtB.d.ts} +1 -1
- package/dist/{dataLayerProvider-VlngD19_.d.mts → dataLayerProvider-D6PoCwS6.d.cts} +1 -1
- package/dist/impl/couch/{index.d.mts → index.d.cts} +46 -5
- package/dist/impl/couch/index.d.ts +44 -3
- package/dist/impl/couch/index.js +1971 -171
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +1933 -134
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/{index.d.mts → index.d.cts} +5 -6
- package/dist/impl/static/index.d.ts +2 -3
- package/dist/impl/static/index.js +1614 -119
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +1585 -92
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/{index-Bmll7Xse.d.mts → index-D-Fa4Smt.d.cts} +1 -1
- package/dist/{index.d.mts → index.d.cts} +97 -13
- package/dist/index.d.ts +90 -6
- package/dist/index.js +2085 -153
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2031 -106
- package/dist/index.mjs.map +1 -1
- package/dist/pouch/index.js +3 -3
- package/dist/{types-Dbp5DaRR.d.mts → types-CzPDLAK6.d.cts} +1 -1
- package/dist/util/packer/{index.d.mts → index.d.cts} +3 -3
- package/dist/util/packer/index.js.map +1 -1
- package/dist/util/packer/index.mjs.map +1 -1
- package/docs/brainstorm-navigation-paradigm.md +369 -0
- package/docs/navigators-architecture.md +265 -0
- package/docs/todo-evolutionary-orchestration.md +310 -0
- package/docs/todo-nominal-tag-types.md +121 -0
- package/docs/todo-pipeline-optimization.md +117 -0
- package/docs/todo-strategy-authoring.md +401 -0
- package/docs/todo-strategy-state-storage.md +278 -0
- package/eslint.config.mjs +1 -1
- package/package.json +9 -4
- package/src/core/interfaces/contentSource.ts +88 -4
- package/src/core/interfaces/navigationStrategyManager.ts +0 -5
- package/src/core/navigators/CompositeGenerator.ts +268 -0
- package/src/core/navigators/Pipeline.ts +205 -0
- package/src/core/navigators/PipelineAssembler.ts +194 -0
- package/src/core/navigators/elo.ts +104 -15
- package/src/core/navigators/filters/eloDistance.ts +132 -0
- package/src/core/navigators/filters/index.ts +6 -0
- package/src/core/navigators/filters/types.ts +115 -0
- package/src/core/navigators/generators/index.ts +2 -0
- package/src/core/navigators/generators/types.ts +107 -0
- package/src/core/navigators/hardcodedOrder.ts +111 -12
- package/src/core/navigators/hierarchyDefinition.ts +266 -0
- package/src/core/navigators/index.ts +345 -3
- package/src/core/navigators/interferenceMitigator.ts +367 -0
- package/src/core/navigators/relativePriority.ts +267 -0
- package/src/core/navigators/srs.ts +195 -0
- package/src/impl/couch/classroomDB.ts +51 -0
- package/src/impl/couch/courseDB.ts +117 -39
- package/src/impl/static/courseDB.ts +0 -4
- package/src/study/SessionController.ts +149 -1
- package/src/study/TagFilteredContentSource.ts +255 -0
- package/src/study/index.ts +1 -0
- package/src/util/dataDirectory.test.ts +51 -22
- package/src/util/logger.ts +0 -1
- package/tests/core/navigators/CompositeGenerator.test.ts +455 -0
- package/tests/core/navigators/Pipeline.test.ts +405 -0
- package/tests/core/navigators/PipelineAssembler.test.ts +351 -0
- package/tests/core/navigators/SRSNavigator.test.ts +344 -0
- package/tests/core/navigators/eloDistanceFilter.test.ts +192 -0
- package/tests/core/navigators/navigators.test.ts +710 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +29 -0
- package/dist/core/index.d.mts +0 -92
- /package/dist/{SyncStrategy-CyATpyLQ.d.mts → SyncStrategy-CyATpyLQ.d.cts} +0 -0
- /package/dist/pouch/{index.d.mts → index.d.cts} +0 -0
- /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: {
|