@contractspec/module.lifecycle-core 1.57.0 → 1.58.0
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/dist/__tests__/stage-scorer.test.d.ts +2 -0
- package/dist/__tests__/stage-scorer.test.d.ts.map +1 -0
- package/dist/adapters/analytics-adapter.d.ts +7 -11
- package/dist/adapters/analytics-adapter.d.ts.map +1 -1
- package/dist/adapters/intent-adapter.d.ts +5 -9
- package/dist/adapters/intent-adapter.d.ts.map +1 -1
- package/dist/adapters/questionnaire-adapter.d.ts +7 -11
- package/dist/adapters/questionnaire-adapter.d.ts.map +1 -1
- package/dist/browser/index.js +427 -0
- package/dist/collectors/signal-collector.d.ts +17 -21
- package/dist/collectors/signal-collector.d.ts.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +427 -5
- package/dist/node/index.js +427 -0
- package/dist/orchestrator/lifecycle-orchestrator.d.ts +16 -19
- package/dist/orchestrator/lifecycle-orchestrator.d.ts.map +1 -1
- package/dist/planning/milestone-planner.d.ts +5 -9
- package/dist/planning/milestone-planner.d.ts.map +1 -1
- package/dist/scoring/stage-scorer.d.ts +18 -19
- package/dist/scoring/stage-scorer.d.ts.map +1 -1
- package/package.json +20 -15
- package/dist/collectors/signal-collector.js +0 -65
- package/dist/collectors/signal-collector.js.map +0 -1
- package/dist/data/milestones-catalog.js +0 -74
- package/dist/data/milestones-catalog.js.map +0 -1
- package/dist/data/stage-weights.js +0 -170
- package/dist/data/stage-weights.js.map +0 -1
- package/dist/orchestrator/lifecycle-orchestrator.js +0 -53
- package/dist/orchestrator/lifecycle-orchestrator.js.map +0 -1
- package/dist/planning/milestone-planner.js +0 -17
- package/dist/planning/milestone-planner.js.map +0 -1
- package/dist/scoring/stage-scorer.js +0 -64
- package/dist/scoring/stage-scorer.js.map +0 -1
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
// src/collectors/signal-collector.ts
|
|
2
|
+
import {
|
|
3
|
+
CapitalPhase,
|
|
4
|
+
CompanyPhase,
|
|
5
|
+
ProductPhase
|
|
6
|
+
} from "@contractspec/lib.lifecycle";
|
|
7
|
+
var DEFAULT_AXES = {
|
|
8
|
+
product: ProductPhase.Sketch,
|
|
9
|
+
company: CompanyPhase.Solo,
|
|
10
|
+
capital: CapitalPhase.Bootstrapped
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
class StageSignalCollector {
|
|
14
|
+
options;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
}
|
|
18
|
+
async collect(input = {}) {
|
|
19
|
+
const axes = {
|
|
20
|
+
...DEFAULT_AXES,
|
|
21
|
+
...input.axes ?? {}
|
|
22
|
+
};
|
|
23
|
+
const metricsSnapshots = [];
|
|
24
|
+
const aggregatedSignals = [...input.signals ?? []];
|
|
25
|
+
const questionnaireAnswers = {
|
|
26
|
+
...input.questionnaireAnswers ?? {}
|
|
27
|
+
};
|
|
28
|
+
if (input.metrics) {
|
|
29
|
+
metricsSnapshots.push(input.metrics);
|
|
30
|
+
}
|
|
31
|
+
if (this.options.analyticsAdapter) {
|
|
32
|
+
const result = await this.options.analyticsAdapter.fetch();
|
|
33
|
+
if (result.axes)
|
|
34
|
+
Object.assign(axes, result.axes);
|
|
35
|
+
if (result.metrics)
|
|
36
|
+
metricsSnapshots.push(result.metrics);
|
|
37
|
+
if (result.signals)
|
|
38
|
+
aggregatedSignals.push(...result.signals);
|
|
39
|
+
}
|
|
40
|
+
if (this.options.questionnaireAdapter) {
|
|
41
|
+
const result = await this.options.questionnaireAdapter.fetch();
|
|
42
|
+
if (result.axes)
|
|
43
|
+
Object.assign(axes, result.axes);
|
|
44
|
+
if (result.signals)
|
|
45
|
+
aggregatedSignals.push(...result.signals);
|
|
46
|
+
Object.assign(questionnaireAnswers, result.answers);
|
|
47
|
+
}
|
|
48
|
+
if (this.options.intentAdapter) {
|
|
49
|
+
const result = await this.options.intentAdapter.fetch();
|
|
50
|
+
if (result.signals)
|
|
51
|
+
aggregatedSignals.push(...result.signals);
|
|
52
|
+
}
|
|
53
|
+
const metrics = mergeMetricSnapshots(metricsSnapshots);
|
|
54
|
+
return {
|
|
55
|
+
axes,
|
|
56
|
+
metrics,
|
|
57
|
+
signals: dedupeSignals(aggregatedSignals),
|
|
58
|
+
questionnaireAnswers: Object.keys(questionnaireAnswers).length ? questionnaireAnswers : undefined
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
var mergeMetricSnapshots = (snapshots) => snapshots.reduce((acc, snapshot) => {
|
|
63
|
+
Object.entries(snapshot ?? {}).forEach(([key, value]) => {
|
|
64
|
+
if (value !== undefined && value !== null) {
|
|
65
|
+
acc[key] = value;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return acc;
|
|
69
|
+
}, {});
|
|
70
|
+
var dedupeSignals = (signals) => {
|
|
71
|
+
const seen = new Set;
|
|
72
|
+
return signals.filter((signal) => {
|
|
73
|
+
if (!signal.id)
|
|
74
|
+
return true;
|
|
75
|
+
if (seen.has(signal.id))
|
|
76
|
+
return false;
|
|
77
|
+
seen.add(signal.id);
|
|
78
|
+
return true;
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
// src/scoring/stage-scorer.ts
|
|
82
|
+
import {
|
|
83
|
+
LIFECYCLE_STAGE_META,
|
|
84
|
+
LifecycleStage
|
|
85
|
+
} from "@contractspec/lib.lifecycle";
|
|
86
|
+
// src/data/stage-weights.json
|
|
87
|
+
var stage_weights_default = {
|
|
88
|
+
Exploration: {
|
|
89
|
+
base: 0.35,
|
|
90
|
+
metrics: {
|
|
91
|
+
activeUsers: { weight: 0.3, direction: "lte", threshold: 5 },
|
|
92
|
+
customerCount: { weight: 0.2, direction: "lte", threshold: 3 },
|
|
93
|
+
teamSize: { weight: 0.1, direction: "lte", threshold: 3 }
|
|
94
|
+
},
|
|
95
|
+
signalKinds: {
|
|
96
|
+
qualitative: 0.4,
|
|
97
|
+
metric: 0.1
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
ProblemSolutionFit: {
|
|
101
|
+
base: 0.4,
|
|
102
|
+
metrics: {
|
|
103
|
+
activeUsers: { weight: 0.2, direction: "gte", threshold: 5 },
|
|
104
|
+
customerCount: { weight: 0.2, direction: "gte", threshold: 3 }
|
|
105
|
+
},
|
|
106
|
+
signalKinds: {
|
|
107
|
+
qualitative: 0.3,
|
|
108
|
+
event: 0.1
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
MvpEarlyTraction: {
|
|
112
|
+
base: 0.45,
|
|
113
|
+
metrics: {
|
|
114
|
+
activeUsers: { weight: 0.25, direction: "gte", threshold: 25 },
|
|
115
|
+
weeklyActiveUsers: {
|
|
116
|
+
weight: 0.25,
|
|
117
|
+
direction: "gte",
|
|
118
|
+
threshold: 20
|
|
119
|
+
},
|
|
120
|
+
retentionRate: { weight: 0.2, direction: "gte", threshold: 0.25 }
|
|
121
|
+
},
|
|
122
|
+
signalKinds: {
|
|
123
|
+
metric: 0.15,
|
|
124
|
+
event: 0.1
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
ProductMarketFit: {
|
|
128
|
+
base: 0.5,
|
|
129
|
+
metrics: {
|
|
130
|
+
retentionRate: {
|
|
131
|
+
weight: 0.35,
|
|
132
|
+
direction: "gte",
|
|
133
|
+
threshold: 0.45
|
|
134
|
+
},
|
|
135
|
+
monthlyRecurringRevenue: {
|
|
136
|
+
weight: 0.25,
|
|
137
|
+
direction: "gte",
|
|
138
|
+
threshold: 1e4
|
|
139
|
+
},
|
|
140
|
+
customerCount: { weight: 0.2, direction: "gte", threshold: 30 }
|
|
141
|
+
},
|
|
142
|
+
signalKinds: {
|
|
143
|
+
metric: 0.15,
|
|
144
|
+
event: 0.1
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
GrowthScaleUp: {
|
|
148
|
+
base: 0.55,
|
|
149
|
+
metrics: {
|
|
150
|
+
retentionRate: { weight: 0.2, direction: "gte", threshold: 0.55 },
|
|
151
|
+
monthlyRecurringRevenue: {
|
|
152
|
+
weight: 0.3,
|
|
153
|
+
direction: "gte",
|
|
154
|
+
threshold: 50000
|
|
155
|
+
},
|
|
156
|
+
teamSize: { weight: 0.15, direction: "gte", threshold: 15 }
|
|
157
|
+
},
|
|
158
|
+
signalKinds: {
|
|
159
|
+
event: 0.2,
|
|
160
|
+
metric: 0.15
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
ExpansionPlatform: {
|
|
164
|
+
base: 0.6,
|
|
165
|
+
metrics: {
|
|
166
|
+
monthlyRecurringRevenue: {
|
|
167
|
+
weight: 0.35,
|
|
168
|
+
direction: "gte",
|
|
169
|
+
threshold: 150000
|
|
170
|
+
},
|
|
171
|
+
customerCount: { weight: 0.2, direction: "gte", threshold: 100 },
|
|
172
|
+
teamSize: { weight: 0.15, direction: "gte", threshold: 40 }
|
|
173
|
+
},
|
|
174
|
+
signalKinds: {
|
|
175
|
+
event: 0.2,
|
|
176
|
+
milestone: 0.1
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
MaturityRenewal: {
|
|
180
|
+
base: 0.6,
|
|
181
|
+
metrics: {
|
|
182
|
+
monthlyRecurringRevenue: {
|
|
183
|
+
weight: 0.25,
|
|
184
|
+
direction: "gte",
|
|
185
|
+
threshold: 250000
|
|
186
|
+
},
|
|
187
|
+
teamSize: { weight: 0.15, direction: "gte", threshold: 80 },
|
|
188
|
+
burnMultiple: { weight: 0.2, direction: "lte", threshold: 1.5 }
|
|
189
|
+
},
|
|
190
|
+
signalKinds: {
|
|
191
|
+
event: 0.2,
|
|
192
|
+
milestone: 0.15
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/scoring/stage-scorer.ts
|
|
198
|
+
var DEFAULT_WEIGHTS = stage_weights_default;
|
|
199
|
+
|
|
200
|
+
class StageScorer {
|
|
201
|
+
weights;
|
|
202
|
+
constructor(weights = DEFAULT_WEIGHTS) {
|
|
203
|
+
this.weights = weights;
|
|
204
|
+
}
|
|
205
|
+
score(input) {
|
|
206
|
+
const kindStrength = evaluateSignalKinds(input.signals);
|
|
207
|
+
const scores = Object.values(LifecycleStage).filter(isStageValue).map((stage) => {
|
|
208
|
+
const stageName = LifecycleStage[stage];
|
|
209
|
+
const config = this.weights[stageName] ?? { base: 0.5 };
|
|
210
|
+
let score = config.base ?? 0.5;
|
|
211
|
+
let contributions = 0;
|
|
212
|
+
const totalPossible = Object.keys(config.metrics ?? {}).length + Object.keys(config.signalKinds ?? {}).length || 1;
|
|
213
|
+
const supportingSignals = [];
|
|
214
|
+
if (config.metrics) {
|
|
215
|
+
Object.entries(config.metrics).forEach(([metricKey, metricConfig]) => {
|
|
216
|
+
const value = input.metrics[metricKey];
|
|
217
|
+
if (value === undefined || value === null)
|
|
218
|
+
return;
|
|
219
|
+
if (passesThreshold(value, metricConfig)) {
|
|
220
|
+
score += metricConfig.weight;
|
|
221
|
+
contributions += 1;
|
|
222
|
+
supportingSignals.push(`metric:${metricKey}`);
|
|
223
|
+
} else {
|
|
224
|
+
score += metricConfig.weight * 0.25;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (config.signalKinds) {
|
|
229
|
+
Object.entries(config.signalKinds).forEach(([kind, weight]) => {
|
|
230
|
+
const strength = kindStrength[kind] ?? 0;
|
|
231
|
+
if (strength > 0 && typeof weight === "number") {
|
|
232
|
+
score += weight;
|
|
233
|
+
contributions += 1;
|
|
234
|
+
supportingSignals.push(`signal:${kind}`);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
score = clamp(score, 0, 1.25);
|
|
239
|
+
const confidence = clamp(contributions / totalPossible, 0.1, 1);
|
|
240
|
+
return {
|
|
241
|
+
stage,
|
|
242
|
+
score,
|
|
243
|
+
confidence,
|
|
244
|
+
supportingSignals
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
return scores.sort((a, b) => {
|
|
248
|
+
if (b.score === a.score) {
|
|
249
|
+
return b.confidence - a.confidence;
|
|
250
|
+
}
|
|
251
|
+
return b.score - a.score;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
var passesThreshold = (value, config) => {
|
|
256
|
+
const direction = config.direction ?? "gte";
|
|
257
|
+
if (direction === "gte") {
|
|
258
|
+
return value >= config.threshold;
|
|
259
|
+
}
|
|
260
|
+
return value <= config.threshold;
|
|
261
|
+
};
|
|
262
|
+
var clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
263
|
+
var evaluateSignalKinds = (signals) => signals.reduce((acc, signal) => {
|
|
264
|
+
const key = signal.kind ?? "unknown";
|
|
265
|
+
acc[key] = (acc[key] ?? 0) + (signal.weight ?? 1);
|
|
266
|
+
return acc;
|
|
267
|
+
}, {});
|
|
268
|
+
var isStageValue = (value) => typeof value === "number" && (value in LIFECYCLE_STAGE_META);
|
|
269
|
+
// src/orchestrator/lifecycle-orchestrator.ts
|
|
270
|
+
import {
|
|
271
|
+
LIFECYCLE_STAGE_META as LIFECYCLE_STAGE_META2,
|
|
272
|
+
LifecycleStage as LifecycleStage2
|
|
273
|
+
} from "@contractspec/lib.lifecycle";
|
|
274
|
+
|
|
275
|
+
class LifecycleOrchestrator {
|
|
276
|
+
collector;
|
|
277
|
+
scorer;
|
|
278
|
+
planner;
|
|
279
|
+
constructor(options) {
|
|
280
|
+
this.collector = options.collector;
|
|
281
|
+
this.scorer = options.scorer;
|
|
282
|
+
this.planner = options.milestonePlanner;
|
|
283
|
+
}
|
|
284
|
+
async run(input) {
|
|
285
|
+
const collected = await this.collector.collect(input);
|
|
286
|
+
const scorecard = this.scorer.score({
|
|
287
|
+
metrics: collected.metrics,
|
|
288
|
+
signals: collected.signals
|
|
289
|
+
});
|
|
290
|
+
const top = scorecard[0] ?? fallbackScore();
|
|
291
|
+
const meta = LIFECYCLE_STAGE_META2[top.stage];
|
|
292
|
+
return {
|
|
293
|
+
stage: top.stage,
|
|
294
|
+
confidence: top.confidence,
|
|
295
|
+
axes: collected.axes,
|
|
296
|
+
signals: collected.signals,
|
|
297
|
+
metrics: toMetricRecord(collected.metrics),
|
|
298
|
+
gaps: meta.focusAreas.slice(0, 3),
|
|
299
|
+
focusAreas: meta.focusAreas,
|
|
300
|
+
scorecard,
|
|
301
|
+
generatedAt: new Date().toISOString()
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
getUpcomingMilestones(stage, completedMilestoneIds = [], limit = 5) {
|
|
305
|
+
if (!this.planner)
|
|
306
|
+
return [];
|
|
307
|
+
return this.planner.getUpcoming(stage, completedMilestoneIds, limit);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
var fallbackScore = () => ({
|
|
311
|
+
stage: LifecycleStage2.Exploration,
|
|
312
|
+
score: 0.3,
|
|
313
|
+
confidence: 0.3,
|
|
314
|
+
supportingSignals: []
|
|
315
|
+
});
|
|
316
|
+
var toMetricRecord = (snapshot) => Object.entries(snapshot ?? {}).reduce((acc, [key, value]) => {
|
|
317
|
+
if (typeof value === "number") {
|
|
318
|
+
acc[key] = value;
|
|
319
|
+
}
|
|
320
|
+
return acc;
|
|
321
|
+
}, {});
|
|
322
|
+
// src/data/milestones-catalog.json
|
|
323
|
+
var milestones_catalog_default = [
|
|
324
|
+
{
|
|
325
|
+
id: "stage0-problem-statement",
|
|
326
|
+
stage: 0,
|
|
327
|
+
category: "product",
|
|
328
|
+
title: "Write the pain statement",
|
|
329
|
+
description: "Capture the clearest description of the top problem in the customer’s own words.",
|
|
330
|
+
priority: 1,
|
|
331
|
+
actionItems: [
|
|
332
|
+
"Interview at least 5 ideal customers",
|
|
333
|
+
"Synthesize quotes into a one-page brief"
|
|
334
|
+
]
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
id: "stage1-prototype-loop",
|
|
338
|
+
stage: 1,
|
|
339
|
+
category: "product",
|
|
340
|
+
title: "Prototype feedback loop",
|
|
341
|
+
description: "Ship a clickable prototype and gather 3 rounds of feedback.",
|
|
342
|
+
priority: 2,
|
|
343
|
+
actionItems: [
|
|
344
|
+
"Create a low-fidelity prototype",
|
|
345
|
+
"Schedule standing feedback calls"
|
|
346
|
+
]
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: "stage2-activation",
|
|
350
|
+
stage: 2,
|
|
351
|
+
category: "operations",
|
|
352
|
+
title: "Activation checklist",
|
|
353
|
+
description: "Define the minimum steps required for a new user to succeed.",
|
|
354
|
+
priority: 1,
|
|
355
|
+
actionItems: [
|
|
356
|
+
"Document onboarding flow",
|
|
357
|
+
"Instrument activation analytics"
|
|
358
|
+
]
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
id: "stage3-retention-narrative",
|
|
362
|
+
stage: 3,
|
|
363
|
+
category: "product",
|
|
364
|
+
title: "Retention narrative",
|
|
365
|
+
description: "Create the before/after story that proves why users stay.",
|
|
366
|
+
priority: 2,
|
|
367
|
+
actionItems: [
|
|
368
|
+
"Interview 3 retained users",
|
|
369
|
+
"Publish a one-pager with concrete metrics"
|
|
370
|
+
]
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: "stage4-growth-loop",
|
|
374
|
+
stage: 4,
|
|
375
|
+
category: "growth",
|
|
376
|
+
title: "Install a growth loop",
|
|
377
|
+
description: "Stand up a repeatable acquisition → activation → referral motion.",
|
|
378
|
+
priority: 1,
|
|
379
|
+
actionItems: [
|
|
380
|
+
"Define loop metrics",
|
|
381
|
+
"Assign owners for each stage",
|
|
382
|
+
"Review weekly"
|
|
383
|
+
]
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
id: "stage5-platform-blueprint",
|
|
387
|
+
stage: 5,
|
|
388
|
+
category: "product",
|
|
389
|
+
title: "Platform blueprint",
|
|
390
|
+
description: "Align on APIs, integrations, and governance for partners.",
|
|
391
|
+
priority: 2,
|
|
392
|
+
actionItems: [
|
|
393
|
+
"Create integration scoring rubric",
|
|
394
|
+
"Publish partner onboarding checklist"
|
|
395
|
+
]
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: "stage6-renewal-ops",
|
|
399
|
+
stage: 6,
|
|
400
|
+
category: "operations",
|
|
401
|
+
title: "Renewal operating rhythm",
|
|
402
|
+
description: "Decide whether to optimize, reinvest, or sunset each major surface.",
|
|
403
|
+
priority: 1,
|
|
404
|
+
actionItems: [
|
|
405
|
+
"Hold quarterly renewal review",
|
|
406
|
+
"Document reinvestment bets"
|
|
407
|
+
]
|
|
408
|
+
}
|
|
409
|
+
];
|
|
410
|
+
|
|
411
|
+
// src/planning/milestone-planner.ts
|
|
412
|
+
class LifecycleMilestonePlanner {
|
|
413
|
+
milestones;
|
|
414
|
+
constructor(customCatalog) {
|
|
415
|
+
this.milestones = customCatalog ?? milestones_catalog_default;
|
|
416
|
+
}
|
|
417
|
+
getUpcoming(stage, completedIds = [], limit = 5) {
|
|
418
|
+
const completedSet = new Set(completedIds);
|
|
419
|
+
return this.milestones.filter((milestone) => milestone.stage === stage && !completedSet.has(milestone.id)).sort((a, b) => a.priority - b.priority).slice(0, limit);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
export {
|
|
423
|
+
StageSignalCollector,
|
|
424
|
+
StageScorer,
|
|
425
|
+
LifecycleOrchestrator,
|
|
426
|
+
LifecycleMilestonePlanner
|
|
427
|
+
};
|
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
milestonePlanner?: LifecycleMilestonePlanner;
|
|
1
|
+
import type { LifecycleAssessment, LifecycleAssessmentInput, LifecycleMilestone } from '@contractspec/lib.lifecycle';
|
|
2
|
+
import { LifecycleStage } from '@contractspec/lib.lifecycle';
|
|
3
|
+
import { StageSignalCollector } from '../collectors/signal-collector';
|
|
4
|
+
import { StageScorer } from '../scoring/stage-scorer';
|
|
5
|
+
import { LifecycleMilestonePlanner } from '../planning/milestone-planner';
|
|
6
|
+
export interface LifecycleOrchestratorOptions {
|
|
7
|
+
collector: StageSignalCollector;
|
|
8
|
+
scorer: StageScorer;
|
|
9
|
+
milestonePlanner?: LifecycleMilestonePlanner;
|
|
11
10
|
}
|
|
12
|
-
declare class LifecycleOrchestrator {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
export declare class LifecycleOrchestrator {
|
|
12
|
+
private readonly collector;
|
|
13
|
+
private readonly scorer;
|
|
14
|
+
private readonly planner?;
|
|
15
|
+
constructor(options: LifecycleOrchestratorOptions);
|
|
16
|
+
run(input?: LifecycleAssessmentInput): Promise<LifecycleAssessment>;
|
|
17
|
+
getUpcomingMilestones(stage: LifecycleStage, completedMilestoneIds?: string[], limit?: number): LifecycleMilestone[];
|
|
19
18
|
}
|
|
20
|
-
//#endregion
|
|
21
|
-
export { LifecycleOrchestrator, LifecycleOrchestratorOptions };
|
|
22
19
|
//# sourceMappingURL=lifecycle-orchestrator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-orchestrator.d.ts","
|
|
1
|
+
{"version":3,"file":"lifecycle-orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrator/lifecycle-orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAEnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEL,cAAc,EACf,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAE1E,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,oBAAoB,CAAC;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,gBAAgB,CAAC,EAAE,yBAAyB,CAAC;CAC9C;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAA4B;gBAEzC,OAAO,EAAE,4BAA4B;IAM3C,GAAG,CAAC,KAAK,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAsBzE,qBAAqB,CACnB,KAAK,EAAE,cAAc,EACrB,qBAAqB,GAAE,MAAM,EAAO,EACpC,KAAK,SAAI,GACR,kBAAkB,EAAE;CAIxB"}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { LifecycleMilestone, LifecycleStage } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
constructor(customCatalog?: LifecycleMilestone[]);
|
|
7
|
-
getUpcoming(stage: LifecycleStage, completedIds?: string[], limit?: number): LifecycleMilestone[];
|
|
1
|
+
import type { LifecycleMilestone, LifecycleStage } from '@contractspec/lib.lifecycle';
|
|
2
|
+
export declare class LifecycleMilestonePlanner {
|
|
3
|
+
private readonly milestones;
|
|
4
|
+
constructor(customCatalog?: LifecycleMilestone[]);
|
|
5
|
+
getUpcoming(stage: LifecycleStage, completedIds?: string[], limit?: number): LifecycleMilestone[];
|
|
8
6
|
}
|
|
9
|
-
//#endregion
|
|
10
|
-
export { LifecycleMilestonePlanner };
|
|
11
7
|
//# sourceMappingURL=milestone-planner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"milestone-planner.d.ts","
|
|
1
|
+
{"version":3,"file":"milestone-planner.d.ts","sourceRoot":"","sources":["../../src/planning/milestone-planner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EACf,MAAM,6BAA6B,CAAC;AAGrC,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;gBAEtC,aAAa,CAAC,EAAE,kBAAkB,EAAE;IAIhD,WAAW,CACT,KAAK,EAAE,cAAc,EACrB,YAAY,GAAE,MAAM,EAAO,EAC3B,KAAK,SAAI,GACR,kBAAkB,EAAE;CAUxB"}
|
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import { LifecycleMetricSnapshot, LifecycleScore, LifecycleSignal } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { LifecycleMetricSnapshot, LifecycleScore, LifecycleSignal } from '@contractspec/lib.lifecycle';
|
|
2
|
+
type NumericMetricKey = Extract<{
|
|
3
|
+
[Key in keyof LifecycleMetricSnapshot]: LifecycleMetricSnapshot[Key] extends number | undefined ? Key : never;
|
|
4
|
+
}[keyof LifecycleMetricSnapshot], string>;
|
|
5
5
|
interface MetricWeightConfig {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
weight: number;
|
|
7
|
+
threshold: number;
|
|
8
|
+
direction?: 'gte' | 'lte';
|
|
9
9
|
}
|
|
10
10
|
interface StageWeightConfig {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
base: number;
|
|
12
|
+
metrics?: Partial<Record<NumericMetricKey, MetricWeightConfig>>;
|
|
13
|
+
signalKinds?: Partial<Record<string, number>>;
|
|
14
14
|
}
|
|
15
15
|
type StageWeights = Record<string, StageWeightConfig>;
|
|
16
|
-
interface StageScoreInput {
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
export interface StageScoreInput {
|
|
17
|
+
metrics: LifecycleMetricSnapshot;
|
|
18
|
+
signals: LifecycleSignal[];
|
|
19
19
|
}
|
|
20
|
-
declare class StageScorer {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
export declare class StageScorer {
|
|
21
|
+
private readonly weights;
|
|
22
|
+
constructor(weights?: StageWeights);
|
|
23
|
+
score(input: StageScoreInput): LifecycleScore[];
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
export { StageScoreInput, StageScorer };
|
|
25
|
+
export {};
|
|
27
26
|
//# sourceMappingURL=stage-scorer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stage-scorer.d.ts","
|
|
1
|
+
{"version":3,"file":"stage-scorer.d.ts","sourceRoot":"","sources":["../../src/scoring/stage-scorer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,cAAc,EACd,eAAe,EAChB,MAAM,6BAA6B,CAAC;AAOrC,KAAK,gBAAgB,GAAG,OAAO,CAC7B;KACG,GAAG,IAAI,MAAM,uBAAuB,GAAG,uBAAuB,CAAC,GAAG,CAAC,SAChE,MAAM,GACN,SAAS,GACT,GAAG,GACH,KAAK;CACV,CAAC,MAAM,uBAAuB,CAAC,EAChC,MAAM,CACP,CAAC;AAEF,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CAC3B;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAChE,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC/C;AAED,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAItD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,uBAAuB,CAAC;IACjC,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;gBAE3B,OAAO,GAAE,YAA8B;IAInD,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,cAAc,EAAE;CA4DhD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/module.lifecycle-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.58.0",
|
|
4
4
|
"description": "Core lifecycle stage definitions and transitions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"contractspec",
|
|
@@ -20,34 +20,39 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
|
|
22
22
|
"publish:pkg:canary": "bun publish:pkg --tag canary",
|
|
23
|
-
"build": "bun build:
|
|
24
|
-
"build:bundle": "
|
|
25
|
-
"build:types": "
|
|
26
|
-
"dev": "bun
|
|
23
|
+
"build": "bun run prebuild && bun run build:bundle && bun run build:types",
|
|
24
|
+
"build:bundle": "contractspec-bun-build transpile",
|
|
25
|
+
"build:types": "contractspec-bun-build types",
|
|
26
|
+
"dev": "contractspec-bun-build dev",
|
|
27
27
|
"clean": "rimraf dist .turbo",
|
|
28
28
|
"lint": "bun lint:fix",
|
|
29
29
|
"lint:fix": "eslint src --fix",
|
|
30
30
|
"lint:check": "eslint src",
|
|
31
|
-
"test": "bun test"
|
|
31
|
+
"test": "bun test",
|
|
32
|
+
"prebuild": "contractspec-bun-build prebuild",
|
|
33
|
+
"typecheck": "tsc --noEmit"
|
|
32
34
|
},
|
|
33
35
|
"dependencies": {
|
|
34
|
-
"@contractspec/lib.lifecycle": "1.
|
|
36
|
+
"@contractspec/lib.lifecycle": "1.58.0"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
|
-
"@contractspec/tool.
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"typescript": "^5.9.3"
|
|
39
|
+
"@contractspec/tool.typescript": "1.58.0",
|
|
40
|
+
"typescript": "^5.9.3",
|
|
41
|
+
"@contractspec/tool.bun": "1.57.0"
|
|
41
42
|
},
|
|
42
43
|
"exports": {
|
|
43
|
-
".": "./
|
|
44
|
-
"./*": "./*"
|
|
44
|
+
".": "./src/index.ts"
|
|
45
45
|
},
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"access": "public",
|
|
48
48
|
"exports": {
|
|
49
|
-
".":
|
|
50
|
-
|
|
49
|
+
".": {
|
|
50
|
+
"types": "./dist/index.d.ts",
|
|
51
|
+
"bun": "./dist/index.js",
|
|
52
|
+
"node": "./dist/node/index.mjs",
|
|
53
|
+
"browser": "./dist/browser/index.js",
|
|
54
|
+
"default": "./dist/index.js"
|
|
55
|
+
}
|
|
51
56
|
},
|
|
52
57
|
"registry": "https://registry.npmjs.org/"
|
|
53
58
|
},
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { CapitalPhase, CompanyPhase, ProductPhase } from "@contractspec/lib.lifecycle";
|
|
2
|
-
|
|
3
|
-
//#region src/collectors/signal-collector.ts
|
|
4
|
-
const DEFAULT_AXES = {
|
|
5
|
-
product: ProductPhase.Sketch,
|
|
6
|
-
company: CompanyPhase.Solo,
|
|
7
|
-
capital: CapitalPhase.Bootstrapped
|
|
8
|
-
};
|
|
9
|
-
var StageSignalCollector = class {
|
|
10
|
-
options;
|
|
11
|
-
constructor(options) {
|
|
12
|
-
this.options = options;
|
|
13
|
-
}
|
|
14
|
-
async collect(input = {}) {
|
|
15
|
-
const axes = {
|
|
16
|
-
...DEFAULT_AXES,
|
|
17
|
-
...input.axes ?? {}
|
|
18
|
-
};
|
|
19
|
-
const metricsSnapshots = [];
|
|
20
|
-
const aggregatedSignals = [...input.signals ?? []];
|
|
21
|
-
const questionnaireAnswers = { ...input.questionnaireAnswers ?? {} };
|
|
22
|
-
if (input.metrics) metricsSnapshots.push(input.metrics);
|
|
23
|
-
if (this.options.analyticsAdapter) {
|
|
24
|
-
const result = await this.options.analyticsAdapter.fetch();
|
|
25
|
-
if (result.axes) Object.assign(axes, result.axes);
|
|
26
|
-
if (result.metrics) metricsSnapshots.push(result.metrics);
|
|
27
|
-
if (result.signals) aggregatedSignals.push(...result.signals);
|
|
28
|
-
}
|
|
29
|
-
if (this.options.questionnaireAdapter) {
|
|
30
|
-
const result = await this.options.questionnaireAdapter.fetch();
|
|
31
|
-
if (result.axes) Object.assign(axes, result.axes);
|
|
32
|
-
if (result.signals) aggregatedSignals.push(...result.signals);
|
|
33
|
-
Object.assign(questionnaireAnswers, result.answers);
|
|
34
|
-
}
|
|
35
|
-
if (this.options.intentAdapter) {
|
|
36
|
-
const result = await this.options.intentAdapter.fetch();
|
|
37
|
-
if (result.signals) aggregatedSignals.push(...result.signals);
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
axes,
|
|
41
|
-
metrics: mergeMetricSnapshots(metricsSnapshots),
|
|
42
|
-
signals: dedupeSignals(aggregatedSignals),
|
|
43
|
-
questionnaireAnswers: Object.keys(questionnaireAnswers).length ? questionnaireAnswers : void 0
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
const mergeMetricSnapshots = (snapshots) => snapshots.reduce((acc, snapshot) => {
|
|
48
|
-
Object.entries(snapshot ?? {}).forEach(([key, value]) => {
|
|
49
|
-
if (value !== void 0 && value !== null) acc[key] = value;
|
|
50
|
-
});
|
|
51
|
-
return acc;
|
|
52
|
-
}, {});
|
|
53
|
-
const dedupeSignals = (signals) => {
|
|
54
|
-
const seen = /* @__PURE__ */ new Set();
|
|
55
|
-
return signals.filter((signal) => {
|
|
56
|
-
if (!signal.id) return true;
|
|
57
|
-
if (seen.has(signal.id)) return false;
|
|
58
|
-
seen.add(signal.id);
|
|
59
|
-
return true;
|
|
60
|
-
});
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
//#endregion
|
|
64
|
-
export { StageSignalCollector };
|
|
65
|
-
//# sourceMappingURL=signal-collector.js.map
|