@lssm/example.learning-journey-studio-onboarding 0.0.0-canary-20251213172311
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 +19 -0
- package/CHANGELOG.md +10 -0
- package/README.md +38 -0
- package/dist/contracts/index.js +1 -0
- package/dist/docs/index.js +1 -0
- package/dist/docs/studio-onboarding.docblock.js +1 -0
- package/dist/example.js +1 -0
- package/dist/handlers/demo.handlers.js +1 -0
- package/dist/index.js +1 -0
- package/dist/presentations/index.js +1 -0
- package/dist/track.js +1 -0
- package/example.ts +1 -0
- package/package.json +54 -0
- package/src/contracts/index.test.ts +49 -0
- package/src/contracts/index.ts +122 -0
- package/src/docs/index.ts +1 -0
- package/src/docs/studio-onboarding.docblock.ts +38 -0
- package/src/example.ts +26 -0
- package/src/handlers/demo.handlers.ts +48 -0
- package/src/index.ts +6 -0
- package/src/presentations/index.ts +47 -0
- package/src/track.ts +87 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsdown.config.js +7 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
$ bun build:bundle && bun build:types
|
|
2
|
+
$ tsdown
|
|
3
|
+
[34mℹ[39m tsdown [2mv0.17.0[22m powered by rolldown [2mv1.0.0-beta.53[22m
|
|
4
|
+
[34mℹ[39m config file: [4m/home/runner/work/contractspec/contractspec/packages/examples/learning-journey-studio-onboarding/tsdown.config.js[24m
|
|
5
|
+
[34mℹ[39m entry: [34msrc/example.ts, src/index.ts, src/track.ts, src/contracts/index.ts, src/docs/index.ts, src/docs/studio-onboarding.docblock.ts, src/handlers/demo.handlers.ts, src/presentations/index.ts[39m
|
|
6
|
+
[34mℹ[39m target: [34mesnext[39m
|
|
7
|
+
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
8
|
+
[34mℹ[39m Build start
|
|
9
|
+
[34mℹ[39m [2mdist/[22m[1mcontracts/index.js[22m [2m2.88 kB[22m [2m│ gzip: 0.91 kB[22m
|
|
10
|
+
[34mℹ[39m [2mdist/[22m[1mtrack.js[22m [2m1.93 kB[22m [2m│ gzip: 0.80 kB[22m
|
|
11
|
+
[34mℹ[39m [2mdist/[22m[1mdocs/studio-onboarding.docblock.js[22m [2m1.47 kB[22m [2m│ gzip: 0.76 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22m[1mindex.js[22m [2m0.99 kB[22m [2m│ gzip: 0.32 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22m[1mpresentations/index.js[22m [2m0.80 kB[22m [2m│ gzip: 0.37 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22m[1mexample.js[22m [2m0.60 kB[22m [2m│ gzip: 0.37 kB[22m
|
|
15
|
+
[34mℹ[39m [2mdist/[22m[1mhandlers/demo.handlers.js[22m [2m0.44 kB[22m [2m│ gzip: 0.28 kB[22m
|
|
16
|
+
[34mℹ[39m [2mdist/[22m[1mdocs/index.js[22m [2m0.04 kB[22m [2m│ gzip: 0.06 kB[22m
|
|
17
|
+
[34mℹ[39m 8 files, total: 9.16 kB
|
|
18
|
+
[32m✔[39m Build complete in [32m80ms[39m
|
|
19
|
+
$ tsc --noEmit
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# @lssm/example.learning-journey-studio-onboarding
|
|
2
|
+
|
|
3
|
+
## 0.0.0-canary-20251213172311
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [3086383]
|
|
8
|
+
- @lssm/lib.contracts@0.0.0-canary-20251213172311
|
|
9
|
+
- @lssm/lib.schema@0.0.0-canary-20251213172311
|
|
10
|
+
- @lssm/module.learning-journey@0.0.0-canary-20251213172311
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @lssm/example.learning-journey-studio-onboarding
|
|
2
|
+
|
|
3
|
+
Learning Journey example focused on the first 30 minutes inside ContractSpec Studio.
|
|
4
|
+
|
|
5
|
+
## Track
|
|
6
|
+
|
|
7
|
+
- **Key**: `studio_getting_started`
|
|
8
|
+
- **Persona**: new Studio developer
|
|
9
|
+
- **XP**: 20/20/20/20/30 + 25 bonus within 48h
|
|
10
|
+
- **Badge**: `studio_first_30m`
|
|
11
|
+
|
|
12
|
+
## Steps & Events
|
|
13
|
+
|
|
14
|
+
1. `choose_template` → `studio.template.instantiated`
|
|
15
|
+
2. `edit_spec` → `spec.changed` (scope: sandbox)
|
|
16
|
+
3. `regenerate_app` → `regeneration.completed`
|
|
17
|
+
4. `play_in_playground` → `playground.session.started`
|
|
18
|
+
5. `try_evolution_mode` → `studio.evolution.applied`
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
- Import track specs:
|
|
23
|
+
`import { studioLearningTracks } from '@lssm/example.learning-journey-studio-onboarding/track'`
|
|
24
|
+
- Contracts/handlers for demos:
|
|
25
|
+
`import { GetStudioOnboardingTrack, RecordStudioOnboardingEvent } from '@lssm/example.learning-journey-studio-onboarding/contracts'`
|
|
26
|
+
`import { emitStudioOnboardingEvent } from '@lssm/example.learning-journey-studio-onboarding/handlers/demo.handlers'`
|
|
27
|
+
- Presentations (react/markdown/json targets):
|
|
28
|
+
`import { studioOnboardingPresentations } from '@lssm/example.learning-journey-studio-onboarding/presentations'`
|
|
29
|
+
- Register with Learning Journey surfaces via onboarding API:
|
|
30
|
+
- `learning.onboarding.listTracks` to surface catalog
|
|
31
|
+
- `learning.onboarding.getProgress` to show status
|
|
32
|
+
- `learning.onboarding.recordEvent` in event-bus handlers to advance steps
|
|
33
|
+
- Wire events from Studio surfaces (template spawn, spec save, regeneration, playground session start, evolution apply) to `recordEvent`.
|
|
34
|
+
|
|
35
|
+
## Docs
|
|
36
|
+
|
|
37
|
+
- Docblock: `src/docs/studio-onboarding.docblock.ts`
|
|
38
|
+
- Route suggestion: `/docs/learning-journey/studio-onboarding`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{studioGettingStartedTrack as e}from"../track.js";import{ScalarTypeEnum as t,defineSchemaModel as n}from"@lssm/lib.schema";import{defineCommand as r,defineQuery as i}from"@lssm/lib.contracts";const a=[`examples.learning-journey.studio-onboarding`],o=n({name:`StudioOnboardingStep`,description:`Step metadata for Studio onboarding journey`,fields:{id:{type:t.String_unsecure(),isOptional:!1},title:{type:t.String_unsecure(),isOptional:!1},description:{type:t.String_unsecure(),isOptional:!0},completionEvent:{type:t.String_unsecure(),isOptional:!1},sourceModule:{type:t.String_unsecure(),isOptional:!0},xpReward:{type:t.Int_unsecure(),isOptional:!0},order:{type:t.Int_unsecure(),isOptional:!0}}}),s=n({name:`StudioOnboardingTrack`,description:`Studio onboarding track definition`,fields:{id:{type:t.String_unsecure(),isOptional:!1},name:{type:t.String_unsecure(),isOptional:!1},description:{type:t.String_unsecure(),isOptional:!0},totalXp:{type:t.Int_unsecure(),isOptional:!0},completionXpBonus:{type:t.Int_unsecure(),isOptional:!0},completionBadgeKey:{type:t.String_unsecure(),isOptional:!0},streakHoursWindow:{type:t.Int_unsecure(),isOptional:!0},streakBonusXp:{type:t.Int_unsecure(),isOptional:!0},steps:{type:o,isArray:!0,isOptional:!1}}}),c=n({name:`StudioOnboardingTrackResponse`,description:`Response wrapper for studio onboarding track`,fields:{track:{type:s,isOptional:!1}}}),l=n({name:`StudioOnboardingRecordEventInput`,description:`Emit a demo event to advance Studio onboarding steps`,fields:{learnerId:{type:t.String_unsecure(),isOptional:!1},eventName:{type:t.String_unsecure(),isOptional:!1},payload:{type:t.JSON(),isOptional:!0},occurredAt:{type:t.DateTime(),isOptional:!0}}}),u=n({name:`StudioOnboardingSuccess`,description:`Generic success response`,fields:{success:{type:t.Boolean(),isOptional:!1}}}),d=i({meta:{name:`learningJourney.studioOnboarding.getTrack`,version:1,stability:`experimental`,owners:[...a],tags:[`learning`,`onboarding`,`studio`],description:`Fetch the Studio onboarding track definition.`,goal:`Expose track metadata to UIs and templates.`,context:`Called by Studio/Playground to render journey steps.`},io:{input:n({name:`StudioOnboardingTrackInput`,description:`Track input`,fields:{}}),output:c},policy:{auth:`user`}}),f=r({meta:{name:`learningJourney.studioOnboarding.recordEvent`,version:1,stability:`experimental`,owners:[...a],tags:[`learning`,`onboarding`,`studio`],description:`Record an event to advance Studio onboarding progress.`,goal:`Advance steps via domain events in demo/sandbox contexts.`,context:`Called by handlers or demo scripts to emit step completion events.`},io:{input:l,output:u},policy:{auth:`user`}}),p={GetStudioOnboardingTrack:d,RecordStudioOnboardingEvent:f,track:e};export{d as GetStudioOnboardingTrack,f as RecordStudioOnboardingEvent,s as StudioOnboardingTrackModel,p as studioOnboardingContracts};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./studio-onboarding.docblock.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{registerDocBlocks as e}from"@lssm/lib.contracts/docs";e([{id:`docs.learning-journey.studio-onboarding`,title:`Learning Journey — Studio Getting Started`,summary:`Track that guides a new Studio user through template spawn, spec edit, regeneration, playground, and evolution.`,kind:`reference`,visibility:`public`,route:`/docs/learning-journey/studio-onboarding`,tags:[`learning`,`onboarding`,`studio`],body:"## Track\n- **Key**: `studio_getting_started`\n- **Persona**: new Studio developer, first 30 minutes\n- **Goal**: instantiate template → edit spec → regenerate → play → run evolution\n\n## Steps & Events\n1) `choose_template` → event `studio.template.instantiated`\n2) `edit_spec` → event `spec.changed` (scope: sandbox)\n3) `regenerate_app` → event `regeneration.completed`\n4) `play_in_playground` → event `playground.session.started`\n5) `try_evolution_mode` → event `studio.evolution.applied`\n\nXP: 20/20/20/20/30 with bonus 25 XP if completed within 48h (streak rule). Badge: `studio_first_30m`.\n\n## Wiring\n- Tracks export from `@lssm/example.learning-journey-studio-onboarding/track`.\n- Use onboarding API:\n - `learning.onboarding.listTracks` to surface catalog\n - `learning.onboarding.getProgress` to render progress\n - `learning.onboarding.recordEvent` to advance from bus events\n- Events should be emitted by Studio surfaces (template creation, spec save, regeneration, playground session start, evolution apply)."}]);
|
package/dist/example.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={id:`learning-journey-studio-onboarding`,title:`Learning Journey — Studio Getting Started`,summary:`Onboarding track guiding a new Studio user through template spawn, spec edit, regeneration, playground, and evolution.`,tags:[`learning`,`onboarding`,`studio`],kind:`template`,visibility:`public`,docs:{rootDocId:`docs.learning-journey.studio-onboarding`},entrypoints:{packageName:`@lssm/example.learning-journey-studio-onboarding`,docs:`./docs`},surfaces:{templates:!0,sandbox:{enabled:!0,modes:[`playground`,`markdown`]},studio:{enabled:!0,installable:!0},mcp:{enabled:!0}}};export{e as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{studioGettingStartedTrack as e}from"../track.js";const t=[`studio.template.instantiated`,`spec.changed`,`regeneration.completed`,`playground.session.started`,`studio.evolution.applied`],n=(t,{learnerId:n,occurredAt:r=new Date},i)=>{let a={learnerId:n,name:t,occurredAt:r,trackId:e.id};return i?i(a):a},r=(e,r)=>t.map(t=>n(t,e,r));export{r as emitAllStudioOnboardingEvents,n as emitStudioOnboardingEvent,t as studioOnboardingEvents};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"./example.js";import{studioGettingStartedTrack as t,studioLearningTracks as n}from"./track.js";import"./docs/index.js";import{GetStudioOnboardingTrack as r,RecordStudioOnboardingEvent as i,StudioOnboardingTrackModel as a,studioOnboardingContracts as o}from"./contracts/index.js";import{emitAllStudioOnboardingEvents as s,emitStudioOnboardingEvent as c,studioOnboardingEvents as l}from"./handlers/demo.handlers.js";import{StudioOnboardingTrackPresentation as u,StudioOnboardingWidgetPresentation as d,studioOnboardingPresentations as f}from"./presentations/index.js";export{r as GetStudioOnboardingTrack,i as RecordStudioOnboardingEvent,a as StudioOnboardingTrackModel,u as StudioOnboardingTrackPresentation,d as StudioOnboardingWidgetPresentation,s as emitAllStudioOnboardingEvents,c as emitStudioOnboardingEvent,e as example,t as studioGettingStartedTrack,n as studioLearningTracks,o as studioOnboardingContracts,l as studioOnboardingEvents,f as studioOnboardingPresentations};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{StudioOnboardingTrackModel as e}from"../contracts/index.js";const t={domain:`learning-journey`,owners:[`examples.learning-journey.studio-onboarding`],tags:[`learning`,`onboarding`,`studio`]},n={meta:{name:`learning.journey.studio.track`,version:1,description:`Studio onboarding track detail`,...t},source:{type:`component`,framework:`react`,componentKey:`LearningTrackDetail`,props:e},targets:[`react`,`markdown`,`application/json`]},r={meta:{name:`learning.journey.studio.widget`,version:1,description:`Compact widget for Studio onboarding progress`,...t},source:{type:`component`,framework:`react`,componentKey:`LearningTrackProgressWidget`},targets:[`react`]},i=[n,r];export{n as StudioOnboardingTrackPresentation,r as StudioOnboardingWidgetPresentation,i as studioOnboardingPresentations};
|
package/dist/track.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={id:`studio_getting_started`,productId:`contractspec-studio`,name:`Studio Getting Started`,description:`First 30 minutes in Studio: instantiate a template, edit the spec, regenerate, and try runtime surfaces.`,targetUserSegment:`new_studio_user`,targetRole:`developer`,totalXp:110,streakRule:{hoursWindow:48,bonusXp:25},completionRewards:{xpBonus:25,badgeKey:`studio_first_30m`},steps:[{id:`choose_template`,title:`Choose a template`,description:`Pick a Phase 1 template and create a sandbox.`,order:1,completion:{eventName:`studio.template.instantiated`,sourceModule:`@lssm/bundle.contractspec-studio`},xpReward:20,metadata:{surface:`templates`}},{id:`edit_spec`,title:`Edit the spec`,description:`Change the spec (not generated code) and save.`,instructions:`Open spec editor, tweak a contract or presentation, save.`,order:2,completion:{eventName:`spec.changed`,sourceModule:`@lssm/lib.contracts`,payloadFilter:{scope:`sandbox`}},xpReward:20,metadata:{surface:`spec-editor`}},{id:`regenerate_app`,title:`Regenerate the app`,description:`Regenerate artifacts from the updated spec.`,order:3,completion:{eventName:`regeneration.completed`,sourceModule:`@lssm/lib.contracts/regenerator`},xpReward:20,metadata:{surface:`regenerator`}},{id:`play_in_playground`,title:`Use the playground`,description:`Start a playground or runtime session against your sandbox.`,order:4,completion:{eventName:`playground.session.started`,sourceModule:`@lssm/bundle.contractspec-studio`},xpReward:20,metadata:{surface:`playground`}},{id:`try_evolution_mode`,title:`Try evolution mode`,description:`Request a change via Evolution, then regenerate.`,order:5,completion:{eventName:`studio.evolution.applied`,sourceModule:`@lssm/lib.evolution`},xpReward:30,metadata:{surface:`evolution`}}],metadata:{persona:`first_run`,surfacedIn:[`studio/home`,`studio/sidebar/learning`]}},t=[e];export{e as studioGettingStartedTrack,t as studioLearningTracks};
|
package/example.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './src/example';
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lssm/example.learning-journey-studio-onboarding",
|
|
3
|
+
"version": "0.0.0-canary-20251213172311",
|
|
4
|
+
"description": "Learning journey track for first 30 minutes in ContractSpec Studio.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./src/index.ts",
|
|
10
|
+
"./contracts": "./src/contracts/index.ts",
|
|
11
|
+
"./docs": "./src/docs/index.ts",
|
|
12
|
+
"./docs/studio-onboarding.docblock": "./src/docs/studio-onboarding.docblock.ts",
|
|
13
|
+
"./example": "./src/example.ts",
|
|
14
|
+
"./handlers/demo.handlers": "./src/handlers/demo.handlers.ts",
|
|
15
|
+
"./presentations": "./src/presentations/index.ts",
|
|
16
|
+
"./track": "./src/track.ts",
|
|
17
|
+
"./*": "./*"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "bun build:bundle && bun build:types",
|
|
21
|
+
"build:bundle": "tsdown",
|
|
22
|
+
"build:types": "tsc --noEmit",
|
|
23
|
+
"dev": "bun build:bundle --watch",
|
|
24
|
+
"clean": "rimraf dist .turbo",
|
|
25
|
+
"lint": "bun lint:fix",
|
|
26
|
+
"lint:fix": "eslint src --fix",
|
|
27
|
+
"lint:check": "eslint src"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@lssm/lib.contracts": "workspace:*",
|
|
31
|
+
"@lssm/lib.schema": "workspace:*",
|
|
32
|
+
"@lssm/module.learning-journey": "workspace:*"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@lssm/tool.tsdown": "workspace:*",
|
|
36
|
+
"@lssm/tool.typescript": "workspace:*",
|
|
37
|
+
"tsdown": "^0.17.0",
|
|
38
|
+
"typescript": "^5.9.3"
|
|
39
|
+
},
|
|
40
|
+
"module": "./dist/index.js",
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"exports": {
|
|
43
|
+
".": "./dist/index.js",
|
|
44
|
+
"./contracts": "./dist/contracts/index.js",
|
|
45
|
+
"./docs": "./dist/docs/index.js",
|
|
46
|
+
"./docs/studio-onboarding.docblock": "./dist/docs/studio-onboarding.docblock.js",
|
|
47
|
+
"./example": "./dist/example.js",
|
|
48
|
+
"./handlers/demo.handlers": "./dist/handlers/demo.handlers.js",
|
|
49
|
+
"./presentations": "./dist/presentations/index.js",
|
|
50
|
+
"./track": "./dist/track.js",
|
|
51
|
+
"./*": "./*"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
GetStudioOnboardingTrack,
|
|
5
|
+
RecordStudioOnboardingEvent,
|
|
6
|
+
studioOnboardingContracts,
|
|
7
|
+
} from './index';
|
|
8
|
+
import { studioGettingStartedTrack } from '../track';
|
|
9
|
+
import {
|
|
10
|
+
emitStudioOnboardingEvent,
|
|
11
|
+
studioOnboardingEvents,
|
|
12
|
+
} from '../handlers/demo.handlers';
|
|
13
|
+
import type { StudioEvent } from '../handlers/demo.handlers';
|
|
14
|
+
|
|
15
|
+
describe('studio onboarding contracts', () => {
|
|
16
|
+
it('exposes track metadata', () => {
|
|
17
|
+
expect(studioOnboardingContracts.track.id).toBe('studio_getting_started');
|
|
18
|
+
expect(studioOnboardingContracts.track.steps.length).toBeGreaterThan(0);
|
|
19
|
+
expect(GetStudioOnboardingTrack.meta.name).toBe(
|
|
20
|
+
'learningJourney.studioOnboarding.getTrack'
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('allows recording events via demo handler', async () => {
|
|
25
|
+
const step = studioGettingStartedTrack.steps[0];
|
|
26
|
+
expect(step).toBeDefined();
|
|
27
|
+
if (!step) throw new Error('Expected at least one onboarding step');
|
|
28
|
+
|
|
29
|
+
const eventName = step.completion.eventName;
|
|
30
|
+
const isStudioEvent = (value: string): value is StudioEvent =>
|
|
31
|
+
studioOnboardingEvents.includes(value as StudioEvent);
|
|
32
|
+
|
|
33
|
+
expect(isStudioEvent(eventName)).toBe(true);
|
|
34
|
+
if (!isStudioEvent(eventName)) {
|
|
35
|
+
throw new Error(`Unexpected event name: ${eventName}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const result = await emitStudioOnboardingEvent(eventName, {
|
|
39
|
+
learnerId: 'demo-learner',
|
|
40
|
+
});
|
|
41
|
+
expect(result).toBeDefined();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('exposes record event contract', () => {
|
|
45
|
+
expect(RecordStudioOnboardingEvent.meta.name).toBe(
|
|
46
|
+
'learningJourney.studioOnboarding.recordEvent'
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { ScalarTypeEnum, defineSchemaModel } from '@lssm/lib.schema';
|
|
2
|
+
import { defineCommand, defineQuery } from '@lssm/lib.contracts';
|
|
3
|
+
|
|
4
|
+
import { studioGettingStartedTrack } from '../track';
|
|
5
|
+
|
|
6
|
+
const OWNERS = ['examples.learning-journey.studio-onboarding'] as const;
|
|
7
|
+
|
|
8
|
+
const StepModel = defineSchemaModel({
|
|
9
|
+
name: 'StudioOnboardingStep',
|
|
10
|
+
description: 'Step metadata for Studio onboarding journey',
|
|
11
|
+
fields: {
|
|
12
|
+
id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
13
|
+
title: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
14
|
+
description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
15
|
+
completionEvent: {
|
|
16
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
17
|
+
isOptional: false,
|
|
18
|
+
},
|
|
19
|
+
sourceModule: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
20
|
+
xpReward: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
|
|
21
|
+
order: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const StudioOnboardingTrackModel = defineSchemaModel({
|
|
26
|
+
name: 'StudioOnboardingTrack',
|
|
27
|
+
description: 'Studio onboarding track definition',
|
|
28
|
+
fields: {
|
|
29
|
+
id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
30
|
+
name: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
31
|
+
description: { type: ScalarTypeEnum.String_unsecure(), isOptional: true },
|
|
32
|
+
totalXp: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
|
|
33
|
+
completionXpBonus: {
|
|
34
|
+
type: ScalarTypeEnum.Int_unsecure(),
|
|
35
|
+
isOptional: true,
|
|
36
|
+
},
|
|
37
|
+
completionBadgeKey: {
|
|
38
|
+
type: ScalarTypeEnum.String_unsecure(),
|
|
39
|
+
isOptional: true,
|
|
40
|
+
},
|
|
41
|
+
streakHoursWindow: {
|
|
42
|
+
type: ScalarTypeEnum.Int_unsecure(),
|
|
43
|
+
isOptional: true,
|
|
44
|
+
},
|
|
45
|
+
streakBonusXp: { type: ScalarTypeEnum.Int_unsecure(), isOptional: true },
|
|
46
|
+
steps: { type: StepModel, isArray: true, isOptional: false },
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const TrackResponseModel = defineSchemaModel({
|
|
51
|
+
name: 'StudioOnboardingTrackResponse',
|
|
52
|
+
description: 'Response wrapper for studio onboarding track',
|
|
53
|
+
fields: {
|
|
54
|
+
track: { type: StudioOnboardingTrackModel, isOptional: false },
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const RecordDemoEventInput = defineSchemaModel({
|
|
59
|
+
name: 'StudioOnboardingRecordEventInput',
|
|
60
|
+
description: 'Emit a demo event to advance Studio onboarding steps',
|
|
61
|
+
fields: {
|
|
62
|
+
learnerId: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
63
|
+
eventName: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },
|
|
64
|
+
payload: { type: ScalarTypeEnum.JSON(), isOptional: true },
|
|
65
|
+
occurredAt: { type: ScalarTypeEnum.DateTime(), isOptional: true },
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const SuccessModel = defineSchemaModel({
|
|
70
|
+
name: 'StudioOnboardingSuccess',
|
|
71
|
+
description: 'Generic success response',
|
|
72
|
+
fields: {
|
|
73
|
+
success: { type: ScalarTypeEnum.Boolean(), isOptional: false },
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export const GetStudioOnboardingTrack = defineQuery({
|
|
78
|
+
meta: {
|
|
79
|
+
name: 'learningJourney.studioOnboarding.getTrack',
|
|
80
|
+
version: 1,
|
|
81
|
+
stability: 'experimental',
|
|
82
|
+
owners: [...OWNERS],
|
|
83
|
+
tags: ['learning', 'onboarding', 'studio'],
|
|
84
|
+
description: 'Fetch the Studio onboarding track definition.',
|
|
85
|
+
goal: 'Expose track metadata to UIs and templates.',
|
|
86
|
+
context: 'Called by Studio/Playground to render journey steps.',
|
|
87
|
+
},
|
|
88
|
+
io: {
|
|
89
|
+
input: defineSchemaModel({
|
|
90
|
+
name: 'StudioOnboardingTrackInput',
|
|
91
|
+
description: 'Track input',
|
|
92
|
+
fields: {},
|
|
93
|
+
}),
|
|
94
|
+
output: TrackResponseModel,
|
|
95
|
+
},
|
|
96
|
+
policy: { auth: 'user' },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export const RecordStudioOnboardingEvent = defineCommand({
|
|
100
|
+
meta: {
|
|
101
|
+
name: 'learningJourney.studioOnboarding.recordEvent',
|
|
102
|
+
version: 1,
|
|
103
|
+
stability: 'experimental',
|
|
104
|
+
owners: [...OWNERS],
|
|
105
|
+
tags: ['learning', 'onboarding', 'studio'],
|
|
106
|
+
description: 'Record an event to advance Studio onboarding progress.',
|
|
107
|
+
goal: 'Advance steps via domain events in demo/sandbox contexts.',
|
|
108
|
+
context:
|
|
109
|
+
'Called by handlers or demo scripts to emit step completion events.',
|
|
110
|
+
},
|
|
111
|
+
io: {
|
|
112
|
+
input: RecordDemoEventInput,
|
|
113
|
+
output: SuccessModel,
|
|
114
|
+
},
|
|
115
|
+
policy: { auth: 'user' },
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export const studioOnboardingContracts = {
|
|
119
|
+
GetStudioOnboardingTrack,
|
|
120
|
+
RecordStudioOnboardingEvent,
|
|
121
|
+
track: studioGettingStartedTrack,
|
|
122
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './studio-onboarding.docblock';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DocBlock } from '@lssm/lib.contracts/docs';
|
|
2
|
+
import { registerDocBlocks } from '@lssm/lib.contracts/docs';
|
|
3
|
+
|
|
4
|
+
const studioOnboardingDocBlocks: DocBlock[] = [
|
|
5
|
+
{
|
|
6
|
+
id: 'docs.learning-journey.studio-onboarding',
|
|
7
|
+
title: 'Learning Journey — Studio Getting Started',
|
|
8
|
+
summary:
|
|
9
|
+
'Track that guides a new Studio user through template spawn, spec edit, regeneration, playground, and evolution.',
|
|
10
|
+
kind: 'reference',
|
|
11
|
+
visibility: 'public',
|
|
12
|
+
route: '/docs/learning-journey/studio-onboarding',
|
|
13
|
+
tags: ['learning', 'onboarding', 'studio'],
|
|
14
|
+
body: `## Track
|
|
15
|
+
- **Key**: \`studio_getting_started\`
|
|
16
|
+
- **Persona**: new Studio developer, first 30 minutes
|
|
17
|
+
- **Goal**: instantiate template → edit spec → regenerate → play → run evolution
|
|
18
|
+
|
|
19
|
+
## Steps & Events
|
|
20
|
+
1) \`choose_template\` → event \`studio.template.instantiated\`
|
|
21
|
+
2) \`edit_spec\` → event \`spec.changed\` (scope: sandbox)
|
|
22
|
+
3) \`regenerate_app\` → event \`regeneration.completed\`
|
|
23
|
+
4) \`play_in_playground\` → event \`playground.session.started\`
|
|
24
|
+
5) \`try_evolution_mode\` → event \`studio.evolution.applied\`
|
|
25
|
+
|
|
26
|
+
XP: 20/20/20/20/30 with bonus 25 XP if completed within 48h (streak rule). Badge: \`studio_first_30m\`.
|
|
27
|
+
|
|
28
|
+
## Wiring
|
|
29
|
+
- Tracks export from \`@lssm/example.learning-journey-studio-onboarding/track\`.
|
|
30
|
+
- Use onboarding API:
|
|
31
|
+
- \`learning.onboarding.listTracks\` to surface catalog
|
|
32
|
+
- \`learning.onboarding.getProgress\` to render progress
|
|
33
|
+
- \`learning.onboarding.recordEvent\` to advance from bus events
|
|
34
|
+
- Events should be emitted by Studio surfaces (template creation, spec save, regeneration, playground session start, evolution apply).`,
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
registerDocBlocks(studioOnboardingDocBlocks);
|
package/src/example.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const example = {
|
|
2
|
+
id: 'learning-journey-studio-onboarding',
|
|
3
|
+
title: 'Learning Journey — Studio Getting Started',
|
|
4
|
+
summary:
|
|
5
|
+
'Onboarding track guiding a new Studio user through template spawn, spec edit, regeneration, playground, and evolution.',
|
|
6
|
+
tags: ['learning', 'onboarding', 'studio'],
|
|
7
|
+
kind: 'template',
|
|
8
|
+
visibility: 'public',
|
|
9
|
+
docs: {
|
|
10
|
+
rootDocId: 'docs.learning-journey.studio-onboarding',
|
|
11
|
+
},
|
|
12
|
+
entrypoints: {
|
|
13
|
+
packageName: '@lssm/example.learning-journey-studio-onboarding',
|
|
14
|
+
docs: './docs',
|
|
15
|
+
},
|
|
16
|
+
surfaces: {
|
|
17
|
+
templates: true,
|
|
18
|
+
sandbox: { enabled: true, modes: ['playground', 'markdown'] },
|
|
19
|
+
studio: { enabled: true, installable: true },
|
|
20
|
+
mcp: { enabled: true },
|
|
21
|
+
},
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
export default example;
|
|
25
|
+
|
|
26
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { studioGettingStartedTrack } from '../track';
|
|
2
|
+
|
|
3
|
+
interface EmitParams {
|
|
4
|
+
learnerId: string;
|
|
5
|
+
occurredAt?: Date;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface LearningJourneyEvent {
|
|
9
|
+
learnerId: string;
|
|
10
|
+
name: string;
|
|
11
|
+
occurredAt: Date;
|
|
12
|
+
trackId: string;
|
|
13
|
+
payload?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type RecordEvent = (event: LearningJourneyEvent) => unknown;
|
|
17
|
+
|
|
18
|
+
export const studioOnboardingEvents = [
|
|
19
|
+
'studio.template.instantiated',
|
|
20
|
+
'spec.changed',
|
|
21
|
+
'regeneration.completed',
|
|
22
|
+
'playground.session.started',
|
|
23
|
+
'studio.evolution.applied',
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
export type StudioEvent = (typeof studioOnboardingEvents)[number];
|
|
27
|
+
|
|
28
|
+
export const emitStudioOnboardingEvent = (
|
|
29
|
+
eventName: StudioEvent,
|
|
30
|
+
{ learnerId, occurredAt = new Date() }: EmitParams,
|
|
31
|
+
record?: RecordEvent
|
|
32
|
+
) => {
|
|
33
|
+
const event: LearningJourneyEvent = {
|
|
34
|
+
learnerId,
|
|
35
|
+
name: eventName,
|
|
36
|
+
occurredAt,
|
|
37
|
+
trackId: studioGettingStartedTrack.id,
|
|
38
|
+
};
|
|
39
|
+
return record ? record(event) : event;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const emitAllStudioOnboardingEvents = (
|
|
43
|
+
params: EmitParams,
|
|
44
|
+
record?: RecordEvent
|
|
45
|
+
) =>
|
|
46
|
+
studioOnboardingEvents.map((name) =>
|
|
47
|
+
emitStudioOnboardingEvent(name, params, record)
|
|
48
|
+
);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PresentationDescriptorV2,
|
|
3
|
+
PresentationV2Meta,
|
|
4
|
+
} from '@lssm/lib.contracts';
|
|
5
|
+
import { StudioOnboardingTrackModel } from '../contracts';
|
|
6
|
+
|
|
7
|
+
const baseMeta: Pick<PresentationV2Meta, 'domain' | 'owners' | 'tags'> = {
|
|
8
|
+
domain: 'learning-journey',
|
|
9
|
+
owners: ['examples.learning-journey.studio-onboarding'] as string[],
|
|
10
|
+
tags: ['learning', 'onboarding', 'studio'] as string[],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const StudioOnboardingTrackPresentation: PresentationDescriptorV2 = {
|
|
14
|
+
meta: {
|
|
15
|
+
name: 'learning.journey.studio.track',
|
|
16
|
+
version: 1,
|
|
17
|
+
description: 'Studio onboarding track detail',
|
|
18
|
+
...baseMeta,
|
|
19
|
+
},
|
|
20
|
+
source: {
|
|
21
|
+
type: 'component',
|
|
22
|
+
framework: 'react',
|
|
23
|
+
componentKey: 'LearningTrackDetail',
|
|
24
|
+
props: StudioOnboardingTrackModel,
|
|
25
|
+
},
|
|
26
|
+
targets: ['react', 'markdown', 'application/json'],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const StudioOnboardingWidgetPresentation: PresentationDescriptorV2 = {
|
|
30
|
+
meta: {
|
|
31
|
+
name: 'learning.journey.studio.widget',
|
|
32
|
+
version: 1,
|
|
33
|
+
description: 'Compact widget for Studio onboarding progress',
|
|
34
|
+
...baseMeta,
|
|
35
|
+
},
|
|
36
|
+
source: {
|
|
37
|
+
type: 'component',
|
|
38
|
+
framework: 'react',
|
|
39
|
+
componentKey: 'LearningTrackProgressWidget',
|
|
40
|
+
},
|
|
41
|
+
targets: ['react'],
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const studioOnboardingPresentations = [
|
|
45
|
+
StudioOnboardingTrackPresentation,
|
|
46
|
+
StudioOnboardingWidgetPresentation,
|
|
47
|
+
];
|
package/src/track.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { LearningJourneyTrackSpec } from '@lssm/module.learning-journey/track-spec';
|
|
2
|
+
|
|
3
|
+
export const studioGettingStartedTrack: LearningJourneyTrackSpec = {
|
|
4
|
+
id: 'studio_getting_started',
|
|
5
|
+
productId: 'contractspec-studio',
|
|
6
|
+
name: 'Studio Getting Started',
|
|
7
|
+
description:
|
|
8
|
+
'First 30 minutes in Studio: instantiate a template, edit the spec, regenerate, and try runtime surfaces.',
|
|
9
|
+
targetUserSegment: 'new_studio_user',
|
|
10
|
+
targetRole: 'developer',
|
|
11
|
+
totalXp: 110,
|
|
12
|
+
streakRule: { hoursWindow: 48, bonusXp: 25 },
|
|
13
|
+
completionRewards: { xpBonus: 25, badgeKey: 'studio_first_30m' },
|
|
14
|
+
steps: [
|
|
15
|
+
{
|
|
16
|
+
id: 'choose_template',
|
|
17
|
+
title: 'Choose a template',
|
|
18
|
+
description: 'Pick a Phase 1 template and create a sandbox.',
|
|
19
|
+
order: 1,
|
|
20
|
+
completion: {
|
|
21
|
+
eventName: 'studio.template.instantiated',
|
|
22
|
+
sourceModule: '@lssm/bundle.contractspec-studio',
|
|
23
|
+
},
|
|
24
|
+
xpReward: 20,
|
|
25
|
+
metadata: { surface: 'templates' },
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'edit_spec',
|
|
29
|
+
title: 'Edit the spec',
|
|
30
|
+
description: 'Change the spec (not generated code) and save.',
|
|
31
|
+
instructions: 'Open spec editor, tweak a contract or presentation, save.',
|
|
32
|
+
order: 2,
|
|
33
|
+
completion: {
|
|
34
|
+
eventName: 'spec.changed',
|
|
35
|
+
sourceModule: '@lssm/lib.contracts',
|
|
36
|
+
payloadFilter: { scope: 'sandbox' },
|
|
37
|
+
},
|
|
38
|
+
xpReward: 20,
|
|
39
|
+
metadata: { surface: 'spec-editor' },
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'regenerate_app',
|
|
43
|
+
title: 'Regenerate the app',
|
|
44
|
+
description: 'Regenerate artifacts from the updated spec.',
|
|
45
|
+
order: 3,
|
|
46
|
+
completion: {
|
|
47
|
+
eventName: 'regeneration.completed',
|
|
48
|
+
sourceModule: '@lssm/lib.contracts/regenerator',
|
|
49
|
+
},
|
|
50
|
+
xpReward: 20,
|
|
51
|
+
metadata: { surface: 'regenerator' },
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'play_in_playground',
|
|
55
|
+
title: 'Use the playground',
|
|
56
|
+
description:
|
|
57
|
+
'Start a playground or runtime session against your sandbox.',
|
|
58
|
+
order: 4,
|
|
59
|
+
completion: {
|
|
60
|
+
eventName: 'playground.session.started',
|
|
61
|
+
sourceModule: '@lssm/bundle.contractspec-studio',
|
|
62
|
+
},
|
|
63
|
+
xpReward: 20,
|
|
64
|
+
metadata: { surface: 'playground' },
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'try_evolution_mode',
|
|
68
|
+
title: 'Try evolution mode',
|
|
69
|
+
description: 'Request a change via Evolution, then regenerate.',
|
|
70
|
+
order: 5,
|
|
71
|
+
completion: {
|
|
72
|
+
eventName: 'studio.evolution.applied',
|
|
73
|
+
sourceModule: '@lssm/lib.evolution',
|
|
74
|
+
},
|
|
75
|
+
xpReward: 30,
|
|
76
|
+
metadata: { surface: 'evolution' },
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
metadata: {
|
|
80
|
+
persona: 'first_run',
|
|
81
|
+
surfacedIn: ['studio/home', 'studio/sidebar/learning'],
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const studioLearningTracks: LearningJourneyTrackSpec[] = [
|
|
86
|
+
studioGettingStartedTrack,
|
|
87
|
+
];
|