@contractspec/module.lifecycle-core 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/README.md +46 -56
- package/dist/browser/index.js +161 -161
- package/dist/collectors/signal-collector.d.ts +1 -1
- package/dist/i18n/catalogs/index.d.ts +1 -1
- package/dist/i18n/index.d.ts +7 -7
- package/dist/i18n/locale.d.ts +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +161 -161
- package/dist/node/index.js +161 -161
- package/dist/orchestrator/lifecycle-orchestrator.d.ts +1 -1
- package/package.json +5 -5
- package/src/data/milestones-catalog.json +85 -85
- package/src/data/stage-weights.json +107 -107
package/dist/index.js
CHANGED
|
@@ -79,11 +79,168 @@ var dedupeSignals = (signals) => {
|
|
|
79
79
|
return true;
|
|
80
80
|
});
|
|
81
81
|
};
|
|
82
|
-
// src/
|
|
82
|
+
// src/orchestrator/lifecycle-orchestrator.ts
|
|
83
83
|
import {
|
|
84
|
+
getLocalizedStageMeta,
|
|
84
85
|
LIFECYCLE_STAGE_META,
|
|
85
86
|
LifecycleStage
|
|
86
87
|
} from "@contractspec/lib.lifecycle";
|
|
88
|
+
|
|
89
|
+
class LifecycleOrchestrator {
|
|
90
|
+
collector;
|
|
91
|
+
scorer;
|
|
92
|
+
planner;
|
|
93
|
+
locale;
|
|
94
|
+
constructor(options) {
|
|
95
|
+
this.collector = options.collector;
|
|
96
|
+
this.scorer = options.scorer;
|
|
97
|
+
this.planner = options.milestonePlanner;
|
|
98
|
+
this.locale = options.locale;
|
|
99
|
+
}
|
|
100
|
+
async run(input) {
|
|
101
|
+
const collected = await this.collector.collect(input);
|
|
102
|
+
const scorecard = this.scorer.score({
|
|
103
|
+
metrics: collected.metrics,
|
|
104
|
+
signals: collected.signals
|
|
105
|
+
});
|
|
106
|
+
const top = scorecard[0] ?? fallbackScore();
|
|
107
|
+
const stageMeta = this.locale ? getLocalizedStageMeta(this.locale) : LIFECYCLE_STAGE_META;
|
|
108
|
+
const meta = stageMeta[top.stage];
|
|
109
|
+
return {
|
|
110
|
+
stage: top.stage,
|
|
111
|
+
confidence: top.confidence,
|
|
112
|
+
axes: collected.axes,
|
|
113
|
+
signals: collected.signals,
|
|
114
|
+
metrics: toMetricRecord(collected.metrics),
|
|
115
|
+
gaps: meta.focusAreas.slice(0, 3),
|
|
116
|
+
focusAreas: meta.focusAreas,
|
|
117
|
+
scorecard,
|
|
118
|
+
generatedAt: new Date().toISOString()
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
getUpcomingMilestones(stage, completedMilestoneIds = [], limit = 5) {
|
|
122
|
+
if (!this.planner)
|
|
123
|
+
return [];
|
|
124
|
+
return this.planner.getUpcoming(stage, completedMilestoneIds, limit);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
var fallbackScore = () => ({
|
|
128
|
+
stage: LifecycleStage.Exploration,
|
|
129
|
+
score: 0.3,
|
|
130
|
+
confidence: 0.3,
|
|
131
|
+
supportingSignals: []
|
|
132
|
+
});
|
|
133
|
+
var toMetricRecord = (snapshot) => Object.entries(snapshot ?? {}).reduce((acc, [key, value]) => {
|
|
134
|
+
if (typeof value === "number") {
|
|
135
|
+
acc[key] = value;
|
|
136
|
+
}
|
|
137
|
+
return acc;
|
|
138
|
+
}, {});
|
|
139
|
+
// src/data/milestones-catalog.json
|
|
140
|
+
var milestones_catalog_default = [
|
|
141
|
+
{
|
|
142
|
+
id: "stage0-problem-statement",
|
|
143
|
+
stage: 0,
|
|
144
|
+
category: "product",
|
|
145
|
+
title: "Write the pain statement",
|
|
146
|
+
description: "Capture the clearest description of the top problem in the customer\u2019s own words.",
|
|
147
|
+
priority: 1,
|
|
148
|
+
actionItems: [
|
|
149
|
+
"Interview at least 5 ideal customers",
|
|
150
|
+
"Synthesize quotes into a one-page brief"
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "stage1-prototype-loop",
|
|
155
|
+
stage: 1,
|
|
156
|
+
category: "product",
|
|
157
|
+
title: "Prototype feedback loop",
|
|
158
|
+
description: "Ship a clickable prototype and gather 3 rounds of feedback.",
|
|
159
|
+
priority: 2,
|
|
160
|
+
actionItems: [
|
|
161
|
+
"Create a low-fidelity prototype",
|
|
162
|
+
"Schedule standing feedback calls"
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "stage2-activation",
|
|
167
|
+
stage: 2,
|
|
168
|
+
category: "operations",
|
|
169
|
+
title: "Activation checklist",
|
|
170
|
+
description: "Define the minimum steps required for a new user to succeed.",
|
|
171
|
+
priority: 1,
|
|
172
|
+
actionItems: [
|
|
173
|
+
"Document onboarding flow",
|
|
174
|
+
"Instrument activation analytics"
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
id: "stage3-retention-narrative",
|
|
179
|
+
stage: 3,
|
|
180
|
+
category: "product",
|
|
181
|
+
title: "Retention narrative",
|
|
182
|
+
description: "Create the before/after story that proves why users stay.",
|
|
183
|
+
priority: 2,
|
|
184
|
+
actionItems: [
|
|
185
|
+
"Interview 3 retained users",
|
|
186
|
+
"Publish a one-pager with concrete metrics"
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "stage4-growth-loop",
|
|
191
|
+
stage: 4,
|
|
192
|
+
category: "growth",
|
|
193
|
+
title: "Install a growth loop",
|
|
194
|
+
description: "Stand up a repeatable acquisition \u2192 activation \u2192 referral motion.",
|
|
195
|
+
priority: 1,
|
|
196
|
+
actionItems: [
|
|
197
|
+
"Define loop metrics",
|
|
198
|
+
"Assign owners for each stage",
|
|
199
|
+
"Review weekly"
|
|
200
|
+
]
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: "stage5-platform-blueprint",
|
|
204
|
+
stage: 5,
|
|
205
|
+
category: "product",
|
|
206
|
+
title: "Platform blueprint",
|
|
207
|
+
description: "Align on APIs, integrations, and governance for partners.",
|
|
208
|
+
priority: 2,
|
|
209
|
+
actionItems: [
|
|
210
|
+
"Create integration scoring rubric",
|
|
211
|
+
"Publish partner onboarding checklist"
|
|
212
|
+
]
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: "stage6-renewal-ops",
|
|
216
|
+
stage: 6,
|
|
217
|
+
category: "operations",
|
|
218
|
+
title: "Renewal operating rhythm",
|
|
219
|
+
description: "Decide whether to optimize, reinvest, or sunset each major surface.",
|
|
220
|
+
priority: 1,
|
|
221
|
+
actionItems: [
|
|
222
|
+
"Hold quarterly renewal review",
|
|
223
|
+
"Document reinvestment bets"
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
];
|
|
227
|
+
|
|
228
|
+
// src/planning/milestone-planner.ts
|
|
229
|
+
class LifecycleMilestonePlanner {
|
|
230
|
+
milestones;
|
|
231
|
+
constructor(customCatalog) {
|
|
232
|
+
this.milestones = customCatalog ?? milestones_catalog_default;
|
|
233
|
+
}
|
|
234
|
+
getUpcoming(stage, completedIds = [], limit = 5) {
|
|
235
|
+
const completedSet = new Set(completedIds);
|
|
236
|
+
return this.milestones.filter((milestone) => milestone.stage === stage && !completedSet.has(milestone.id)).sort((a, b) => a.priority - b.priority).slice(0, limit);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// src/scoring/stage-scorer.ts
|
|
240
|
+
import {
|
|
241
|
+
LIFECYCLE_STAGE_META as LIFECYCLE_STAGE_META2,
|
|
242
|
+
LifecycleStage as LifecycleStage2
|
|
243
|
+
} from "@contractspec/lib.lifecycle";
|
|
87
244
|
// src/data/stage-weights.json
|
|
88
245
|
var stage_weights_default = {
|
|
89
246
|
Exploration: {
|
|
@@ -205,8 +362,8 @@ class StageScorer {
|
|
|
205
362
|
}
|
|
206
363
|
score(input) {
|
|
207
364
|
const kindStrength = evaluateSignalKinds(input.signals);
|
|
208
|
-
const scores = Object.values(
|
|
209
|
-
const stageName =
|
|
365
|
+
const scores = Object.values(LifecycleStage2).filter(isStageValue).map((stage) => {
|
|
366
|
+
const stageName = LifecycleStage2[stage];
|
|
210
367
|
const config = this.weights[stageName] ?? { base: 0.5 };
|
|
211
368
|
let score = config.base ?? 0.5;
|
|
212
369
|
let contributions = 0;
|
|
@@ -266,164 +423,7 @@ var evaluateSignalKinds = (signals) => signals.reduce((acc, signal) => {
|
|
|
266
423
|
acc[key] = (acc[key] ?? 0) + (signal.weight ?? 1);
|
|
267
424
|
return acc;
|
|
268
425
|
}, {});
|
|
269
|
-
var isStageValue = (value) => typeof value === "number" && (value in
|
|
270
|
-
// src/orchestrator/lifecycle-orchestrator.ts
|
|
271
|
-
import {
|
|
272
|
-
LIFECYCLE_STAGE_META as LIFECYCLE_STAGE_META2,
|
|
273
|
-
LifecycleStage as LifecycleStage2,
|
|
274
|
-
getLocalizedStageMeta
|
|
275
|
-
} from "@contractspec/lib.lifecycle";
|
|
276
|
-
|
|
277
|
-
class LifecycleOrchestrator {
|
|
278
|
-
collector;
|
|
279
|
-
scorer;
|
|
280
|
-
planner;
|
|
281
|
-
locale;
|
|
282
|
-
constructor(options) {
|
|
283
|
-
this.collector = options.collector;
|
|
284
|
-
this.scorer = options.scorer;
|
|
285
|
-
this.planner = options.milestonePlanner;
|
|
286
|
-
this.locale = options.locale;
|
|
287
|
-
}
|
|
288
|
-
async run(input) {
|
|
289
|
-
const collected = await this.collector.collect(input);
|
|
290
|
-
const scorecard = this.scorer.score({
|
|
291
|
-
metrics: collected.metrics,
|
|
292
|
-
signals: collected.signals
|
|
293
|
-
});
|
|
294
|
-
const top = scorecard[0] ?? fallbackScore();
|
|
295
|
-
const stageMeta = this.locale ? getLocalizedStageMeta(this.locale) : LIFECYCLE_STAGE_META2;
|
|
296
|
-
const meta = stageMeta[top.stage];
|
|
297
|
-
return {
|
|
298
|
-
stage: top.stage,
|
|
299
|
-
confidence: top.confidence,
|
|
300
|
-
axes: collected.axes,
|
|
301
|
-
signals: collected.signals,
|
|
302
|
-
metrics: toMetricRecord(collected.metrics),
|
|
303
|
-
gaps: meta.focusAreas.slice(0, 3),
|
|
304
|
-
focusAreas: meta.focusAreas,
|
|
305
|
-
scorecard,
|
|
306
|
-
generatedAt: new Date().toISOString()
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
getUpcomingMilestones(stage, completedMilestoneIds = [], limit = 5) {
|
|
310
|
-
if (!this.planner)
|
|
311
|
-
return [];
|
|
312
|
-
return this.planner.getUpcoming(stage, completedMilestoneIds, limit);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
var fallbackScore = () => ({
|
|
316
|
-
stage: LifecycleStage2.Exploration,
|
|
317
|
-
score: 0.3,
|
|
318
|
-
confidence: 0.3,
|
|
319
|
-
supportingSignals: []
|
|
320
|
-
});
|
|
321
|
-
var toMetricRecord = (snapshot) => Object.entries(snapshot ?? {}).reduce((acc, [key, value]) => {
|
|
322
|
-
if (typeof value === "number") {
|
|
323
|
-
acc[key] = value;
|
|
324
|
-
}
|
|
325
|
-
return acc;
|
|
326
|
-
}, {});
|
|
327
|
-
// src/data/milestones-catalog.json
|
|
328
|
-
var milestones_catalog_default = [
|
|
329
|
-
{
|
|
330
|
-
id: "stage0-problem-statement",
|
|
331
|
-
stage: 0,
|
|
332
|
-
category: "product",
|
|
333
|
-
title: "Write the pain statement",
|
|
334
|
-
description: "Capture the clearest description of the top problem in the customer\u2019s own words.",
|
|
335
|
-
priority: 1,
|
|
336
|
-
actionItems: [
|
|
337
|
-
"Interview at least 5 ideal customers",
|
|
338
|
-
"Synthesize quotes into a one-page brief"
|
|
339
|
-
]
|
|
340
|
-
},
|
|
341
|
-
{
|
|
342
|
-
id: "stage1-prototype-loop",
|
|
343
|
-
stage: 1,
|
|
344
|
-
category: "product",
|
|
345
|
-
title: "Prototype feedback loop",
|
|
346
|
-
description: "Ship a clickable prototype and gather 3 rounds of feedback.",
|
|
347
|
-
priority: 2,
|
|
348
|
-
actionItems: [
|
|
349
|
-
"Create a low-fidelity prototype",
|
|
350
|
-
"Schedule standing feedback calls"
|
|
351
|
-
]
|
|
352
|
-
},
|
|
353
|
-
{
|
|
354
|
-
id: "stage2-activation",
|
|
355
|
-
stage: 2,
|
|
356
|
-
category: "operations",
|
|
357
|
-
title: "Activation checklist",
|
|
358
|
-
description: "Define the minimum steps required for a new user to succeed.",
|
|
359
|
-
priority: 1,
|
|
360
|
-
actionItems: [
|
|
361
|
-
"Document onboarding flow",
|
|
362
|
-
"Instrument activation analytics"
|
|
363
|
-
]
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
id: "stage3-retention-narrative",
|
|
367
|
-
stage: 3,
|
|
368
|
-
category: "product",
|
|
369
|
-
title: "Retention narrative",
|
|
370
|
-
description: "Create the before/after story that proves why users stay.",
|
|
371
|
-
priority: 2,
|
|
372
|
-
actionItems: [
|
|
373
|
-
"Interview 3 retained users",
|
|
374
|
-
"Publish a one-pager with concrete metrics"
|
|
375
|
-
]
|
|
376
|
-
},
|
|
377
|
-
{
|
|
378
|
-
id: "stage4-growth-loop",
|
|
379
|
-
stage: 4,
|
|
380
|
-
category: "growth",
|
|
381
|
-
title: "Install a growth loop",
|
|
382
|
-
description: "Stand up a repeatable acquisition \u2192 activation \u2192 referral motion.",
|
|
383
|
-
priority: 1,
|
|
384
|
-
actionItems: [
|
|
385
|
-
"Define loop metrics",
|
|
386
|
-
"Assign owners for each stage",
|
|
387
|
-
"Review weekly"
|
|
388
|
-
]
|
|
389
|
-
},
|
|
390
|
-
{
|
|
391
|
-
id: "stage5-platform-blueprint",
|
|
392
|
-
stage: 5,
|
|
393
|
-
category: "product",
|
|
394
|
-
title: "Platform blueprint",
|
|
395
|
-
description: "Align on APIs, integrations, and governance for partners.",
|
|
396
|
-
priority: 2,
|
|
397
|
-
actionItems: [
|
|
398
|
-
"Create integration scoring rubric",
|
|
399
|
-
"Publish partner onboarding checklist"
|
|
400
|
-
]
|
|
401
|
-
},
|
|
402
|
-
{
|
|
403
|
-
id: "stage6-renewal-ops",
|
|
404
|
-
stage: 6,
|
|
405
|
-
category: "operations",
|
|
406
|
-
title: "Renewal operating rhythm",
|
|
407
|
-
description: "Decide whether to optimize, reinvest, or sunset each major surface.",
|
|
408
|
-
priority: 1,
|
|
409
|
-
actionItems: [
|
|
410
|
-
"Hold quarterly renewal review",
|
|
411
|
-
"Document reinvestment bets"
|
|
412
|
-
]
|
|
413
|
-
}
|
|
414
|
-
];
|
|
415
|
-
|
|
416
|
-
// src/planning/milestone-planner.ts
|
|
417
|
-
class LifecycleMilestonePlanner {
|
|
418
|
-
milestones;
|
|
419
|
-
constructor(customCatalog) {
|
|
420
|
-
this.milestones = customCatalog ?? milestones_catalog_default;
|
|
421
|
-
}
|
|
422
|
-
getUpcoming(stage, completedIds = [], limit = 5) {
|
|
423
|
-
const completedSet = new Set(completedIds);
|
|
424
|
-
return this.milestones.filter((milestone) => milestone.stage === stage && !completedSet.has(milestone.id)).sort((a, b) => a.priority - b.priority).slice(0, limit);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
426
|
+
var isStageValue = (value) => typeof value === "number" && (value in LIFECYCLE_STAGE_META2);
|
|
427
427
|
export {
|
|
428
428
|
StageSignalCollector,
|
|
429
429
|
StageScorer,
|
package/dist/node/index.js
CHANGED
|
@@ -78,11 +78,168 @@ var dedupeSignals = (signals) => {
|
|
|
78
78
|
return true;
|
|
79
79
|
});
|
|
80
80
|
};
|
|
81
|
-
// src/
|
|
81
|
+
// src/orchestrator/lifecycle-orchestrator.ts
|
|
82
82
|
import {
|
|
83
|
+
getLocalizedStageMeta,
|
|
83
84
|
LIFECYCLE_STAGE_META,
|
|
84
85
|
LifecycleStage
|
|
85
86
|
} from "@contractspec/lib.lifecycle";
|
|
87
|
+
|
|
88
|
+
class LifecycleOrchestrator {
|
|
89
|
+
collector;
|
|
90
|
+
scorer;
|
|
91
|
+
planner;
|
|
92
|
+
locale;
|
|
93
|
+
constructor(options) {
|
|
94
|
+
this.collector = options.collector;
|
|
95
|
+
this.scorer = options.scorer;
|
|
96
|
+
this.planner = options.milestonePlanner;
|
|
97
|
+
this.locale = options.locale;
|
|
98
|
+
}
|
|
99
|
+
async run(input) {
|
|
100
|
+
const collected = await this.collector.collect(input);
|
|
101
|
+
const scorecard = this.scorer.score({
|
|
102
|
+
metrics: collected.metrics,
|
|
103
|
+
signals: collected.signals
|
|
104
|
+
});
|
|
105
|
+
const top = scorecard[0] ?? fallbackScore();
|
|
106
|
+
const stageMeta = this.locale ? getLocalizedStageMeta(this.locale) : LIFECYCLE_STAGE_META;
|
|
107
|
+
const meta = stageMeta[top.stage];
|
|
108
|
+
return {
|
|
109
|
+
stage: top.stage,
|
|
110
|
+
confidence: top.confidence,
|
|
111
|
+
axes: collected.axes,
|
|
112
|
+
signals: collected.signals,
|
|
113
|
+
metrics: toMetricRecord(collected.metrics),
|
|
114
|
+
gaps: meta.focusAreas.slice(0, 3),
|
|
115
|
+
focusAreas: meta.focusAreas,
|
|
116
|
+
scorecard,
|
|
117
|
+
generatedAt: new Date().toISOString()
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
getUpcomingMilestones(stage, completedMilestoneIds = [], limit = 5) {
|
|
121
|
+
if (!this.planner)
|
|
122
|
+
return [];
|
|
123
|
+
return this.planner.getUpcoming(stage, completedMilestoneIds, limit);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
var fallbackScore = () => ({
|
|
127
|
+
stage: LifecycleStage.Exploration,
|
|
128
|
+
score: 0.3,
|
|
129
|
+
confidence: 0.3,
|
|
130
|
+
supportingSignals: []
|
|
131
|
+
});
|
|
132
|
+
var toMetricRecord = (snapshot) => Object.entries(snapshot ?? {}).reduce((acc, [key, value]) => {
|
|
133
|
+
if (typeof value === "number") {
|
|
134
|
+
acc[key] = value;
|
|
135
|
+
}
|
|
136
|
+
return acc;
|
|
137
|
+
}, {});
|
|
138
|
+
// src/data/milestones-catalog.json
|
|
139
|
+
var milestones_catalog_default = [
|
|
140
|
+
{
|
|
141
|
+
id: "stage0-problem-statement",
|
|
142
|
+
stage: 0,
|
|
143
|
+
category: "product",
|
|
144
|
+
title: "Write the pain statement",
|
|
145
|
+
description: "Capture the clearest description of the top problem in the customer’s own words.",
|
|
146
|
+
priority: 1,
|
|
147
|
+
actionItems: [
|
|
148
|
+
"Interview at least 5 ideal customers",
|
|
149
|
+
"Synthesize quotes into a one-page brief"
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: "stage1-prototype-loop",
|
|
154
|
+
stage: 1,
|
|
155
|
+
category: "product",
|
|
156
|
+
title: "Prototype feedback loop",
|
|
157
|
+
description: "Ship a clickable prototype and gather 3 rounds of feedback.",
|
|
158
|
+
priority: 2,
|
|
159
|
+
actionItems: [
|
|
160
|
+
"Create a low-fidelity prototype",
|
|
161
|
+
"Schedule standing feedback calls"
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "stage2-activation",
|
|
166
|
+
stage: 2,
|
|
167
|
+
category: "operations",
|
|
168
|
+
title: "Activation checklist",
|
|
169
|
+
description: "Define the minimum steps required for a new user to succeed.",
|
|
170
|
+
priority: 1,
|
|
171
|
+
actionItems: [
|
|
172
|
+
"Document onboarding flow",
|
|
173
|
+
"Instrument activation analytics"
|
|
174
|
+
]
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "stage3-retention-narrative",
|
|
178
|
+
stage: 3,
|
|
179
|
+
category: "product",
|
|
180
|
+
title: "Retention narrative",
|
|
181
|
+
description: "Create the before/after story that proves why users stay.",
|
|
182
|
+
priority: 2,
|
|
183
|
+
actionItems: [
|
|
184
|
+
"Interview 3 retained users",
|
|
185
|
+
"Publish a one-pager with concrete metrics"
|
|
186
|
+
]
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: "stage4-growth-loop",
|
|
190
|
+
stage: 4,
|
|
191
|
+
category: "growth",
|
|
192
|
+
title: "Install a growth loop",
|
|
193
|
+
description: "Stand up a repeatable acquisition → activation → referral motion.",
|
|
194
|
+
priority: 1,
|
|
195
|
+
actionItems: [
|
|
196
|
+
"Define loop metrics",
|
|
197
|
+
"Assign owners for each stage",
|
|
198
|
+
"Review weekly"
|
|
199
|
+
]
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: "stage5-platform-blueprint",
|
|
203
|
+
stage: 5,
|
|
204
|
+
category: "product",
|
|
205
|
+
title: "Platform blueprint",
|
|
206
|
+
description: "Align on APIs, integrations, and governance for partners.",
|
|
207
|
+
priority: 2,
|
|
208
|
+
actionItems: [
|
|
209
|
+
"Create integration scoring rubric",
|
|
210
|
+
"Publish partner onboarding checklist"
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "stage6-renewal-ops",
|
|
215
|
+
stage: 6,
|
|
216
|
+
category: "operations",
|
|
217
|
+
title: "Renewal operating rhythm",
|
|
218
|
+
description: "Decide whether to optimize, reinvest, or sunset each major surface.",
|
|
219
|
+
priority: 1,
|
|
220
|
+
actionItems: [
|
|
221
|
+
"Hold quarterly renewal review",
|
|
222
|
+
"Document reinvestment bets"
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
];
|
|
226
|
+
|
|
227
|
+
// src/planning/milestone-planner.ts
|
|
228
|
+
class LifecycleMilestonePlanner {
|
|
229
|
+
milestones;
|
|
230
|
+
constructor(customCatalog) {
|
|
231
|
+
this.milestones = customCatalog ?? milestones_catalog_default;
|
|
232
|
+
}
|
|
233
|
+
getUpcoming(stage, completedIds = [], limit = 5) {
|
|
234
|
+
const completedSet = new Set(completedIds);
|
|
235
|
+
return this.milestones.filter((milestone) => milestone.stage === stage && !completedSet.has(milestone.id)).sort((a, b) => a.priority - b.priority).slice(0, limit);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// src/scoring/stage-scorer.ts
|
|
239
|
+
import {
|
|
240
|
+
LIFECYCLE_STAGE_META as LIFECYCLE_STAGE_META2,
|
|
241
|
+
LifecycleStage as LifecycleStage2
|
|
242
|
+
} from "@contractspec/lib.lifecycle";
|
|
86
243
|
// src/data/stage-weights.json
|
|
87
244
|
var stage_weights_default = {
|
|
88
245
|
Exploration: {
|
|
@@ -204,8 +361,8 @@ class StageScorer {
|
|
|
204
361
|
}
|
|
205
362
|
score(input) {
|
|
206
363
|
const kindStrength = evaluateSignalKinds(input.signals);
|
|
207
|
-
const scores = Object.values(
|
|
208
|
-
const stageName =
|
|
364
|
+
const scores = Object.values(LifecycleStage2).filter(isStageValue).map((stage) => {
|
|
365
|
+
const stageName = LifecycleStage2[stage];
|
|
209
366
|
const config = this.weights[stageName] ?? { base: 0.5 };
|
|
210
367
|
let score = config.base ?? 0.5;
|
|
211
368
|
let contributions = 0;
|
|
@@ -265,164 +422,7 @@ var evaluateSignalKinds = (signals) => signals.reduce((acc, signal) => {
|
|
|
265
422
|
acc[key] = (acc[key] ?? 0) + (signal.weight ?? 1);
|
|
266
423
|
return acc;
|
|
267
424
|
}, {});
|
|
268
|
-
var isStageValue = (value) => typeof value === "number" && (value in
|
|
269
|
-
// src/orchestrator/lifecycle-orchestrator.ts
|
|
270
|
-
import {
|
|
271
|
-
LIFECYCLE_STAGE_META as LIFECYCLE_STAGE_META2,
|
|
272
|
-
LifecycleStage as LifecycleStage2,
|
|
273
|
-
getLocalizedStageMeta
|
|
274
|
-
} from "@contractspec/lib.lifecycle";
|
|
275
|
-
|
|
276
|
-
class LifecycleOrchestrator {
|
|
277
|
-
collector;
|
|
278
|
-
scorer;
|
|
279
|
-
planner;
|
|
280
|
-
locale;
|
|
281
|
-
constructor(options) {
|
|
282
|
-
this.collector = options.collector;
|
|
283
|
-
this.scorer = options.scorer;
|
|
284
|
-
this.planner = options.milestonePlanner;
|
|
285
|
-
this.locale = options.locale;
|
|
286
|
-
}
|
|
287
|
-
async run(input) {
|
|
288
|
-
const collected = await this.collector.collect(input);
|
|
289
|
-
const scorecard = this.scorer.score({
|
|
290
|
-
metrics: collected.metrics,
|
|
291
|
-
signals: collected.signals
|
|
292
|
-
});
|
|
293
|
-
const top = scorecard[0] ?? fallbackScore();
|
|
294
|
-
const stageMeta = this.locale ? getLocalizedStageMeta(this.locale) : LIFECYCLE_STAGE_META2;
|
|
295
|
-
const meta = stageMeta[top.stage];
|
|
296
|
-
return {
|
|
297
|
-
stage: top.stage,
|
|
298
|
-
confidence: top.confidence,
|
|
299
|
-
axes: collected.axes,
|
|
300
|
-
signals: collected.signals,
|
|
301
|
-
metrics: toMetricRecord(collected.metrics),
|
|
302
|
-
gaps: meta.focusAreas.slice(0, 3),
|
|
303
|
-
focusAreas: meta.focusAreas,
|
|
304
|
-
scorecard,
|
|
305
|
-
generatedAt: new Date().toISOString()
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
getUpcomingMilestones(stage, completedMilestoneIds = [], limit = 5) {
|
|
309
|
-
if (!this.planner)
|
|
310
|
-
return [];
|
|
311
|
-
return this.planner.getUpcoming(stage, completedMilestoneIds, limit);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
var fallbackScore = () => ({
|
|
315
|
-
stage: LifecycleStage2.Exploration,
|
|
316
|
-
score: 0.3,
|
|
317
|
-
confidence: 0.3,
|
|
318
|
-
supportingSignals: []
|
|
319
|
-
});
|
|
320
|
-
var toMetricRecord = (snapshot) => Object.entries(snapshot ?? {}).reduce((acc, [key, value]) => {
|
|
321
|
-
if (typeof value === "number") {
|
|
322
|
-
acc[key] = value;
|
|
323
|
-
}
|
|
324
|
-
return acc;
|
|
325
|
-
}, {});
|
|
326
|
-
// src/data/milestones-catalog.json
|
|
327
|
-
var milestones_catalog_default = [
|
|
328
|
-
{
|
|
329
|
-
id: "stage0-problem-statement",
|
|
330
|
-
stage: 0,
|
|
331
|
-
category: "product",
|
|
332
|
-
title: "Write the pain statement",
|
|
333
|
-
description: "Capture the clearest description of the top problem in the customer’s own words.",
|
|
334
|
-
priority: 1,
|
|
335
|
-
actionItems: [
|
|
336
|
-
"Interview at least 5 ideal customers",
|
|
337
|
-
"Synthesize quotes into a one-page brief"
|
|
338
|
-
]
|
|
339
|
-
},
|
|
340
|
-
{
|
|
341
|
-
id: "stage1-prototype-loop",
|
|
342
|
-
stage: 1,
|
|
343
|
-
category: "product",
|
|
344
|
-
title: "Prototype feedback loop",
|
|
345
|
-
description: "Ship a clickable prototype and gather 3 rounds of feedback.",
|
|
346
|
-
priority: 2,
|
|
347
|
-
actionItems: [
|
|
348
|
-
"Create a low-fidelity prototype",
|
|
349
|
-
"Schedule standing feedback calls"
|
|
350
|
-
]
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
id: "stage2-activation",
|
|
354
|
-
stage: 2,
|
|
355
|
-
category: "operations",
|
|
356
|
-
title: "Activation checklist",
|
|
357
|
-
description: "Define the minimum steps required for a new user to succeed.",
|
|
358
|
-
priority: 1,
|
|
359
|
-
actionItems: [
|
|
360
|
-
"Document onboarding flow",
|
|
361
|
-
"Instrument activation analytics"
|
|
362
|
-
]
|
|
363
|
-
},
|
|
364
|
-
{
|
|
365
|
-
id: "stage3-retention-narrative",
|
|
366
|
-
stage: 3,
|
|
367
|
-
category: "product",
|
|
368
|
-
title: "Retention narrative",
|
|
369
|
-
description: "Create the before/after story that proves why users stay.",
|
|
370
|
-
priority: 2,
|
|
371
|
-
actionItems: [
|
|
372
|
-
"Interview 3 retained users",
|
|
373
|
-
"Publish a one-pager with concrete metrics"
|
|
374
|
-
]
|
|
375
|
-
},
|
|
376
|
-
{
|
|
377
|
-
id: "stage4-growth-loop",
|
|
378
|
-
stage: 4,
|
|
379
|
-
category: "growth",
|
|
380
|
-
title: "Install a growth loop",
|
|
381
|
-
description: "Stand up a repeatable acquisition → activation → referral motion.",
|
|
382
|
-
priority: 1,
|
|
383
|
-
actionItems: [
|
|
384
|
-
"Define loop metrics",
|
|
385
|
-
"Assign owners for each stage",
|
|
386
|
-
"Review weekly"
|
|
387
|
-
]
|
|
388
|
-
},
|
|
389
|
-
{
|
|
390
|
-
id: "stage5-platform-blueprint",
|
|
391
|
-
stage: 5,
|
|
392
|
-
category: "product",
|
|
393
|
-
title: "Platform blueprint",
|
|
394
|
-
description: "Align on APIs, integrations, and governance for partners.",
|
|
395
|
-
priority: 2,
|
|
396
|
-
actionItems: [
|
|
397
|
-
"Create integration scoring rubric",
|
|
398
|
-
"Publish partner onboarding checklist"
|
|
399
|
-
]
|
|
400
|
-
},
|
|
401
|
-
{
|
|
402
|
-
id: "stage6-renewal-ops",
|
|
403
|
-
stage: 6,
|
|
404
|
-
category: "operations",
|
|
405
|
-
title: "Renewal operating rhythm",
|
|
406
|
-
description: "Decide whether to optimize, reinvest, or sunset each major surface.",
|
|
407
|
-
priority: 1,
|
|
408
|
-
actionItems: [
|
|
409
|
-
"Hold quarterly renewal review",
|
|
410
|
-
"Document reinvestment bets"
|
|
411
|
-
]
|
|
412
|
-
}
|
|
413
|
-
];
|
|
414
|
-
|
|
415
|
-
// src/planning/milestone-planner.ts
|
|
416
|
-
class LifecycleMilestonePlanner {
|
|
417
|
-
milestones;
|
|
418
|
-
constructor(customCatalog) {
|
|
419
|
-
this.milestones = customCatalog ?? milestones_catalog_default;
|
|
420
|
-
}
|
|
421
|
-
getUpcoming(stage, completedIds = [], limit = 5) {
|
|
422
|
-
const completedSet = new Set(completedIds);
|
|
423
|
-
return this.milestones.filter((milestone) => milestone.stage === stage && !completedSet.has(milestone.id)).sort((a, b) => a.priority - b.priority).slice(0, limit);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
425
|
+
var isStageValue = (value) => typeof value === "number" && (value in LIFECYCLE_STAGE_META2);
|
|
426
426
|
export {
|
|
427
427
|
StageSignalCollector,
|
|
428
428
|
StageScorer,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { LifecycleAssessment, LifecycleAssessmentInput, LifecycleMilestone } from '@contractspec/lib.lifecycle';
|
|
2
2
|
import { LifecycleStage } from '@contractspec/lib.lifecycle';
|
|
3
3
|
import { StageSignalCollector } from '../collectors/signal-collector';
|
|
4
|
-
import { StageScorer } from '../scoring/stage-scorer';
|
|
5
4
|
import { LifecycleMilestonePlanner } from '../planning/milestone-planner';
|
|
5
|
+
import { StageScorer } from '../scoring/stage-scorer';
|
|
6
6
|
export interface LifecycleOrchestratorOptions {
|
|
7
7
|
collector: StageSignalCollector;
|
|
8
8
|
scorer: StageScorer;
|