@contractspec/example.learning-journey-duo-drills 3.7.6 → 3.7.10

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.
@@ -3,7 +3,7 @@ $ bun run prebuild && bun run build:bundle && bun run build:types
3
3
  $ contractspec-bun-build prebuild
4
4
  $ contractspec-bun-build transpile
5
5
  [contractspec-bun-build] transpile target=bun root=src entries=6 noBundle=false
6
- Bundled 6 modules in 28ms
6
+ Bundled 6 modules in 23ms
7
7
 
8
8
  docs/duo-drills.docblock.js 1.45 KB (entry point)
9
9
  ./index.js 4.78 KB (entry point)
@@ -13,7 +13,7 @@ Bundled 6 modules in 28ms
13
13
  ./track.js 1.80 KB (entry point)
14
14
 
15
15
  [contractspec-bun-build] transpile target=node root=src entries=6 noBundle=false
16
- Bundled 6 modules in 36ms
16
+ Bundled 6 modules in 20ms
17
17
 
18
18
  docs/duo-drills.docblock.js 1.43 KB (entry point)
19
19
  ./index.js 4.76 KB (entry point)
@@ -23,7 +23,7 @@ Bundled 6 modules in 36ms
23
23
  ./track.js 1.79 KB (entry point)
24
24
 
25
25
  [contractspec-bun-build] transpile target=browser root=src entries=6 noBundle=false
26
- Bundled 6 modules in 53ms
26
+ Bundled 6 modules in 14ms
27
27
 
28
28
  docs/duo-drills.docblock.js 1.43 KB (entry point)
29
29
  ./index.js 4.76 KB (entry point)
package/AGENTS.md CHANGED
@@ -1,31 +1,53 @@
1
- # AI Agent Guide -- `@contractspec/example.learning-journey-duo-drills`
1
+ # AI Agent Guide `@contractspec/example.learning-journey-duo-drills`
2
2
 
3
3
  Scope: `packages/examples/learning-journey-duo-drills/*`
4
4
 
5
- Demonstrates drill-based learning with spaced repetition (SRS), XP rewards, and streak hooks.
5
+ Drill-based learning journey example with SRS, XP, and streak hooks.
6
6
 
7
7
  ## Quick Context
8
8
 
9
- - **Layer**: example
10
- - **Related Packages**: `module.learning-journey`, `lib.contracts-spec`
9
+ - Layer: `example`.
10
+ - Package visibility: published package.
11
+ - Primary consumers are example explorers, template authors, and documentation readers.
12
+ - Related packages: `@contractspec/lib.contracts-spec`, `@contractspec/module.learning-journey`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
11
13
 
12
- ## What This Demonstrates
14
+ ## Architecture
13
15
 
14
- - Drill-based learning track with SRS scheduling
15
- - XP and streak gamification hooks
16
- - Lightweight track + example pattern
17
- - Integration with the learning-journey module
16
+ - `src/docs/` contains docblocks and documentation-facing exports.
17
+ - `src/example.ts` is the runnable example entrypoint.
18
+ - `src/index.ts` is the root public barrel and package entrypoint.
19
+ - `src/learning-journey-duo-drills.feature.ts` defines a feature entrypoint.
20
+ - `src/track.test.ts` is part of the package's public or composition surface.
21
+ - `src/track.ts` is part of the package's public or composition surface.
18
22
 
19
- ## Public Exports
23
+ ## Public Surface
20
24
 
21
- - `.` -- root barrel
22
- - `./docs` -- DocBlock documentation
23
- - `./example` -- runnable example entry point
24
- - `./track` -- duo drills track definition
25
+ - Export `.` resolves through `./src/index.ts`.
26
+ - Export `./docs` resolves through `./src/docs/index.ts`.
27
+ - Export `./docs/duo-drills.docblock` resolves through `./src/docs/duo-drills.docblock.ts`.
28
+ - Export `./example` resolves through `./src/example.ts`.
29
+ - Export `./learning-journey-duo-drills.feature` resolves through `./src/learning-journey-duo-drills.feature.ts`.
30
+ - Export `./track` resolves through `./src/track.ts`.
31
+
32
+ ## Guardrails
33
+
34
+ - Keep the example package demonstrative, buildable, and aligned with the exported feature surface.
35
+ - Do not add hidden production assumptions that are not actually implemented in the example.
36
+ - Changes here can affect downstream packages such as `@contractspec/lib.contracts-spec`, `@contractspec/module.learning-journey`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
37
+ - Changes here can affect downstream packages such as `@contractspec/lib.contracts-spec`, `@contractspec/module.learning-journey`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
25
38
 
26
39
  ## Local Commands
27
40
 
28
- - Build: `bun run build`
29
- - Dev: `bun run dev`
30
- - Test: `bun test`
31
- - Typecheck: `bun run typecheck`
41
+ - `bun run dev` — contractspec-bun-build dev
42
+ - `bun run build`bun run prebuild && bun run build:bundle && bun run build:types
43
+ - `bun run test`bun test
44
+ - `bun run lint` — bun lint:fix
45
+ - `bun run lint:check` — biome check .
46
+ - `bun run lint:fix` — biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .
47
+ - `bun run typecheck` — tsc --noEmit
48
+ - `bun run publish:pkg` — bun publish --tolerate-republish --ignore-scripts --verbose
49
+ - `bun run publish:pkg:canary` — bun publish:pkg --tag canary
50
+ - `bun run clean` — rimraf dist .turbo
51
+ - `bun run build:bundle` — contractspec-bun-build transpile
52
+ - `bun run build:types` — contractspec-bun-build types
53
+ - `bun run prebuild` — contractspec-bun-build prebuild
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @contractspec/example.learning-journey-duo-drills
2
2
 
3
+ ## 3.7.10
4
+
5
+ ### Patch Changes
6
+
7
+ - 1a44cb6: feat: improve examples to increase coverage of Contracts type
8
+ - Updated dependencies [1a44cb6]
9
+ - @contractspec/module.learning-journey@3.7.10
10
+ - @contractspec/lib.contracts-spec@4.1.2
11
+
12
+ ## 3.7.9
13
+
14
+ ### Patch Changes
15
+
16
+ - fix: release
17
+ - Updated dependencies
18
+ - @contractspec/module.learning-journey@3.7.9
19
+ - @contractspec/lib.contracts-spec@4.1.1
20
+
3
21
  ## 3.7.6
4
22
 
5
23
  ### Patch Changes
@@ -708,14 +726,17 @@
708
726
  feat: Contract layers support (features, examples, app-configs)
709
727
 
710
728
  ### New CLI Commands
729
+
711
730
  - `contractspec list layers` - List all contract layers with filtering
712
731
 
713
732
  ### Enhanced Commands
733
+
714
734
  - `contractspec ci` - New `layers` check category validates features/examples/config
715
735
  - `contractspec doctor` - New `layers` health checks
716
736
  - `contractspec integrity` - Now shows layer statistics
717
737
 
718
738
  ### New APIs
739
+
719
740
  - `discoverLayers()` - Scan workspace for all layer files
720
741
  - `scanExampleSource()` - Parse ExampleSpec from source code
721
742
  - `isExampleFile()` - Check if file is an example spec
package/README.md CHANGED
@@ -1,41 +1,69 @@
1
1
  # @contractspec/example.learning-journey-duo-drills
2
2
 
3
- Website: https://contractspec.io/
3
+ Website: https://contractspec.io
4
4
 
5
+ **Drill-based learning journey example with SRS, XP, and streak hooks.**
5
6
 
6
- Drill-based learning journey example that demonstrates short SRS-driven drills, XP, and streak hooks. Tracks are defined in `src/track.ts` and can be reused for language, finance, or ContractSpec concept drills.
7
+ ## What This Demonstrates
7
8
 
8
- ## What it shows
9
+ - Drill-based learning track with SRS scheduling.
10
+ - XP and streak gamification hooks.
11
+ - Lightweight track + example pattern.
12
+ - Integration with the learning-journey module.
13
+ - `src/docs/` contains docblocks and documentation-facing exports.
14
+ - `src/docs/` contains docblocks and documentation-facing exports.
9
15
 
10
- - Event-driven steps (`drill.session.completed`, `drill.card.mastered`)
11
- - Count-based completion (reach accuracy threshold across multiple sessions)
12
- - SRS mastery-based completion (master N cards in a skill)
13
- - XP per card/session and daily streak (via registry progress logic)
16
+ ## Running Locally
14
17
 
15
- ## How to run
18
+ From `packages/examples/learning-journey-duo-drills`:
19
+ - `bun run dev`
20
+ - `bun run build`
21
+ - `bun run test`
22
+ - `bun run typecheck`
16
23
 
17
- 1. Build dependencies: `bun install`
18
- 2. Run tests: `bun test packages/examples/learning-journey-duo-drills`
19
- 3. Use the registry helper to emit events:
24
+ ## Usage
20
25
 
21
- ```ts
22
- import {
23
- recordEvent,
24
- getProgress,
25
- } from '@contractspec/example.learning-journey-registry/api';
26
- import { drillTracks } from '@contractspec/example.learning-journey-duo-drills/track';
26
+ Use `@contractspec/example.learning-journey-duo-drills` as a reference implementation, or import its exported surfaces into a workspace that composes ContractSpec examples and bundles.
27
27
 
28
- recordEvent({
29
- name: 'drill.session.completed',
30
- learnerId: 'u1',
31
- payload: { accuracyBucket: 'high' },
32
- });
33
- // ...
34
- const progress = getProgress('drills_language_basics', 'u1');
35
- ```
28
+ ## Architecture
36
29
 
37
- ## Adapting
30
+ - `src/docs/` contains docblocks and documentation-facing exports.
31
+ - `src/example.ts` is the runnable example entrypoint.
32
+ - `src/index.ts` is the root public barrel and package entrypoint.
33
+ - `src/learning-journey-duo-drills.feature.ts` defines a feature entrypoint.
34
+ - `src/track.test.ts` is part of the package's public or composition surface.
35
+ - `src/track.ts` is part of the package's public or composition surface.
38
36
 
39
- - Swap skills/cards to represent language vocab, budgeting basics, or ContractSpec concepts.
40
- - Tune count thresholds, mastery requirements, and XP rewards per step.
41
- - Wire real drill events into the registry or your own progression handler.
37
+ ## Public Entry Points
38
+
39
+ - Export `.` resolves through `./src/index.ts`.
40
+ - Export `./docs` resolves through `./src/docs/index.ts`.
41
+ - Export `./docs/duo-drills.docblock` resolves through `./src/docs/duo-drills.docblock.ts`.
42
+ - Export `./example` resolves through `./src/example.ts`.
43
+ - Export `./learning-journey-duo-drills.feature` resolves through `./src/learning-journey-duo-drills.feature.ts`.
44
+ - Export `./track` resolves through `./src/track.ts`.
45
+
46
+ ## Local Commands
47
+
48
+ - `bun run dev` — contractspec-bun-build dev
49
+ - `bun run build` — bun run prebuild && bun run build:bundle && bun run build:types
50
+ - `bun run test` — bun test
51
+ - `bun run lint` — bun lint:fix
52
+ - `bun run lint:check` — biome check .
53
+ - `bun run lint:fix` — biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .
54
+ - `bun run typecheck` — tsc --noEmit
55
+ - `bun run publish:pkg` — bun publish --tolerate-republish --ignore-scripts --verbose
56
+ - `bun run publish:pkg:canary` — bun publish:pkg --tag canary
57
+ - `bun run clean` — rimraf dist .turbo
58
+ - `bun run build:bundle` — contractspec-bun-build transpile
59
+ - `bun run build:types` — contractspec-bun-build types
60
+ - `bun run prebuild` — contractspec-bun-build prebuild
61
+
62
+ ## Recent Updates
63
+
64
+ - Replace eslint+prettier by biomejs to optimize speed.
65
+ - Missing contract layers.
66
+
67
+ ## Notes
68
+
69
+ - Works alongside `@contractspec/lib.contracts-spec`, `@contractspec/module.learning-journey`, `@contractspec/tool.bun`, `@contractspec/tool.typescript`.
@@ -58,6 +58,22 @@ var example = defineExample({
58
58
  });
59
59
  var example_default = example;
60
60
 
61
+ // src/learning-journey-duo-drills.feature.ts
62
+ import { defineFeature } from "@contractspec/lib.contracts-spec";
63
+ var LearningJourneyDuoDrillsFeature = defineFeature({
64
+ meta: {
65
+ key: "learning-journey-duo-drills",
66
+ version: "1.0.0",
67
+ title: "Learning Journey: Duo Drills",
68
+ description: "Drill-based learning with spaced repetition, XP, and streaks",
69
+ domain: "learning-journey",
70
+ owners: ["@examples"],
71
+ tags: ["learning", "drills", "srs", "gamification"],
72
+ stability: "experimental"
73
+ },
74
+ docs: ["docs.learning-journey.duo-drills"]
75
+ });
76
+
61
77
  // src/track.ts
62
78
  var drillsLanguageBasicsTrack = {
63
79
  id: "drills_language_basics",
@@ -116,22 +132,6 @@ var drillsLanguageBasicsTrack = {
116
132
  var drillTracks = [
117
133
  drillsLanguageBasicsTrack
118
134
  ];
119
-
120
- // src/learning-journey-duo-drills.feature.ts
121
- import { defineFeature } from "@contractspec/lib.contracts-spec";
122
- var LearningJourneyDuoDrillsFeature = defineFeature({
123
- meta: {
124
- key: "learning-journey-duo-drills",
125
- version: "1.0.0",
126
- title: "Learning Journey: Duo Drills",
127
- description: "Drill-based learning with spaced repetition, XP, and streaks",
128
- domain: "learning-journey",
129
- owners: ["@examples"],
130
- tags: ["learning", "drills", "srs", "gamification"],
131
- stability: "experimental"
132
- },
133
- docs: ["docs.learning-journey.duo-drills"]
134
- });
135
135
  export {
136
136
  example_default as example,
137
137
  drillsLanguageBasicsTrack,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './track';
2
- export * from './learning-journey-duo-drills.feature';
3
1
  export { default as example } from './example';
2
+ export * from './learning-journey-duo-drills.feature';
3
+ export * from './track';
4
4
  import './docs';
package/dist/index.js CHANGED
@@ -59,6 +59,22 @@ var example = defineExample({
59
59
  });
60
60
  var example_default = example;
61
61
 
62
+ // src/learning-journey-duo-drills.feature.ts
63
+ import { defineFeature } from "@contractspec/lib.contracts-spec";
64
+ var LearningJourneyDuoDrillsFeature = defineFeature({
65
+ meta: {
66
+ key: "learning-journey-duo-drills",
67
+ version: "1.0.0",
68
+ title: "Learning Journey: Duo Drills",
69
+ description: "Drill-based learning with spaced repetition, XP, and streaks",
70
+ domain: "learning-journey",
71
+ owners: ["@examples"],
72
+ tags: ["learning", "drills", "srs", "gamification"],
73
+ stability: "experimental"
74
+ },
75
+ docs: ["docs.learning-journey.duo-drills"]
76
+ });
77
+
62
78
  // src/track.ts
63
79
  var drillsLanguageBasicsTrack = {
64
80
  id: "drills_language_basics",
@@ -117,22 +133,6 @@ var drillsLanguageBasicsTrack = {
117
133
  var drillTracks = [
118
134
  drillsLanguageBasicsTrack
119
135
  ];
120
-
121
- // src/learning-journey-duo-drills.feature.ts
122
- import { defineFeature } from "@contractspec/lib.contracts-spec";
123
- var LearningJourneyDuoDrillsFeature = defineFeature({
124
- meta: {
125
- key: "learning-journey-duo-drills",
126
- version: "1.0.0",
127
- title: "Learning Journey: Duo Drills",
128
- description: "Drill-based learning with spaced repetition, XP, and streaks",
129
- domain: "learning-journey",
130
- owners: ["@examples"],
131
- tags: ["learning", "drills", "srs", "gamification"],
132
- stability: "experimental"
133
- },
134
- docs: ["docs.learning-journey.duo-drills"]
135
- });
136
136
  export {
137
137
  example_default as example,
138
138
  drillsLanguageBasicsTrack,
@@ -58,6 +58,22 @@ var example = defineExample({
58
58
  });
59
59
  var example_default = example;
60
60
 
61
+ // src/learning-journey-duo-drills.feature.ts
62
+ import { defineFeature } from "@contractspec/lib.contracts-spec";
63
+ var LearningJourneyDuoDrillsFeature = defineFeature({
64
+ meta: {
65
+ key: "learning-journey-duo-drills",
66
+ version: "1.0.0",
67
+ title: "Learning Journey: Duo Drills",
68
+ description: "Drill-based learning with spaced repetition, XP, and streaks",
69
+ domain: "learning-journey",
70
+ owners: ["@examples"],
71
+ tags: ["learning", "drills", "srs", "gamification"],
72
+ stability: "experimental"
73
+ },
74
+ docs: ["docs.learning-journey.duo-drills"]
75
+ });
76
+
61
77
  // src/track.ts
62
78
  var drillsLanguageBasicsTrack = {
63
79
  id: "drills_language_basics",
@@ -116,22 +132,6 @@ var drillsLanguageBasicsTrack = {
116
132
  var drillTracks = [
117
133
  drillsLanguageBasicsTrack
118
134
  ];
119
-
120
- // src/learning-journey-duo-drills.feature.ts
121
- import { defineFeature } from "@contractspec/lib.contracts-spec";
122
- var LearningJourneyDuoDrillsFeature = defineFeature({
123
- meta: {
124
- key: "learning-journey-duo-drills",
125
- version: "1.0.0",
126
- title: "Learning Journey: Duo Drills",
127
- description: "Drill-based learning with spaced repetition, XP, and streaks",
128
- domain: "learning-journey",
129
- owners: ["@examples"],
130
- tags: ["learning", "drills", "srs", "gamification"],
131
- stability: "experimental"
132
- },
133
- docs: ["docs.learning-journey.duo-drills"]
134
- });
135
135
  export {
136
136
  example_default as example,
137
137
  drillsLanguageBasicsTrack,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractspec/example.learning-journey-duo-drills",
3
- "version": "3.7.6",
3
+ "version": "3.7.10",
4
4
  "description": "Drill-based learning journey example with SRS, XP, and streak hooks.",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -57,20 +57,20 @@
57
57
  "dev": "contractspec-bun-build dev",
58
58
  "clean": "rimraf dist .turbo",
59
59
  "lint": "bun lint:fix",
60
- "lint:fix": "eslint src --fix",
61
- "lint:check": "eslint src",
60
+ "lint:fix": "biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .",
61
+ "lint:check": "biome check .",
62
62
  "test": "bun test",
63
63
  "prebuild": "contractspec-bun-build prebuild",
64
64
  "typecheck": "tsc --noEmit"
65
65
  },
66
66
  "dependencies": {
67
- "@contractspec/module.learning-journey": "3.7.6",
68
- "@contractspec/lib.contracts-spec": "3.7.6"
67
+ "@contractspec/module.learning-journey": "3.7.10",
68
+ "@contractspec/lib.contracts-spec": "4.1.2"
69
69
  },
70
70
  "devDependencies": {
71
- "@contractspec/tool.typescript": "3.7.6",
71
+ "@contractspec/tool.typescript": "3.7.8",
72
72
  "typescript": "^5.9.3",
73
- "@contractspec/tool.bun": "3.7.6"
73
+ "@contractspec/tool.bun": "3.7.8"
74
74
  },
75
75
  "publishConfig": {
76
76
  "exports": {
@@ -2,16 +2,16 @@ import type { DocBlock } from '@contractspec/lib.contracts-spec/docs';
2
2
  import { registerDocBlocks } from '@contractspec/lib.contracts-spec/docs';
3
3
 
4
4
  const duoDrillsDocBlocks: DocBlock[] = [
5
- {
6
- id: 'docs.learning-journey.duo-drills',
7
- title: 'Learning Journey — Duo Drills',
8
- summary:
9
- 'Short drill/SRS example with XP and streak hooks for language, finance, or ContractSpec concept drills.',
10
- kind: 'reference',
11
- visibility: 'public',
12
- route: '/docs/learning-journey/duo-drills',
13
- tags: ['learning', 'drills', 'srs', 'xp'],
14
- body: `## Track
5
+ {
6
+ id: 'docs.learning-journey.duo-drills',
7
+ title: 'Learning Journey — Duo Drills',
8
+ summary:
9
+ 'Short drill/SRS example with XP and streak hooks for language, finance, or ContractSpec concept drills.',
10
+ kind: 'reference',
11
+ visibility: 'public',
12
+ route: '/docs/learning-journey/duo-drills',
13
+ tags: ['learning', 'drills', 'srs', 'xp'],
14
+ body: `## Track
15
15
  - **Key**: \`drills_language_basics\`
16
16
  - **Persona**: learner running quick drills (language/finance/spec concepts)
17
17
  - **Goal**: complete first session, maintain high-accuracy sessions, master cards in the first skill
@@ -27,7 +27,7 @@ XP: 20 + 30 + 40. Streak: daily session completion can be used to drive streak r
27
27
  - Tracks export from \`@contractspec/example.learning-journey-duo-drills/track\`.
28
28
  - Use registry helper \`recordEvent\` to advance steps from drill/session events.
29
29
  - SRS mastery events should include payload: \`{ skillId, mastery }\`.`,
30
- },
30
+ },
31
31
  ];
32
32
 
33
33
  registerDocBlocks(duoDrillsDocBlocks);
package/src/example.ts CHANGED
@@ -1,31 +1,31 @@
1
1
  import { defineExample } from '@contractspec/lib.contracts-spec';
2
2
 
3
3
  const example = defineExample({
4
- meta: {
5
- key: 'learning-journey-duo-drills',
6
- version: '1.0.0',
7
- title: 'Learning Journey — Duo Drills',
8
- description:
9
- 'Short drill/SRS example with XP and streak hooks for language, finance, or ContractSpec concept drills.',
10
- kind: 'template',
11
- visibility: 'public',
12
- stability: 'experimental',
13
- owners: ['@platform.core'],
14
- tags: ['learning', 'drills', 'srs', 'xp'],
15
- },
16
- docs: {
17
- rootDocId: 'docs.learning-journey.duo-drills',
18
- },
19
- entrypoints: {
20
- packageName: '@contractspec/example.learning-journey-duo-drills',
21
- docs: './docs',
22
- },
23
- surfaces: {
24
- templates: true,
25
- sandbox: { enabled: true, modes: ['playground', 'markdown'] },
26
- studio: { enabled: true, installable: true },
27
- mcp: { enabled: true },
28
- },
4
+ meta: {
5
+ key: 'learning-journey-duo-drills',
6
+ version: '1.0.0',
7
+ title: 'Learning Journey — Duo Drills',
8
+ description:
9
+ 'Short drill/SRS example with XP and streak hooks for language, finance, or ContractSpec concept drills.',
10
+ kind: 'template',
11
+ visibility: 'public',
12
+ stability: 'experimental',
13
+ owners: ['@platform.core'],
14
+ tags: ['learning', 'drills', 'srs', 'xp'],
15
+ },
16
+ docs: {
17
+ rootDocId: 'docs.learning-journey.duo-drills',
18
+ },
19
+ entrypoints: {
20
+ packageName: '@contractspec/example.learning-journey-duo-drills',
21
+ docs: './docs',
22
+ },
23
+ surfaces: {
24
+ templates: true,
25
+ sandbox: { enabled: true, modes: ['playground', 'markdown'] },
26
+ studio: { enabled: true, installable: true },
27
+ mcp: { enabled: true },
28
+ },
29
29
  });
30
30
 
31
31
  export default example;
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './track';
2
- export * from './learning-journey-duo-drills.feature';
3
1
  export { default as example } from './example';
2
+ export * from './learning-journey-duo-drills.feature';
3
+ export * from './track';
4
4
  import './docs';
@@ -1,16 +1,16 @@
1
1
  import { defineFeature } from '@contractspec/lib.contracts-spec';
2
2
 
3
3
  export const LearningJourneyDuoDrillsFeature = defineFeature({
4
- meta: {
5
- key: 'learning-journey-duo-drills',
6
- version: '1.0.0',
7
- title: 'Learning Journey: Duo Drills',
8
- description: 'Drill-based learning with spaced repetition, XP, and streaks',
9
- domain: 'learning-journey',
10
- owners: ['@examples'],
11
- tags: ['learning', 'drills', 'srs', 'gamification'],
12
- stability: 'experimental',
13
- },
4
+ meta: {
5
+ key: 'learning-journey-duo-drills',
6
+ version: '1.0.0',
7
+ title: 'Learning Journey: Duo Drills',
8
+ description: 'Drill-based learning with spaced repetition, XP, and streaks',
9
+ domain: 'learning-journey',
10
+ owners: ['@examples'],
11
+ tags: ['learning', 'drills', 'srs', 'gamification'],
12
+ stability: 'experimental',
13
+ },
14
14
 
15
- docs: ['docs.learning-journey.duo-drills'],
15
+ docs: ['docs.learning-journey.duo-drills'],
16
16
  });
package/src/track.test.ts CHANGED
@@ -3,104 +3,104 @@ import { describe, expect, it } from 'bun:test';
3
3
  import { drillsLanguageBasicsTrack } from './track';
4
4
 
5
5
  interface TestEvent {
6
- name: string;
7
- payload?: Record<string, unknown>;
8
- occurredAt?: Date;
6
+ name: string;
7
+ payload?: Record<string, unknown>;
8
+ occurredAt?: Date;
9
9
  }
10
10
 
11
11
  const matchesFilter = (
12
- filter: Record<string, unknown> | undefined,
13
- payload: Record<string, unknown> | undefined
12
+ filter: Record<string, unknown> | undefined,
13
+ payload: Record<string, unknown> | undefined
14
14
  ) => {
15
- if (!filter) return true;
16
- if (!payload) return false;
17
- return Object.entries(filter).every(([key, value]) => payload[key] === value);
15
+ if (!filter) return true;
16
+ if (!payload) return false;
17
+ return Object.entries(filter).every(([key, value]) => payload[key] === value);
18
18
  };
19
19
 
20
20
  interface StepState {
21
- id: string;
22
- status: 'PENDING' | 'COMPLETED';
23
- occurrences: number;
24
- masteryCount: number;
21
+ id: string;
22
+ status: 'PENDING' | 'COMPLETED';
23
+ occurrences: number;
24
+ masteryCount: number;
25
25
  }
26
26
 
27
27
  describe('duo drills track', () => {
28
- it('advances on session completion, accuracy counts, and SRS mastery', () => {
29
- const events: TestEvent[] = [
30
- {
31
- name: 'drill.session.completed',
32
- payload: { accuracyBucket: 'high' },
33
- },
34
- {
35
- name: 'drill.session.completed',
36
- payload: { accuracyBucket: 'high' },
37
- },
38
- {
39
- name: 'drill.session.completed',
40
- payload: { accuracyBucket: 'high' },
41
- },
42
- ...Array.from({ length: 5 }).map<TestEvent>(() => ({
43
- name: 'drill.card.mastered',
44
- payload: { skillId: 'language_basics', mastery: 0.9 },
45
- })),
46
- ];
28
+ it('advances on session completion, accuracy counts, and SRS mastery', () => {
29
+ const events: TestEvent[] = [
30
+ {
31
+ name: 'drill.session.completed',
32
+ payload: { accuracyBucket: 'high' },
33
+ },
34
+ {
35
+ name: 'drill.session.completed',
36
+ payload: { accuracyBucket: 'high' },
37
+ },
38
+ {
39
+ name: 'drill.session.completed',
40
+ payload: { accuracyBucket: 'high' },
41
+ },
42
+ ...Array.from({ length: 5 }).map<TestEvent>(() => ({
43
+ name: 'drill.card.mastered',
44
+ payload: { skillId: 'language_basics', mastery: 0.9 },
45
+ })),
46
+ ];
47
47
 
48
- const progress: StepState[] =
49
- drillsLanguageBasicsTrack.steps.map<StepState>((step) => ({
50
- id: step.id,
51
- status: 'PENDING',
52
- occurrences: 0,
53
- masteryCount: 0,
54
- }));
48
+ const progress: StepState[] =
49
+ drillsLanguageBasicsTrack.steps.map<StepState>((step) => ({
50
+ id: step.id,
51
+ status: 'PENDING',
52
+ occurrences: 0,
53
+ masteryCount: 0,
54
+ }));
55
55
 
56
- events.forEach((event) => {
57
- drillsLanguageBasicsTrack.steps.forEach((stepSpec, index) => {
58
- const step = progress[index];
59
- if (!step || step.status === 'COMPLETED') return;
60
- const completion = stepSpec.completion;
61
- if ((completion.kind ?? 'event') === 'event') {
62
- if (completion.eventName !== event.name) return;
63
- if (
64
- matchesFilter(
65
- completion.payloadFilter,
66
- event.payload as Record<string, unknown> | undefined
67
- )
68
- ) {
69
- step.status = 'COMPLETED';
70
- }
71
- return;
72
- }
73
- if (completion.kind === 'count') {
74
- if (
75
- completion.eventName === event.name &&
76
- matchesFilter(
77
- completion.payloadFilter,
78
- event.payload as Record<string, unknown> | undefined
79
- )
80
- ) {
81
- step.occurrences = step.occurrences + 1;
82
- if (step.occurrences >= completion.atLeast) {
83
- step.status = 'COMPLETED';
84
- }
85
- }
86
- return;
87
- }
88
- if (completion.kind === 'srs_mastery') {
89
- if (completion.eventName !== event.name) return;
90
- if (!matchesFilter(completion.payloadFilter, event.payload)) return;
91
- const masteryValue = (
92
- event.payload as Record<string, unknown> | undefined
93
- )?.[completion.masteryField ?? 'mastery'];
94
- if (typeof masteryValue !== 'number') return;
95
- if (masteryValue < completion.minimumMastery) return;
96
- step.masteryCount = step.masteryCount + 1;
97
- if (step.masteryCount >= (completion.requiredCount ?? 1)) {
98
- step.status = 'COMPLETED';
99
- }
100
- }
101
- });
102
- });
56
+ events.forEach((event) => {
57
+ drillsLanguageBasicsTrack.steps.forEach((stepSpec, index) => {
58
+ const step = progress[index];
59
+ if (!step || step.status === 'COMPLETED') return;
60
+ const completion = stepSpec.completion;
61
+ if ((completion.kind ?? 'event') === 'event') {
62
+ if (completion.eventName !== event.name) return;
63
+ if (
64
+ matchesFilter(
65
+ completion.payloadFilter,
66
+ event.payload as Record<string, unknown> | undefined
67
+ )
68
+ ) {
69
+ step.status = 'COMPLETED';
70
+ }
71
+ return;
72
+ }
73
+ if (completion.kind === 'count') {
74
+ if (
75
+ completion.eventName === event.name &&
76
+ matchesFilter(
77
+ completion.payloadFilter,
78
+ event.payload as Record<string, unknown> | undefined
79
+ )
80
+ ) {
81
+ step.occurrences = step.occurrences + 1;
82
+ if (step.occurrences >= completion.atLeast) {
83
+ step.status = 'COMPLETED';
84
+ }
85
+ }
86
+ return;
87
+ }
88
+ if (completion.kind === 'srs_mastery') {
89
+ if (completion.eventName !== event.name) return;
90
+ if (!matchesFilter(completion.payloadFilter, event.payload)) return;
91
+ const masteryValue = (
92
+ event.payload as Record<string, unknown> | undefined
93
+ )?.[completion.masteryField ?? 'mastery'];
94
+ if (typeof masteryValue !== 'number') return;
95
+ if (masteryValue < completion.minimumMastery) return;
96
+ step.masteryCount = step.masteryCount + 1;
97
+ if (step.masteryCount >= (completion.requiredCount ?? 1)) {
98
+ step.status = 'COMPLETED';
99
+ }
100
+ }
101
+ });
102
+ });
103
103
 
104
- expect(progress.every((s) => s.status === 'COMPLETED')).toBeTrue();
105
- });
104
+ expect(progress.every((s) => s.status === 'COMPLETED')).toBeTrue();
105
+ });
106
106
  });
package/src/track.ts CHANGED
@@ -1,62 +1,62 @@
1
1
  import type { LearningJourneyTrackSpec } from '@contractspec/module.learning-journey/track-spec';
2
2
 
3
3
  export const drillsLanguageBasicsTrack: LearningJourneyTrackSpec = {
4
- id: 'drills_language_basics',
5
- name: 'Language Basics Drills',
6
- description:
7
- 'Short SRS-driven drills to master a first skill, modeled after Duolingo-style sessions.',
8
- targetUserSegment: 'learner',
9
- targetRole: 'individual',
10
- totalXp: 50,
11
- completionRewards: { xpBonus: 25 },
12
- steps: [
13
- {
14
- id: 'complete_first_session',
15
- title: 'Complete first drill session',
16
- description: 'Finish a drill session to get started.',
17
- order: 1,
18
- completion: {
19
- kind: 'event',
20
- eventName: 'drill.session.completed',
21
- },
22
- xpReward: 20,
23
- metadata: { surface: 'drills' },
24
- },
25
- {
26
- id: 'reach_accuracy_threshold',
27
- title: 'Hit high accuracy in sessions',
28
- description: 'Achieve three high-accuracy sessions to build confidence.',
29
- order: 2,
30
- completion: {
31
- kind: 'count',
32
- eventName: 'drill.session.completed',
33
- atLeast: 3,
34
- payloadFilter: { accuracyBucket: 'high' },
35
- },
36
- xpReward: 30,
37
- metadata: { metric: 'accuracy', target: '>=85%' },
38
- },
39
- {
40
- id: 'unlock_new_skill',
41
- title: 'Master core cards in first skill',
42
- description:
43
- 'Reach mastery on at least five cards in the first skill to unlock the next one.',
44
- order: 3,
45
- completion: {
46
- kind: 'srs_mastery',
47
- eventName: 'drill.card.mastered',
48
- minimumMastery: 0.8,
49
- requiredCount: 5,
50
- skillIdField: 'skillId',
51
- masteryField: 'mastery',
52
- payloadFilter: { skillId: 'language_basics' },
53
- },
54
- xpReward: 40,
55
- metadata: { surface: 'srs', skill: 'language_basics' },
56
- },
57
- ],
4
+ id: 'drills_language_basics',
5
+ name: 'Language Basics Drills',
6
+ description:
7
+ 'Short SRS-driven drills to master a first skill, modeled after Duolingo-style sessions.',
8
+ targetUserSegment: 'learner',
9
+ targetRole: 'individual',
10
+ totalXp: 50,
11
+ completionRewards: { xpBonus: 25 },
12
+ steps: [
13
+ {
14
+ id: 'complete_first_session',
15
+ title: 'Complete first drill session',
16
+ description: 'Finish a drill session to get started.',
17
+ order: 1,
18
+ completion: {
19
+ kind: 'event',
20
+ eventName: 'drill.session.completed',
21
+ },
22
+ xpReward: 20,
23
+ metadata: { surface: 'drills' },
24
+ },
25
+ {
26
+ id: 'reach_accuracy_threshold',
27
+ title: 'Hit high accuracy in sessions',
28
+ description: 'Achieve three high-accuracy sessions to build confidence.',
29
+ order: 2,
30
+ completion: {
31
+ kind: 'count',
32
+ eventName: 'drill.session.completed',
33
+ atLeast: 3,
34
+ payloadFilter: { accuracyBucket: 'high' },
35
+ },
36
+ xpReward: 30,
37
+ metadata: { metric: 'accuracy', target: '>=85%' },
38
+ },
39
+ {
40
+ id: 'unlock_new_skill',
41
+ title: 'Master core cards in first skill',
42
+ description:
43
+ 'Reach mastery on at least five cards in the first skill to unlock the next one.',
44
+ order: 3,
45
+ completion: {
46
+ kind: 'srs_mastery',
47
+ eventName: 'drill.card.mastered',
48
+ minimumMastery: 0.8,
49
+ requiredCount: 5,
50
+ skillIdField: 'skillId',
51
+ masteryField: 'mastery',
52
+ payloadFilter: { skillId: 'language_basics' },
53
+ },
54
+ xpReward: 40,
55
+ metadata: { surface: 'srs', skill: 'language_basics' },
56
+ },
57
+ ],
58
58
  };
59
59
 
60
60
  export const drillTracks: LearningJourneyTrackSpec[] = [
61
- drillsLanguageBasicsTrack,
61
+ drillsLanguageBasicsTrack,
62
62
  ];
package/tsconfig.json CHANGED
@@ -1,18 +1,9 @@
1
1
  {
2
- "extends": "@contractspec/tool.typescript/react-library.json",
3
- "include": ["src"],
4
- "exclude": ["node_modules"],
5
- "compilerOptions": {
6
- "rootDir": "src",
7
- "outDir": "dist"
8
- }
2
+ "extends": "@contractspec/tool.typescript/react-library.json",
3
+ "include": ["src"],
4
+ "exclude": ["node_modules"],
5
+ "compilerOptions": {
6
+ "rootDir": "src",
7
+ "outDir": "dist"
8
+ }
9
9
  }
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
package/tsdown.config.js CHANGED
@@ -1,16 +1,10 @@
1
- import { defineConfig, moduleLibrary, withDevExports } from '@contractspec/tool.bun';
1
+ import {
2
+ defineConfig,
3
+ moduleLibrary,
4
+ withDevExports,
5
+ } from '@contractspec/tool.bun';
2
6
 
3
7
  export default defineConfig(() => ({
4
- ...moduleLibrary,
5
- ...withDevExports,
8
+ ...moduleLibrary,
9
+ ...withDevExports,
6
10
  }));
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-