@contractspec/module.learning-journey 1.44.0

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 (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/dist/contracts/index.d.ts +5 -0
  4. package/dist/contracts/index.js +6 -0
  5. package/dist/contracts/models.d.ts +451 -0
  6. package/dist/contracts/models.d.ts.map +1 -0
  7. package/dist/contracts/models.js +388 -0
  8. package/dist/contracts/models.js.map +1 -0
  9. package/dist/contracts/onboarding.d.ts +666 -0
  10. package/dist/contracts/onboarding.d.ts.map +1 -0
  11. package/dist/contracts/onboarding.js +402 -0
  12. package/dist/contracts/onboarding.js.map +1 -0
  13. package/dist/contracts/operations.d.ts +280 -0
  14. package/dist/contracts/operations.d.ts.map +1 -0
  15. package/dist/contracts/operations.js +151 -0
  16. package/dist/contracts/operations.js.map +1 -0
  17. package/dist/contracts/shared.d.ts +5 -0
  18. package/dist/contracts/shared.d.ts.map +1 -0
  19. package/dist/contracts/shared.js +6 -0
  20. package/dist/contracts/shared.js.map +1 -0
  21. package/dist/docs/index.d.ts +1 -0
  22. package/dist/docs/index.js +1 -0
  23. package/dist/docs/learning-journey.docblock.d.ts +1 -0
  24. package/dist/docs/learning-journey.docblock.js +136 -0
  25. package/dist/docs/learning-journey.docblock.js.map +1 -0
  26. package/dist/engines/index.d.ts +4 -0
  27. package/dist/engines/index.js +5 -0
  28. package/dist/engines/srs.d.ts +111 -0
  29. package/dist/engines/srs.d.ts.map +1 -0
  30. package/dist/engines/srs.js +219 -0
  31. package/dist/engines/srs.js.map +1 -0
  32. package/dist/engines/streak.d.ts +100 -0
  33. package/dist/engines/streak.d.ts.map +1 -0
  34. package/dist/engines/streak.js +194 -0
  35. package/dist/engines/streak.js.map +1 -0
  36. package/dist/engines/xp.d.ts +97 -0
  37. package/dist/engines/xp.d.ts.map +1 -0
  38. package/dist/engines/xp.js +213 -0
  39. package/dist/engines/xp.js.map +1 -0
  40. package/dist/entities/ai.d.ts +232 -0
  41. package/dist/entities/ai.d.ts.map +1 -0
  42. package/dist/entities/ai.js +376 -0
  43. package/dist/entities/ai.js.map +1 -0
  44. package/dist/entities/course.d.ts +184 -0
  45. package/dist/entities/course.d.ts.map +1 -0
  46. package/dist/entities/course.js +316 -0
  47. package/dist/entities/course.js.map +1 -0
  48. package/dist/entities/flashcard.d.ts +170 -0
  49. package/dist/entities/flashcard.d.ts.map +1 -0
  50. package/dist/entities/flashcard.js +249 -0
  51. package/dist/entities/flashcard.js.map +1 -0
  52. package/dist/entities/gamification.d.ts +238 -0
  53. package/dist/entities/gamification.d.ts.map +1 -0
  54. package/dist/entities/gamification.js +392 -0
  55. package/dist/entities/gamification.js.map +1 -0
  56. package/dist/entities/index.d.ts +629 -0
  57. package/dist/entities/index.d.ts.map +1 -0
  58. package/dist/entities/index.js +45 -0
  59. package/dist/entities/index.js.map +1 -0
  60. package/dist/entities/learner.d.ts +224 -0
  61. package/dist/entities/learner.d.ts.map +1 -0
  62. package/dist/entities/learner.js +365 -0
  63. package/dist/entities/learner.js.map +1 -0
  64. package/dist/entities/onboarding.d.ts +190 -0
  65. package/dist/entities/onboarding.d.ts.map +1 -0
  66. package/dist/entities/onboarding.js +307 -0
  67. package/dist/entities/onboarding.js.map +1 -0
  68. package/dist/entities/quiz.d.ts +220 -0
  69. package/dist/entities/quiz.d.ts.map +1 -0
  70. package/dist/entities/quiz.js +370 -0
  71. package/dist/entities/quiz.js.map +1 -0
  72. package/dist/events.d.ts +796 -0
  73. package/dist/events.d.ts.map +1 -0
  74. package/dist/events.js +690 -0
  75. package/dist/events.js.map +1 -0
  76. package/dist/index.d.ts +20 -0
  77. package/dist/index.js +22 -0
  78. package/dist/learning-journey.feature.d.ts +12 -0
  79. package/dist/learning-journey.feature.d.ts.map +1 -0
  80. package/dist/learning-journey.feature.js +150 -0
  81. package/dist/learning-journey.feature.js.map +1 -0
  82. package/dist/track-spec.d.ts +129 -0
  83. package/dist/track-spec.d.ts.map +1 -0
  84. package/dist/track-spec.js +0 -0
  85. package/package.json +98 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.d.ts","names":[],"sources":["../../src/contracts/operations.ts"],"sourcesContent":[],"mappings":";;;;;;;cAkBa,sDAAsB,0CAAA;EAAtB,QAAA,EAAA;IAgCX,IAAA,EAAA,2BAAA,CAAA,SAAA,CAAA,MAAA,EAAA,MAAA,CAAA;;EAhCiC,CAAA;;;UAAA,2BAAA,CAAA;;;;;;;;IAAA,IAAA,uCAAA,CAAA,MAAA,EAAA,MAAA,CAAA;IAqCtB,UAAA,EAAA,KAAA;EAgCX,CAAA;;;;EAhCiC,CAAA;;;;EAAA,CAAA;EAqCtB,SAAA,EAAA;IAgCX,IAAA,uCAAA,KAAA,EAAA,MAAA,CAAA;;;;IAhCmC,IAAA,uCAAA,KAAA,EAAA,MAAA,CAAA;;;;;AAqCrC;;cA1Ea,sDAAsB,0CAAA;;IA0EH,IAAA,EA1C9B,2BAAA,CAAA,SA0C8B,CAAA,MAAA,EAAA,MAAA,CAAA;;;;;;;;;;;2CAAA,CAAA;EAAA,OAAA,EAAA;IAuBnB,IAAA,EAjGsB,2BAAA,CAAA,SAmHjC,CAAA,OAAA,EAAA,OAAA,CAAA;IAAA,UAAA,EAAA,KAAA;;EAlBsC,QAAA,EAAA;;;;;;;;cA5D3B,wDAAwB,0CAAA;;UAgCnC,2BAAA,CAAA;;;;;;;;;;;;;UAhCmC,2BAAA,CAAA;;;;;;;;;;;cAqCxB,mBAuB2B,gCAvBR,aAuBQ,6BAvBR,WAuBQ,CAAA;EAAA,MAAA,EAAA;UALtC,2BAAA,CAAA;;;;;;;;;;;cAlB8B,2BAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAuBnB,2DAA2B,0CAAA;;UAkBtC,2BAAA,CAAA;;;;;;;cAlBsC,2BAAA,CAAA"}
@@ -0,0 +1,151 @@
1
+ import { LEARNING_JOURNEY_OWNERS } from "./shared.js";
2
+ import { CompleteLessonInput, EnrollInCourseInput, EnrollmentModel, GetDueCardsInput, GetDueCardsOutput, GetLearnerDashboardInput, LearnerDashboardModel, SubmitCardReviewInput, SuccessOutput } from "./models.js";
3
+ import { defineCommand, defineQuery } from "@contractspec/lib.contracts";
4
+
5
+ //#region src/contracts/operations.ts
6
+ /**
7
+ * Enroll in a course.
8
+ */
9
+ const EnrollInCourseContract = defineCommand({
10
+ meta: {
11
+ key: "learning.enroll",
12
+ version: 1,
13
+ stability: "stable",
14
+ owners: [...LEARNING_JOURNEY_OWNERS],
15
+ tags: ["learning", "enrollment"],
16
+ description: "Enroll in a course.",
17
+ goal: "Start learning a new course.",
18
+ context: "Called when a learner wants to start a course."
19
+ },
20
+ io: {
21
+ input: EnrollInCourseInput,
22
+ output: EnrollmentModel,
23
+ errors: {
24
+ COURSE_NOT_FOUND: {
25
+ description: "Course does not exist",
26
+ http: 404,
27
+ gqlCode: "COURSE_NOT_FOUND",
28
+ when: "Course ID is invalid"
29
+ },
30
+ ALREADY_ENROLLED: {
31
+ description: "Already enrolled in course",
32
+ http: 409,
33
+ gqlCode: "ALREADY_ENROLLED",
34
+ when: "Learner is already enrolled"
35
+ }
36
+ }
37
+ },
38
+ policy: { auth: "user" }
39
+ });
40
+ /**
41
+ * Complete a lesson.
42
+ */
43
+ const CompleteLessonContract = defineCommand({
44
+ meta: {
45
+ key: "learning.completeLesson",
46
+ version: 1,
47
+ stability: "stable",
48
+ owners: [...LEARNING_JOURNEY_OWNERS],
49
+ tags: ["learning", "progress"],
50
+ description: "Mark a lesson as completed.",
51
+ goal: "Record lesson completion and earn XP.",
52
+ context: "Called when a learner finishes a lesson."
53
+ },
54
+ io: {
55
+ input: CompleteLessonInput,
56
+ output: SuccessOutput,
57
+ errors: {
58
+ LESSON_NOT_FOUND: {
59
+ description: "Lesson does not exist",
60
+ http: 404,
61
+ gqlCode: "LESSON_NOT_FOUND",
62
+ when: "Lesson ID is invalid"
63
+ },
64
+ NOT_ENROLLED: {
65
+ description: "Not enrolled in course",
66
+ http: 403,
67
+ gqlCode: "NOT_ENROLLED",
68
+ when: "Learner is not enrolled in the course"
69
+ }
70
+ }
71
+ },
72
+ policy: { auth: "user" }
73
+ });
74
+ /**
75
+ * Submit a card review.
76
+ */
77
+ const SubmitCardReviewContract = defineCommand({
78
+ meta: {
79
+ key: "learning.submitCardReview",
80
+ version: 1,
81
+ stability: "stable",
82
+ owners: [...LEARNING_JOURNEY_OWNERS],
83
+ tags: ["learning", "flashcards"],
84
+ description: "Submit a flashcard review.",
85
+ goal: "Record review and update SRS schedule.",
86
+ context: "Called when reviewing flashcards."
87
+ },
88
+ io: {
89
+ input: SubmitCardReviewInput,
90
+ output: SuccessOutput,
91
+ errors: {
92
+ CARD_NOT_FOUND: {
93
+ description: "Card does not exist",
94
+ http: 404,
95
+ gqlCode: "CARD_NOT_FOUND",
96
+ when: "Card ID is invalid"
97
+ },
98
+ INVALID_RATING: {
99
+ description: "Invalid rating",
100
+ http: 400,
101
+ gqlCode: "INVALID_RATING",
102
+ when: "Rating must be AGAIN, HARD, GOOD, or EASY"
103
+ }
104
+ }
105
+ },
106
+ policy: { auth: "user" }
107
+ });
108
+ /**
109
+ * Get cards due for review.
110
+ */
111
+ const GetDueCardsContract = defineQuery({
112
+ meta: {
113
+ key: "learning.getDueCards",
114
+ version: 1,
115
+ stability: "stable",
116
+ owners: [...LEARNING_JOURNEY_OWNERS],
117
+ tags: ["learning", "flashcards"],
118
+ description: "Get flashcards due for review.",
119
+ goal: "Get the next batch of cards to review.",
120
+ context: "Called when starting a review session."
121
+ },
122
+ io: {
123
+ input: GetDueCardsInput,
124
+ output: GetDueCardsOutput
125
+ },
126
+ policy: { auth: "user" }
127
+ });
128
+ /**
129
+ * Get learner dashboard.
130
+ */
131
+ const GetLearnerDashboardContract = defineQuery({
132
+ meta: {
133
+ key: "learning.getDashboard",
134
+ version: 1,
135
+ stability: "stable",
136
+ owners: [...LEARNING_JOURNEY_OWNERS],
137
+ tags: ["learning", "dashboard"],
138
+ description: "Get learner dashboard data.",
139
+ goal: "Display learner progress and stats.",
140
+ context: "Called when viewing the learning dashboard."
141
+ },
142
+ io: {
143
+ input: GetLearnerDashboardInput,
144
+ output: LearnerDashboardModel
145
+ },
146
+ policy: { auth: "user" }
147
+ });
148
+
149
+ //#endregion
150
+ export { CompleteLessonContract, EnrollInCourseContract, GetDueCardsContract, GetLearnerDashboardContract, SubmitCardReviewContract };
151
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operations.js","names":[],"sources":["../../src/contracts/operations.ts"],"sourcesContent":["import { defineCommand, defineQuery } from '@contractspec/lib.contracts';\n\nimport {\n CompleteLessonInput,\n EnrollInCourseInput,\n EnrollmentModel,\n GetDueCardsInput,\n GetDueCardsOutput,\n GetLearnerDashboardInput,\n LearnerDashboardModel,\n SubmitCardReviewInput,\n SuccessOutput,\n} from './models';\nimport { LEARNING_JOURNEY_OWNERS } from './shared';\n\n/**\n * Enroll in a course.\n */\nexport const EnrollInCourseContract = defineCommand({\n meta: {\n key: 'learning.enroll',\n version: 1,\n stability: 'stable',\n owners: [...LEARNING_JOURNEY_OWNERS],\n tags: ['learning', 'enrollment'],\n description: 'Enroll in a course.',\n goal: 'Start learning a new course.',\n context: 'Called when a learner wants to start a course.',\n },\n io: {\n input: EnrollInCourseInput,\n output: EnrollmentModel,\n errors: {\n COURSE_NOT_FOUND: {\n description: 'Course does not exist',\n http: 404,\n gqlCode: 'COURSE_NOT_FOUND',\n when: 'Course ID is invalid',\n },\n ALREADY_ENROLLED: {\n description: 'Already enrolled in course',\n http: 409,\n gqlCode: 'ALREADY_ENROLLED',\n when: 'Learner is already enrolled',\n },\n },\n },\n policy: {\n auth: 'user',\n },\n});\n\n/**\n * Complete a lesson.\n */\nexport const CompleteLessonContract = defineCommand({\n meta: {\n key: 'learning.completeLesson',\n version: 1,\n stability: 'stable',\n owners: [...LEARNING_JOURNEY_OWNERS],\n tags: ['learning', 'progress'],\n description: 'Mark a lesson as completed.',\n goal: 'Record lesson completion and earn XP.',\n context: 'Called when a learner finishes a lesson.',\n },\n io: {\n input: CompleteLessonInput,\n output: SuccessOutput,\n errors: {\n LESSON_NOT_FOUND: {\n description: 'Lesson does not exist',\n http: 404,\n gqlCode: 'LESSON_NOT_FOUND',\n when: 'Lesson ID is invalid',\n },\n NOT_ENROLLED: {\n description: 'Not enrolled in course',\n http: 403,\n gqlCode: 'NOT_ENROLLED',\n when: 'Learner is not enrolled in the course',\n },\n },\n },\n policy: {\n auth: 'user',\n },\n});\n\n/**\n * Submit a card review.\n */\nexport const SubmitCardReviewContract = defineCommand({\n meta: {\n key: 'learning.submitCardReview',\n version: 1,\n stability: 'stable',\n owners: [...LEARNING_JOURNEY_OWNERS],\n tags: ['learning', 'flashcards'],\n description: 'Submit a flashcard review.',\n goal: 'Record review and update SRS schedule.',\n context: 'Called when reviewing flashcards.',\n },\n io: {\n input: SubmitCardReviewInput,\n output: SuccessOutput,\n errors: {\n CARD_NOT_FOUND: {\n description: 'Card does not exist',\n http: 404,\n gqlCode: 'CARD_NOT_FOUND',\n when: 'Card ID is invalid',\n },\n INVALID_RATING: {\n description: 'Invalid rating',\n http: 400,\n gqlCode: 'INVALID_RATING',\n when: 'Rating must be AGAIN, HARD, GOOD, or EASY',\n },\n },\n },\n policy: {\n auth: 'user',\n },\n});\n\n/**\n * Get cards due for review.\n */\nexport const GetDueCardsContract = defineQuery({\n meta: {\n key: 'learning.getDueCards',\n version: 1,\n stability: 'stable',\n owners: [...LEARNING_JOURNEY_OWNERS],\n tags: ['learning', 'flashcards'],\n description: 'Get flashcards due for review.',\n goal: 'Get the next batch of cards to review.',\n context: 'Called when starting a review session.',\n },\n io: {\n input: GetDueCardsInput,\n output: GetDueCardsOutput,\n },\n policy: {\n auth: 'user',\n },\n});\n\n/**\n * Get learner dashboard.\n */\nexport const GetLearnerDashboardContract = defineQuery({\n meta: {\n key: 'learning.getDashboard',\n version: 1,\n stability: 'stable',\n owners: [...LEARNING_JOURNEY_OWNERS],\n tags: ['learning', 'dashboard'],\n description: 'Get learner dashboard data.',\n goal: 'Display learner progress and stats.',\n context: 'Called when viewing the learning dashboard.',\n },\n io: {\n input: GetLearnerDashboardInput,\n output: LearnerDashboardModel,\n },\n policy: {\n auth: 'user',\n },\n});\n"],"mappings":";;;;;;;;AAkBA,MAAa,yBAAyB,cAAc;CAClD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,GAAG,wBAAwB;EACpC,MAAM,CAAC,YAAY,aAAa;EAChC,aAAa;EACb,MAAM;EACN,SAAS;EACV;CACD,IAAI;EACF,OAAO;EACP,QAAQ;EACR,QAAQ;GACN,kBAAkB;IAChB,aAAa;IACb,MAAM;IACN,SAAS;IACT,MAAM;IACP;GACD,kBAAkB;IAChB,aAAa;IACb,MAAM;IACN,SAAS;IACT,MAAM;IACP;GACF;EACF;CACD,QAAQ,EACN,MAAM,QACP;CACF,CAAC;;;;AAKF,MAAa,yBAAyB,cAAc;CAClD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,GAAG,wBAAwB;EACpC,MAAM,CAAC,YAAY,WAAW;EAC9B,aAAa;EACb,MAAM;EACN,SAAS;EACV;CACD,IAAI;EACF,OAAO;EACP,QAAQ;EACR,QAAQ;GACN,kBAAkB;IAChB,aAAa;IACb,MAAM;IACN,SAAS;IACT,MAAM;IACP;GACD,cAAc;IACZ,aAAa;IACb,MAAM;IACN,SAAS;IACT,MAAM;IACP;GACF;EACF;CACD,QAAQ,EACN,MAAM,QACP;CACF,CAAC;;;;AAKF,MAAa,2BAA2B,cAAc;CACpD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,GAAG,wBAAwB;EACpC,MAAM,CAAC,YAAY,aAAa;EAChC,aAAa;EACb,MAAM;EACN,SAAS;EACV;CACD,IAAI;EACF,OAAO;EACP,QAAQ;EACR,QAAQ;GACN,gBAAgB;IACd,aAAa;IACb,MAAM;IACN,SAAS;IACT,MAAM;IACP;GACD,gBAAgB;IACd,aAAa;IACb,MAAM;IACN,SAAS;IACT,MAAM;IACP;GACF;EACF;CACD,QAAQ,EACN,MAAM,QACP;CACF,CAAC;;;;AAKF,MAAa,sBAAsB,YAAY;CAC7C,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,GAAG,wBAAwB;EACpC,MAAM,CAAC,YAAY,aAAa;EAChC,aAAa;EACb,MAAM;EACN,SAAS;EACV;CACD,IAAI;EACF,OAAO;EACP,QAAQ;EACT;CACD,QAAQ,EACN,MAAM,QACP;CACF,CAAC;;;;AAKF,MAAa,8BAA8B,YAAY;CACrD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,WAAW;EACX,QAAQ,CAAC,GAAG,wBAAwB;EACpC,MAAM,CAAC,YAAY,YAAY;EAC/B,aAAa;EACb,MAAM;EACN,SAAS;EACV;CACD,IAAI;EACF,OAAO;EACP,QAAQ;EACT;CACD,QAAQ,EACN,MAAM,QACP;CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ //#region src/contracts/shared.d.ts
2
+ declare const LEARNING_JOURNEY_OWNERS: readonly ["modules.learning-journey"];
3
+ //#endregion
4
+ export { LEARNING_JOURNEY_OWNERS };
5
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","names":[],"sources":["../../src/contracts/shared.ts"],"sourcesContent":[],"mappings":";cAAa"}
@@ -0,0 +1,6 @@
1
+ //#region src/contracts/shared.ts
2
+ const LEARNING_JOURNEY_OWNERS = ["modules.learning-journey"];
3
+
4
+ //#endregion
5
+ export { LEARNING_JOURNEY_OWNERS };
6
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","names":[],"sources":["../../src/contracts/shared.ts"],"sourcesContent":["export const LEARNING_JOURNEY_OWNERS = ['modules.learning-journey'] as const;\n"],"mappings":";AAAA,MAAa,0BAA0B,CAAC,2BAA2B"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ import "./learning-journey.docblock.js";
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,136 @@
1
+ import { registerDocBlocks } from "@contractspec/lib.contracts/docs";
2
+
3
+ //#region src/docs/learning-journey.docblock.ts
4
+ registerDocBlocks([
5
+ {
6
+ id: "docs.learning-journey.engine",
7
+ title: "Learning Journey Engine",
8
+ summary: "Tracks learners, tracks/modules/steps, progress, quizzes, streaks, XP, and AI coaching hooks for product-integrated onboarding.",
9
+ kind: "reference",
10
+ visibility: "public",
11
+ route: "/docs/learning-journey/engine",
12
+ tags: [
13
+ "learning",
14
+ "onboarding",
15
+ "journey",
16
+ "education"
17
+ ],
18
+ body: `## Capabilities
19
+
20
+ - **Entities**: Learner, Track, Module, Step, Progress, Quiz, Flashcard, AI Coach, Gamification (XP, streaks, badges).
21
+ - **Contracts**: enroll/resume/advance steps, complete quizzes, record streaks, assign XP, fetch progress dashboards, onboarding list/progress/recordEvent.
22
+ - **Engines**: spaced-repetition (SRS), streak calculator, XP progression.
23
+ - **Events**: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded, onboarding.started/completed/step_completed.
24
+
25
+ ## Completion conditions
26
+ - Event-based: name/version/source + payload filter
27
+ - Count-based: require N events (optional time window)
28
+ - Time-bounded: must complete within a window; steps can unlock by day/hour
29
+ - SRS mastery: complete when cards/skills hit mastery thresholds (with required counts)
30
+
31
+ ## Usage
32
+
33
+ 1) Compose schema
34
+ - Include \`learningJourneySchemaContribution\` (entities export) in composition.
35
+
36
+ 2) Register contracts/events
37
+ - Import from \`@contractspec/module.learning-journey\` into your spec registry.
38
+
39
+ 3) Bind to product actions
40
+ - Tie \`Step\` completion conditions to domain events (e.g., deal.created, agent.run.completed, drill.session.completed).
41
+ - Trigger notifications via Notification Center and audits on completion.
42
+
43
+ 4) Gamification
44
+ - Use \`XP\` and \`Streak\` engines to update learner stats; emit analytics for UI.
45
+
46
+ ## Example
47
+
48
+ \`\`\`ts
49
+ import { learningJourneyEntities } from '@contractspec/module.learning-journey';
50
+ import { StreakEngine } from '@contractspec/module.learning-journey/engines';
51
+
52
+ const streak = new StreakEngine({ graceDays: 1 });
53
+ const updated = streak.compute({ lastActiveAt: new Date(), today: new Date() });
54
+ \`\`\`,
55
+
56
+ ## Guardrails
57
+
58
+ - Keep steps bound to real product events, not just button clicks.
59
+ - Avoid storing PII in content; keep org/user scoping for multi-tenant isolation.
60
+ - Emit analytics and audit logs for completions; respect \`prefers-reduced-motion\` in UIs consuming these specs.
61
+ - Track completion bonuses: \`completionXpBonus\`, \`completionBadgeKey\`, optional \`streakHoursWindow\` + \`streakBonusXp\`.
62
+ `
63
+ },
64
+ {
65
+ id: "docs.learning-journey.goal",
66
+ title: "Learning Journey — Goal",
67
+ summary: "Why the learning journey engine exists and the outcomes it targets.",
68
+ kind: "goal",
69
+ visibility: "public",
70
+ route: "/docs/learning-journey/goal",
71
+ tags: ["learning", "goal"],
72
+ body: `## Why it matters
73
+ - Provides a regenerable onboarding/education engine tied to product signals.
74
+ - Keeps tracks, steps, quizzes, and gamification consistent across surfaces.
75
+
76
+ ## Business/Product goal
77
+ - Drive activation and retention with measurable progress, SRS, and streaks.
78
+ - Allow product teams to adjust journeys safely via specs.
79
+
80
+ ## Success criteria
81
+ - Journey changes regenerate UI/API/events without drift.
82
+ - Analytics/audit hooks exist for completions and streaks.`
83
+ },
84
+ {
85
+ id: "docs.learning-journey.usage",
86
+ title: "Learning Journey — Usage",
87
+ summary: "How to compose, bind, and regenerate journeys safely.",
88
+ kind: "usage",
89
+ visibility: "public",
90
+ route: "/docs/learning-journey/usage",
91
+ tags: ["learning", "usage"],
92
+ body: `## Setup
93
+ 1) Include \`learningJourneyEntities\` in schema composition.
94
+ 2) Register contracts/events from \`@contractspec/module.learning-journey\`.
95
+ 3) Bind steps to real product events (e.g., deal.created, run.completed).
96
+
97
+ ## Extend & regenerate
98
+ 1) Update track/module/step definitions or quiz schemas in spec.
99
+ 2) Regenerate to sync UI/API/events; mark PII paths where needed.
100
+ 3) Use Feature Flags to trial new tracks or streak rules.
101
+
102
+ ## Guardrails
103
+ - Avoid hardcoded progression; keep engines declarative.
104
+ - Emit analytics/audit for completions; respect user locale/accessibility in presentations.
105
+ - Keep content free of PII; scope learners by org/tenant.`
106
+ },
107
+ {
108
+ id: "docs.learning-journey.constraints",
109
+ title: "Learning Journey — Constraints & Safety",
110
+ summary: "Internal guardrails for progression, telemetry, and regeneration semantics.",
111
+ kind: "reference",
112
+ visibility: "internal",
113
+ route: "/docs/learning-journey/constraints",
114
+ tags: [
115
+ "learning",
116
+ "constraints",
117
+ "internal"
118
+ ],
119
+ body: `## Constraints
120
+ - Progression (tracks/modules/steps) and engines (SRS, streaks, XP) must stay declarative in spec.
121
+ - Events to emit: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded.
122
+ - Regeneration should not change scoring/streak rules without explicit spec change.
123
+
124
+ ## PII & Telemetry
125
+ - Mark PII (learner identifiers) and redact in presentations; keep telemetry aggregated when possible.
126
+ - Respect accessibility (prefers-reduced-motion) in UIs consuming these specs.
127
+
128
+ ## Verification
129
+ - Add fixtures for streak/XP rule changes and quiz scoring.
130
+ - Ensure Notifications/Audit wiring persists for completions; analytics emitted for progress.
131
+ - Use Feature Flags to trial new tracks or reward rules; default safe/off.`
132
+ }
133
+ ]);
134
+
135
+ //#endregion
136
+ //# sourceMappingURL=learning-journey.docblock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learning-journey.docblock.js","names":[],"sources":["../../src/docs/learning-journey.docblock.ts"],"sourcesContent":["import type { DocBlock } from '@contractspec/lib.contracts/docs';\nimport { registerDocBlocks } from '@contractspec/lib.contracts/docs';\n\nconst learningJourneyDocBlocks: DocBlock[] = [\n {\n id: 'docs.learning-journey.engine',\n title: 'Learning Journey Engine',\n summary:\n 'Tracks learners, tracks/modules/steps, progress, quizzes, streaks, XP, and AI coaching hooks for product-integrated onboarding.',\n kind: 'reference',\n visibility: 'public',\n route: '/docs/learning-journey/engine',\n tags: ['learning', 'onboarding', 'journey', 'education'],\n body: `## Capabilities\n\n- **Entities**: Learner, Track, Module, Step, Progress, Quiz, Flashcard, AI Coach, Gamification (XP, streaks, badges).\n- **Contracts**: enroll/resume/advance steps, complete quizzes, record streaks, assign XP, fetch progress dashboards, onboarding list/progress/recordEvent.\n- **Engines**: spaced-repetition (SRS), streak calculator, XP progression.\n- **Events**: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded, onboarding.started/completed/step_completed.\n\n## Completion conditions\n- Event-based: name/version/source + payload filter\n- Count-based: require N events (optional time window)\n- Time-bounded: must complete within a window; steps can unlock by day/hour\n- SRS mastery: complete when cards/skills hit mastery thresholds (with required counts)\n\n## Usage\n\n1) Compose schema\n- Include \\`learningJourneySchemaContribution\\` (entities export) in composition.\n\n2) Register contracts/events\n- Import from \\`@contractspec/module.learning-journey\\` into your spec registry.\n\n3) Bind to product actions\n- Tie \\`Step\\` completion conditions to domain events (e.g., deal.created, agent.run.completed, drill.session.completed).\n- Trigger notifications via Notification Center and audits on completion.\n\n4) Gamification\n- Use \\`XP\\` and \\`Streak\\` engines to update learner stats; emit analytics for UI.\n\n## Example\n\n${'```'}ts\nimport { learningJourneyEntities } from '@contractspec/module.learning-journey';\nimport { StreakEngine } from '@contractspec/module.learning-journey/engines';\n\nconst streak = new StreakEngine({ graceDays: 1 });\nconst updated = streak.compute({ lastActiveAt: new Date(), today: new Date() });\n${'```'},\n\n## Guardrails\n\n- Keep steps bound to real product events, not just button clicks.\n- Avoid storing PII in content; keep org/user scoping for multi-tenant isolation.\n- Emit analytics and audit logs for completions; respect \\`prefers-reduced-motion\\` in UIs consuming these specs.\n- Track completion bonuses: \\`completionXpBonus\\`, \\`completionBadgeKey\\`, optional \\`streakHoursWindow\\` + \\`streakBonusXp\\`.\n`,\n },\n {\n id: 'docs.learning-journey.goal',\n title: 'Learning Journey — Goal',\n summary:\n 'Why the learning journey engine exists and the outcomes it targets.',\n kind: 'goal',\n visibility: 'public',\n route: '/docs/learning-journey/goal',\n tags: ['learning', 'goal'],\n body: `## Why it matters\n- Provides a regenerable onboarding/education engine tied to product signals.\n- Keeps tracks, steps, quizzes, and gamification consistent across surfaces.\n\n## Business/Product goal\n- Drive activation and retention with measurable progress, SRS, and streaks.\n- Allow product teams to adjust journeys safely via specs.\n\n## Success criteria\n- Journey changes regenerate UI/API/events without drift.\n- Analytics/audit hooks exist for completions and streaks.`,\n },\n {\n id: 'docs.learning-journey.usage',\n title: 'Learning Journey — Usage',\n summary: 'How to compose, bind, and regenerate journeys safely.',\n kind: 'usage',\n visibility: 'public',\n route: '/docs/learning-journey/usage',\n tags: ['learning', 'usage'],\n body: `## Setup\n1) Include \\`learningJourneyEntities\\` in schema composition.\n2) Register contracts/events from \\`@contractspec/module.learning-journey\\`.\n3) Bind steps to real product events (e.g., deal.created, run.completed).\n\n## Extend & regenerate\n1) Update track/module/step definitions or quiz schemas in spec.\n2) Regenerate to sync UI/API/events; mark PII paths where needed.\n3) Use Feature Flags to trial new tracks or streak rules.\n\n## Guardrails\n- Avoid hardcoded progression; keep engines declarative.\n- Emit analytics/audit for completions; respect user locale/accessibility in presentations.\n- Keep content free of PII; scope learners by org/tenant.`,\n },\n {\n id: 'docs.learning-journey.constraints',\n title: 'Learning Journey — Constraints & Safety',\n summary:\n 'Internal guardrails for progression, telemetry, and regeneration semantics.',\n kind: 'reference',\n visibility: 'internal',\n route: '/docs/learning-journey/constraints',\n tags: ['learning', 'constraints', 'internal'],\n body: `## Constraints\n- Progression (tracks/modules/steps) and engines (SRS, streaks, XP) must stay declarative in spec.\n- Events to emit: learner.enrolled, step.completed, quiz.scored, streak.reset, xp.awarded.\n- Regeneration should not change scoring/streak rules without explicit spec change.\n\n## PII & Telemetry\n- Mark PII (learner identifiers) and redact in presentations; keep telemetry aggregated when possible.\n- Respect accessibility (prefers-reduced-motion) in UIs consuming these specs.\n\n## Verification\n- Add fixtures for streak/XP rule changes and quiz scoring.\n- Ensure Notifications/Audit wiring persists for completions; analytics emitted for progress.\n- Use Feature Flags to trial new tracks or reward rules; default safe/off.`,\n },\n];\n\nregisterDocBlocks(learningJourneyDocBlocks);\n"],"mappings":";;;AAgIA,kBA7H6C;CAC3C;EACE,IAAI;EACJ,OAAO;EACP,SACE;EACF,MAAM;EACN,YAAY;EACZ,OAAO;EACP,MAAM;GAAC;GAAY;GAAc;GAAW;GAAY;EACxD,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6CP;CACD;EACE,IAAI;EACJ,OAAO;EACP,SACE;EACF,MAAM;EACN,YAAY;EACZ,OAAO;EACP,MAAM,CAAC,YAAY,OAAO;EAC1B,MAAM;;;;;;;;;;;EAWP;CACD;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,MAAM;EACN,YAAY;EACZ,OAAO;EACP,MAAM,CAAC,YAAY,QAAQ;EAC3B,MAAM;;;;;;;;;;;;;;EAcP;CACD;EACE,IAAI;EACJ,OAAO;EACP,SACE;EACF,MAAM;EACN,YAAY;EACZ,OAAO;EACP,MAAM;GAAC;GAAY;GAAe;GAAW;EAC7C,MAAM;;;;;;;;;;;;;EAaP;CACF,CAE0C"}
@@ -0,0 +1,4 @@
1
+ import { CardRating, DEFAULT_SRS_CONFIG, ReviewResult, SRSConfig, SRSEngine, SRSState, srsEngine } from "./srs.js";
2
+ import { DEFAULT_XP_CONFIG, XPActivityType, XPBreakdown, XPCalculationInput, XPConfig, XPEngine, XPResult, xpEngine } from "./xp.js";
3
+ import { DEFAULT_STREAK_CONFIG, StreakConfig, StreakEngine, StreakState, StreakUpdateResult, streakEngine } from "./streak.js";
4
+ export { CardRating, DEFAULT_SRS_CONFIG, DEFAULT_STREAK_CONFIG, DEFAULT_XP_CONFIG, ReviewResult, SRSConfig, SRSEngine, SRSState, StreakConfig, StreakEngine, StreakState, StreakUpdateResult, XPActivityType, XPBreakdown, XPCalculationInput, XPConfig, XPEngine, XPResult, srsEngine, streakEngine, xpEngine };
@@ -0,0 +1,5 @@
1
+ import { DEFAULT_SRS_CONFIG, SRSEngine, srsEngine } from "./srs.js";
2
+ import { DEFAULT_XP_CONFIG, XPEngine, xpEngine } from "./xp.js";
3
+ import { DEFAULT_STREAK_CONFIG, StreakEngine, streakEngine } from "./streak.js";
4
+
5
+ export { DEFAULT_SRS_CONFIG, DEFAULT_STREAK_CONFIG, DEFAULT_XP_CONFIG, SRSEngine, StreakEngine, XPEngine, srsEngine, streakEngine, xpEngine };
@@ -0,0 +1,111 @@
1
+ //#region src/engines/srs.d.ts
2
+ /**
3
+ * Spaced Repetition System (SRS) Engine
4
+ *
5
+ * Implements the SM-2 algorithm variant for optimal flashcard scheduling.
6
+ *
7
+ * The algorithm calculates the optimal time to review a card based on:
8
+ * - User's rating of recall difficulty (again, hard, good, easy)
9
+ * - Current interval between reviews
10
+ * - Ease factor (how easy the card is for this user)
11
+ * - Number of successful repetitions
12
+ */
13
+ type CardRating = 'AGAIN' | 'HARD' | 'GOOD' | 'EASY';
14
+ interface SRSState {
15
+ /** Current interval in days */
16
+ interval: number;
17
+ /** Ease factor (typically 1.3 to 2.5+) */
18
+ easeFactor: number;
19
+ /** Number of successful repetitions */
20
+ repetitions: number;
21
+ /** Current learning step (for new cards) */
22
+ learningStep: number;
23
+ /** Whether card has graduated to review phase */
24
+ isGraduated: boolean;
25
+ /** Whether card is being relearned after a lapse */
26
+ isRelearning: boolean;
27
+ /** Number of times card was forgotten */
28
+ lapses: number;
29
+ }
30
+ interface ReviewResult {
31
+ /** New interval in days */
32
+ interval: number;
33
+ /** New ease factor */
34
+ easeFactor: number;
35
+ /** New repetition count */
36
+ repetitions: number;
37
+ /** Next review date */
38
+ nextReviewAt: Date;
39
+ /** New learning step */
40
+ learningStep: number;
41
+ /** Whether card has graduated */
42
+ isGraduated: boolean;
43
+ /** Whether card is being relearned */
44
+ isRelearning: boolean;
45
+ /** Updated lapse count */
46
+ lapses: number;
47
+ }
48
+ interface SRSConfig {
49
+ /** Learning steps in minutes [1, 10] = 1 min, 10 min */
50
+ learningSteps: number[];
51
+ /** Graduating interval in days */
52
+ graduatingInterval: number;
53
+ /** Easy interval (for easy button on new cards) */
54
+ easyInterval: number;
55
+ /** Relearning steps in minutes */
56
+ relearningSteps: number[];
57
+ /** Minimum ease factor */
58
+ minEaseFactor: number;
59
+ /** Maximum interval in days */
60
+ maxInterval: number;
61
+ /** Interval modifier (1.0 = 100%) */
62
+ intervalModifier: number;
63
+ /** New cards interval modifier */
64
+ newIntervalModifier: number;
65
+ /** Hard interval modifier */
66
+ hardIntervalModifier: number;
67
+ /** Easy bonus modifier */
68
+ easyBonus: number;
69
+ }
70
+ declare const DEFAULT_SRS_CONFIG: SRSConfig;
71
+ declare class SRSEngine {
72
+ private config;
73
+ constructor(config?: Partial<SRSConfig>);
74
+ /**
75
+ * Calculate the next review state based on rating.
76
+ */
77
+ calculateNextReview(state: SRSState, rating: CardRating, now?: Date): ReviewResult;
78
+ /**
79
+ * Get initial SRS state for a new card.
80
+ */
81
+ getInitialState(): SRSState;
82
+ /**
83
+ * Check if a card is due for review.
84
+ */
85
+ isDue(nextReviewAt: Date, now?: Date): boolean;
86
+ /**
87
+ * Calculate overdue days (negative if not yet due).
88
+ */
89
+ getOverdueDays(nextReviewAt: Date, now?: Date): number;
90
+ /**
91
+ * Handle learning phase (new cards).
92
+ */
93
+ private handleLearningCard;
94
+ /**
95
+ * Handle relearning phase (lapsed cards).
96
+ */
97
+ private handleRelearningCard;
98
+ /**
99
+ * Handle review phase (graduated cards).
100
+ */
101
+ private handleReviewCard;
102
+ private addMinutes;
103
+ private addDays;
104
+ }
105
+ /**
106
+ * Default SRS engine instance.
107
+ */
108
+ declare const srsEngine: SRSEngine;
109
+ //#endregion
110
+ export { CardRating, DEFAULT_SRS_CONFIG, ReviewResult, SRSConfig, SRSEngine, SRSState, srsEngine };
111
+ //# sourceMappingURL=srs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"srs.d.ts","names":[],"sources":["../../src/engines/srs.ts"],"sourcesContent":[],"mappings":";;AAcA;AAEA;AAiBA;AAmBA;AAyBA;AAeA;;;;;AAaS,KA3FG,UAAA,GA2FH,OAAA,GAAA,MAAA,GAAA,MAAA,GAAA,MAAA;AACJ,UA1FY,QAAA,CA0FZ;EAkBgB;EAeC,QAAA,EAAA,MAAA;EAAW;EAOF,UAAA,EAAA,MAAA;EAAW;EAAiB,WAAA,EAAA,MAAA;EA4O9C;;;;;;;;;UA7VI,YAAA;;;;;;;;gBAQD;;;;;;;;;;UAWC,SAAA;;;;;;;;;;;;;;;;;;;;;;cAyBJ,oBAAoB;cAepB,SAAA;;uBAGS,QAAQ;;;;6BAQnB,kBACC,kBACH,OACJ;;;;qBAkBgB;;;;sBAeC,YAAW;;;;+BAOF,YAAW;;;;;;;;;;;;;;;;;;;cA4O7B,WAAS"}