@contractspec/example.learning-journey-duo-drills 3.7.6 → 3.7.7
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/.turbo/turbo-build.log +3 -3
- package/AGENTS.md +40 -18
- package/README.md +57 -29
- package/dist/browser/index.js +16 -16
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16 -16
- package/dist/node/index.js +16 -16
- package/package.json +5 -5
- package/src/docs/duo-drills.docblock.ts +11 -11
- package/src/example.ts +25 -25
- package/src/index.ts +2 -2
- package/src/learning-journey-duo-drills.feature.ts +11 -11
- package/src/track.test.ts +87 -87
- package/src/track.ts +55 -55
- package/tsconfig.json +7 -16
- package/tsdown.config.js +7 -13
package/.turbo/turbo-build.log
CHANGED
|
@@ -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
|
|
6
|
+
Bundled 6 modules in 25ms
|
|
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
|
|
16
|
+
Bundled 6 modules in 15ms
|
|
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
|
|
26
|
+
Bundled 6 modules in 38ms
|
|
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
|
|
1
|
+
# AI Agent Guide — `@contractspec/example.learning-journey-duo-drills`
|
|
2
2
|
|
|
3
3
|
Scope: `packages/examples/learning-journey-duo-drills/*`
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Drill-based learning journey example with SRS, XP, and streak hooks.
|
|
6
6
|
|
|
7
7
|
## Quick Context
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
-
##
|
|
14
|
+
## Architecture
|
|
13
15
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
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
|
|
23
|
+
## Public Surface
|
|
20
24
|
|
|
21
|
-
- `.`
|
|
22
|
-
- `./docs`
|
|
23
|
-
- `./
|
|
24
|
-
- `./
|
|
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
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
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/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
|
-
|
|
7
|
+
## What This Demonstrates
|
|
7
8
|
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
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`.
|
package/dist/browser/index.js
CHANGED
|
@@ -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
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,
|
package/dist/node/index.js
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "3.7.7",
|
|
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,15 +57,15 @@
|
|
|
57
57
|
"dev": "contractspec-bun-build dev",
|
|
58
58
|
"clean": "rimraf dist .turbo",
|
|
59
59
|
"lint": "bun lint:fix",
|
|
60
|
-
"lint:fix": "
|
|
61
|
-
"lint:check": "
|
|
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.
|
|
68
|
-
"@contractspec/lib.contracts-spec": "
|
|
67
|
+
"@contractspec/module.learning-journey": "3.7.7",
|
|
68
|
+
"@contractspec/lib.contracts-spec": "4.0.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@contractspec/tool.typescript": "3.7.6",
|
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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,16 +1,16 @@
|
|
|
1
1
|
import { defineFeature } from '@contractspec/lib.contracts-spec';
|
|
2
2
|
|
|
3
3
|
export const LearningJourneyDuoDrillsFeature = defineFeature({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
name: string;
|
|
7
|
+
payload?: Record<string, unknown>;
|
|
8
|
+
occurredAt?: Date;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const matchesFilter = (
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
filter: Record<string, unknown> | undefined,
|
|
13
|
+
payload: Record<string, unknown> | undefined
|
|
14
14
|
) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
id: string;
|
|
22
|
+
status: 'PENDING' | 'COMPLETED';
|
|
23
|
+
occurrences: number;
|
|
24
|
+
masteryCount: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
describe('duo drills track', () => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
61
|
+
drillsLanguageBasicsTrack,
|
|
62
62
|
];
|
package/tsconfig.json
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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 {
|
|
1
|
+
import {
|
|
2
|
+
defineConfig,
|
|
3
|
+
moduleLibrary,
|
|
4
|
+
withDevExports,
|
|
5
|
+
} from '@contractspec/tool.bun';
|
|
2
6
|
|
|
3
7
|
export default defineConfig(() => ({
|
|
4
|
-
|
|
5
|
-
|
|
8
|
+
...moduleLibrary,
|
|
9
|
+
...withDevExports,
|
|
6
10
|
}));
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|