@contractspec/example.learning-journey-ambient-coach 0.0.0-canary-20260113162409
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$colon$bundle.log +25 -0
- package/.turbo/turbo-build.log +26 -0
- package/CHANGELOG.md +322 -0
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/docs/ambient-coach.docblock.d.ts +1 -0
- package/dist/docs/ambient-coach.docblock.js +34 -0
- package/dist/docs/ambient-coach.docblock.js.map +1 -0
- package/dist/docs/index.d.ts +1 -0
- package/dist/docs/index.js +1 -0
- package/dist/example.d.ts +7 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +42 -0
- package/dist/example.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/track.d.ts +9 -0
- package/dist/track.d.ts.map +1 -0
- package/dist/track.js +42 -0
- package/dist/track.js.map +1 -0
- package/example.ts +1 -0
- package/package.json +57 -0
- package/src/docs/ambient-coach.docblock.ts +32 -0
- package/src/docs/index.ts +1 -0
- package/src/example.ts +31 -0
- package/src/index.ts +3 -0
- package/src/track.test.ts +57 -0
- package/src/track.ts +74 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.js +17 -0
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contractspec/example.learning-journey-ambient-coach",
|
|
3
|
+
"version": "0.0.0-canary-20260113162409",
|
|
4
|
+
"description": "Ambient coach learning journey example with contextual tips and follow-up actions.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./docs": "./dist/docs/index.js",
|
|
10
|
+
"./docs/ambient-coach.docblock": "./dist/docs/ambient-coach.docblock.js",
|
|
11
|
+
"./example": "./dist/example.js",
|
|
12
|
+
"./track": "./dist/track.js",
|
|
13
|
+
"./*": "./*"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
17
|
+
"publish:pkg:canary": "bun publish:pkg --tag canary",
|
|
18
|
+
"build": "bun build:types && bun build:bundle",
|
|
19
|
+
"build:bundle": "tsdown",
|
|
20
|
+
"build:types": "tsc --noEmit",
|
|
21
|
+
"dev": "bun build:bundle --watch",
|
|
22
|
+
"clean": "rimraf dist .turbo",
|
|
23
|
+
"lint": "bun lint:fix",
|
|
24
|
+
"lint:fix": "eslint src --fix",
|
|
25
|
+
"lint:check": "eslint src",
|
|
26
|
+
"test": "bun test"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@contractspec/module.learning-journey": "0.0.0-canary-20260113162409",
|
|
30
|
+
"@contractspec/lib.contracts": "0.0.0-canary-20260113162409"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@contractspec/tool.tsdown": "0.0.0-canary-20260113162409",
|
|
34
|
+
"@contractspec/tool.typescript": "0.0.0-canary-20260113162409",
|
|
35
|
+
"tsdown": "^0.19.0",
|
|
36
|
+
"typescript": "^5.9.3"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"exports": {
|
|
40
|
+
".": "./dist/index.js",
|
|
41
|
+
"./example": "./dist/example.js",
|
|
42
|
+
"./track": "./dist/track.js",
|
|
43
|
+
"./docs": "./dist/docs/index.js",
|
|
44
|
+
"./docs/ambient-coach.docblock": "./dist/docs/ambient-coach.docblock.js",
|
|
45
|
+
"./*": "./*"
|
|
46
|
+
},
|
|
47
|
+
"registry": "https://registry.npmjs.org/",
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/lssm-tech/contractspec.git",
|
|
54
|
+
"directory": "packages/examples/learning-journey-ambient-coach"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://contractspec.io"
|
|
57
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { DocBlock } from '@contractspec/lib.contracts/docs';
|
|
2
|
+
import { registerDocBlocks } from '@contractspec/lib.contracts/docs';
|
|
3
|
+
|
|
4
|
+
const ambientCoachDocBlocks: DocBlock[] = [
|
|
5
|
+
{
|
|
6
|
+
id: 'docs.learning-journey.ambient-coach',
|
|
7
|
+
title: 'Learning Journey — Ambient Coach',
|
|
8
|
+
summary:
|
|
9
|
+
'Context-aware coaching pattern that triggers tips, shows them, and marks completion when users act or acknowledge.',
|
|
10
|
+
kind: 'reference',
|
|
11
|
+
visibility: 'public',
|
|
12
|
+
route: '/docs/learning-journey/ambient-coach',
|
|
13
|
+
tags: ['learning', 'coach', 'tips'],
|
|
14
|
+
body: `## Tracks
|
|
15
|
+
- \`money_ambient_coach\`: cash buffer too high, no savings goal, irregular savings
|
|
16
|
+
- \`coliving_ambient_coach\`: noise late evening, guest frequency high, shared space conflicts
|
|
17
|
+
|
|
18
|
+
## Steps & Events
|
|
19
|
+
- Trigger tip: \`coach.tip.triggered\` (payload includes \`tipId\`)
|
|
20
|
+
- Show tip (optional UI emission): \`coach.tip.shown\`
|
|
21
|
+
- Complete when acknowledged or follow-up action taken:
|
|
22
|
+
- \`coach.tip.acknowledged\`
|
|
23
|
+
- \`coach.tip.follow_up_action_taken\`
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
- Tracks export from \`@contractspec/example.learning-journey-ambient-coach/track\`.
|
|
27
|
+
- Registry progression is event-driven; payload filters can scope tips per category.
|
|
28
|
+
- XP/engagement can be awarded on completion of each tip step.`,
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
registerDocBlocks(ambientCoachDocBlocks);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './ambient-coach.docblock';
|
package/src/example.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineExample } from '@contractspec/lib.contracts';
|
|
2
|
+
|
|
3
|
+
const example = defineExample({
|
|
4
|
+
meta: {
|
|
5
|
+
key: 'learning-journey-ambient-coach',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
title: 'Learning Journey — Ambient Coach',
|
|
8
|
+
description:
|
|
9
|
+
'Ambient coaching pattern: lightweight nudges driven by context and recent progress.',
|
|
10
|
+
kind: 'template',
|
|
11
|
+
visibility: 'public',
|
|
12
|
+
stability: 'experimental',
|
|
13
|
+
owners: ['@platform.core'],
|
|
14
|
+
tags: ['learning', 'coaching', 'ambient'],
|
|
15
|
+
},
|
|
16
|
+
docs: {
|
|
17
|
+
rootDocId: 'docs.learning-journey.ambient-coach',
|
|
18
|
+
},
|
|
19
|
+
entrypoints: {
|
|
20
|
+
packageName: '@contractspec/example.learning-journey-ambient-coach',
|
|
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
|
+
});
|
|
30
|
+
|
|
31
|
+
export default example;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import { moneyAmbientCoachTrack } from './track';
|
|
4
|
+
|
|
5
|
+
interface TestEvent {
|
|
6
|
+
name: string;
|
|
7
|
+
payload?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const matchesFilter = (
|
|
11
|
+
filter: Record<string, unknown> | undefined,
|
|
12
|
+
payload: Record<string, unknown> | undefined
|
|
13
|
+
) => {
|
|
14
|
+
if (!filter) return true;
|
|
15
|
+
if (!payload) return false;
|
|
16
|
+
return Object.entries(filter).every(([key, value]) => payload[key] === value);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe('ambient coach track', () => {
|
|
20
|
+
it('completes steps when tips are acted upon', () => {
|
|
21
|
+
const tipIds = [
|
|
22
|
+
'cash_buffer_too_high',
|
|
23
|
+
'no_savings_goal',
|
|
24
|
+
'irregular_savings',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const events: TestEvent[] = tipIds.flatMap((tipId) => [
|
|
28
|
+
{ name: 'coach.tip.triggered', payload: { tipId } },
|
|
29
|
+
{ name: 'coach.tip.follow_up_action_taken', payload: { tipId } },
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const progress = moneyAmbientCoachTrack.steps.map((step) => ({
|
|
33
|
+
id: step.id,
|
|
34
|
+
status: 'PENDING' as 'PENDING' | 'COMPLETED',
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
events.forEach((event) => {
|
|
38
|
+
moneyAmbientCoachTrack.steps.forEach((stepSpec, index) => {
|
|
39
|
+
const step = progress[index];
|
|
40
|
+
if (!step || step.status === 'COMPLETED') return;
|
|
41
|
+
if (stepSpec.completion.kind !== 'event') return;
|
|
42
|
+
if (stepSpec.completion.eventName !== event.name) return;
|
|
43
|
+
if (
|
|
44
|
+
!matchesFilter(
|
|
45
|
+
stepSpec.completion.payloadFilter,
|
|
46
|
+
event.payload as Record<string, unknown> | undefined
|
|
47
|
+
)
|
|
48
|
+
) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
step.status = 'COMPLETED';
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(progress.every((s) => s.status === 'COMPLETED')).toBeTrue();
|
|
56
|
+
});
|
|
57
|
+
});
|
package/src/track.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { LearningJourneyTrackSpec } from '@contractspec/module.learning-journey/track-spec';
|
|
2
|
+
|
|
3
|
+
const makeTipStep = (
|
|
4
|
+
id: string,
|
|
5
|
+
tipId: string,
|
|
6
|
+
description: string
|
|
7
|
+
): LearningJourneyTrackSpec['steps'][number] => ({
|
|
8
|
+
id,
|
|
9
|
+
title: `Resolve tip: ${tipId}`,
|
|
10
|
+
description,
|
|
11
|
+
completion: {
|
|
12
|
+
kind: 'event',
|
|
13
|
+
eventName: 'coach.tip.follow_up_action_taken',
|
|
14
|
+
payloadFilter: { tipId },
|
|
15
|
+
},
|
|
16
|
+
xpReward: 20,
|
|
17
|
+
metadata: { tipId },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const moneyAmbientCoachTrack: LearningJourneyTrackSpec = {
|
|
21
|
+
id: 'money_ambient_coach',
|
|
22
|
+
name: 'Ambient Coach — Money',
|
|
23
|
+
description:
|
|
24
|
+
'Subtle coaching for money patterns (cash buffer, goals, saving rhythm).',
|
|
25
|
+
targetUserSegment: 'money_user',
|
|
26
|
+
totalXp: 60,
|
|
27
|
+
steps: [
|
|
28
|
+
makeTipStep(
|
|
29
|
+
'cash_buffer_too_high',
|
|
30
|
+
'cash_buffer_too_high',
|
|
31
|
+
'Suggest sweeping excess cash into goals.'
|
|
32
|
+
),
|
|
33
|
+
makeTipStep(
|
|
34
|
+
'no_savings_goal',
|
|
35
|
+
'no_savings_goal',
|
|
36
|
+
'Prompt setting a first savings goal.'
|
|
37
|
+
),
|
|
38
|
+
makeTipStep(
|
|
39
|
+
'irregular_savings',
|
|
40
|
+
'irregular_savings',
|
|
41
|
+
'Recommend recurring saves after irregular deposits.'
|
|
42
|
+
),
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const colivingAmbientCoachTrack: LearningJourneyTrackSpec = {
|
|
47
|
+
id: 'coliving_ambient_coach',
|
|
48
|
+
name: 'Ambient Coach — Coliving',
|
|
49
|
+
description: 'Contextual tips for healthy coliving habits.',
|
|
50
|
+
targetUserSegment: 'coliving',
|
|
51
|
+
totalXp: 60,
|
|
52
|
+
steps: [
|
|
53
|
+
makeTipStep(
|
|
54
|
+
'noise_late_evening',
|
|
55
|
+
'noise_late_evening',
|
|
56
|
+
'Suggest updating quiet hours to reduce evening noise.'
|
|
57
|
+
),
|
|
58
|
+
makeTipStep(
|
|
59
|
+
'guest_frequency_high',
|
|
60
|
+
'guest_frequency_high',
|
|
61
|
+
'Set guest frequency expectations for the house.'
|
|
62
|
+
),
|
|
63
|
+
makeTipStep(
|
|
64
|
+
'shared_space_conflicts',
|
|
65
|
+
'shared_space_conflicts',
|
|
66
|
+
'Offer a shared-space checklist to reduce conflicts.'
|
|
67
|
+
),
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const ambientCoachTracks: LearningJourneyTrackSpec[] = [
|
|
72
|
+
moneyAmbientCoachTrack,
|
|
73
|
+
colivingAmbientCoachTrack,
|
|
74
|
+
];
|