@lumenflow/metrics 1.0.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/LICENSE +190 -0
- package/README.md +161 -0
- package/dist/dora/calculate-dora-metrics.d.ts +34 -0
- package/dist/dora/calculate-dora-metrics.d.ts.map +1 -0
- package/dist/dora/calculate-dora-metrics.js +178 -0
- package/dist/dora/calculate-dora-metrics.js.map +1 -0
- package/dist/dora/constants.d.ts +61 -0
- package/dist/dora/constants.d.ts.map +1 -0
- package/dist/dora/constants.js +65 -0
- package/dist/dora/constants.js.map +1 -0
- package/dist/dora/index.d.ts +11 -0
- package/dist/dora/index.d.ts.map +1 -0
- package/dist/dora/index.js +11 -0
- package/dist/dora/index.js.map +1 -0
- package/dist/flow/analyze-bottlenecks.d.ts +51 -0
- package/dist/flow/analyze-bottlenecks.d.ts.map +1 -0
- package/dist/flow/analyze-bottlenecks.js +253 -0
- package/dist/flow/analyze-bottlenecks.js.map +1 -0
- package/dist/flow/calculate-flow-state.d.ts +13 -0
- package/dist/flow/calculate-flow-state.d.ts.map +1 -0
- package/dist/flow/calculate-flow-state.js +27 -0
- package/dist/flow/calculate-flow-state.js.map +1 -0
- package/dist/flow/capture-metrics-snapshot.d.ts +13 -0
- package/dist/flow/capture-metrics-snapshot.d.ts.map +1 -0
- package/dist/flow/capture-metrics-snapshot.js +126 -0
- package/dist/flow/capture-metrics-snapshot.js.map +1 -0
- package/dist/flow/generate-flow-report.d.ts +13 -0
- package/dist/flow/generate-flow-report.d.ts.map +1 -0
- package/dist/flow/generate-flow-report.js +254 -0
- package/dist/flow/generate-flow-report.js.map +1 -0
- package/dist/flow/index.d.ts +12 -0
- package/dist/flow/index.d.ts.map +1 -0
- package/dist/flow/index.js +12 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/telemetry/emit-telemetry.d.ts +31 -0
- package/dist/telemetry/emit-telemetry.d.ts.map +1 -0
- package/dist/telemetry/emit-telemetry.js +103 -0
- package/dist/telemetry/emit-telemetry.js.map +1 -0
- package/dist/telemetry/index.d.ts +9 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +9 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/types.d.ts +332 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DORA Metrics Constants
|
|
3
|
+
*
|
|
4
|
+
* Thresholds based on "Accelerate" research by Nicole Forsgren, Jez Humble, Gene Kim.
|
|
5
|
+
*
|
|
6
|
+
* @module @lumenflow/metrics/dora/constants
|
|
7
|
+
*/
|
|
8
|
+
/** Deployment frequency classification thresholds (deploys per week) */
|
|
9
|
+
export const DEPLOYMENT_FREQUENCY = {
|
|
10
|
+
/** Elite: >5 deploys per week */
|
|
11
|
+
ELITE: 5,
|
|
12
|
+
/** High: 1-5 deploys per week */
|
|
13
|
+
HIGH: 1,
|
|
14
|
+
/** Medium: ~1 deploy per month (0.25/week) */
|
|
15
|
+
MEDIUM: 0.25,
|
|
16
|
+
// Low: <0.25 deploys per week (implicit)
|
|
17
|
+
};
|
|
18
|
+
/** Lead time classification thresholds (hours) */
|
|
19
|
+
export const LEAD_TIME_HOURS = {
|
|
20
|
+
/** Elite: <24 hours (<1 day) */
|
|
21
|
+
ELITE: 24,
|
|
22
|
+
/** High: <168 hours (<7 days) */
|
|
23
|
+
HIGH: 168,
|
|
24
|
+
/** Medium: <720 hours (<30 days) */
|
|
25
|
+
MEDIUM: 720,
|
|
26
|
+
// Low: >720 hours (implicit)
|
|
27
|
+
};
|
|
28
|
+
/** Change failure rate classification thresholds (percentage) */
|
|
29
|
+
export const CFR_PERCENT = {
|
|
30
|
+
/** Elite: <15% failures */
|
|
31
|
+
ELITE: 15,
|
|
32
|
+
/** High: 15-30% failures */
|
|
33
|
+
HIGH: 30,
|
|
34
|
+
/** Medium: 30-45% failures */
|
|
35
|
+
MEDIUM: 45,
|
|
36
|
+
// Low: >45% failures (implicit)
|
|
37
|
+
};
|
|
38
|
+
/** Mean time to recovery classification thresholds (hours) */
|
|
39
|
+
export const MTTR_HOURS = {
|
|
40
|
+
/** Elite: <1 hour */
|
|
41
|
+
ELITE: 1,
|
|
42
|
+
/** High: <24 hours (<1 day) */
|
|
43
|
+
HIGH: 24,
|
|
44
|
+
/** Medium: <168 hours (<7 days) */
|
|
45
|
+
MEDIUM: 168,
|
|
46
|
+
// Low: >168 hours (implicit)
|
|
47
|
+
};
|
|
48
|
+
/** Statistical constants */
|
|
49
|
+
export const STATISTICS = {
|
|
50
|
+
/** 90th percentile for p90Hours calculation */
|
|
51
|
+
P90_PERCENTILE: 0.9,
|
|
52
|
+
/** Decimal places for rounding (10 = 1 decimal place) */
|
|
53
|
+
ROUNDING_FACTOR: 10,
|
|
54
|
+
/** 95th percentile for p95 calculations */
|
|
55
|
+
P95_PERCENTILE: 0.95,
|
|
56
|
+
/** 99th percentile for p99 calculations */
|
|
57
|
+
P99_PERCENTILE: 0.99,
|
|
58
|
+
/** Median percentile */
|
|
59
|
+
MEDIAN_PERCENTILE: 0.5,
|
|
60
|
+
/** Milliseconds per hour */
|
|
61
|
+
MS_PER_HOUR: 3600000,
|
|
62
|
+
/** Percentage multiplier */
|
|
63
|
+
PERCENTAGE_MULTIPLIER: 100,
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/dora/constants.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wEAAwE;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,iCAAiC;IACjC,KAAK,EAAE,CAAC;IACR,iCAAiC;IACjC,IAAI,EAAE,CAAC;IACP,8CAA8C;IAC9C,MAAM,EAAE,IAAI;IACZ,yCAAyC;CACjC,CAAC;AAEX,kDAAkD;AAClD,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,gCAAgC;IAChC,KAAK,EAAE,EAAE;IACT,iCAAiC;IACjC,IAAI,EAAE,GAAG;IACT,oCAAoC;IACpC,MAAM,EAAE,GAAG;IACX,6BAA6B;CACrB,CAAC;AAEX,iEAAiE;AACjE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,2BAA2B;IAC3B,KAAK,EAAE,EAAE;IACT,4BAA4B;IAC5B,IAAI,EAAE,EAAE;IACR,8BAA8B;IAC9B,MAAM,EAAE,EAAE;IACV,gCAAgC;CACxB,CAAC;AAEX,8DAA8D;AAC9D,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,qBAAqB;IACrB,KAAK,EAAE,CAAC;IACR,+BAA+B;IAC/B,IAAI,EAAE,EAAE;IACR,mCAAmC;IACnC,MAAM,EAAE,GAAG;IACX,6BAA6B;CACrB,CAAC;AAEX,4BAA4B;AAC5B,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,+CAA+C;IAC/C,cAAc,EAAE,GAAG;IACnB,yDAAyD;IACzD,eAAe,EAAE,EAAE;IACnB,2CAA2C;IAC3C,cAAc,EAAE,IAAI;IACpB,2CAA2C;IAC3C,cAAc,EAAE,IAAI;IACpB,wBAAwB;IACxB,iBAAiB,EAAE,GAAG;IACtB,4BAA4B;IAC5B,WAAW,EAAE,OAAO;IACpB,4BAA4B;IAC5B,qBAAqB,EAAE,GAAG;CAClB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DORA Metrics Module
|
|
3
|
+
*
|
|
4
|
+
* Calculate DORA metrics (Deployment Frequency, Lead Time, Change Failure Rate, MTTR)
|
|
5
|
+
* based on "Accelerate" research.
|
|
6
|
+
*
|
|
7
|
+
* @module @lumenflow/metrics/dora
|
|
8
|
+
*/
|
|
9
|
+
export { calculateDeploymentFrequency, calculateLeadTime, calculateCFR, calculateMTTR, calculateDORAMetrics, identifyEmergencyFixes, } from './calculate-dora-metrics.js';
|
|
10
|
+
export { DEPLOYMENT_FREQUENCY, LEAD_TIME_HOURS, CFR_PERCENT, MTTR_HOURS, STATISTICS, } from './constants.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dora/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,4BAA4B,EAC5B,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,UAAU,EACV,UAAU,GACX,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DORA Metrics Module
|
|
3
|
+
*
|
|
4
|
+
* Calculate DORA metrics (Deployment Frequency, Lead Time, Change Failure Rate, MTTR)
|
|
5
|
+
* based on "Accelerate" research.
|
|
6
|
+
*
|
|
7
|
+
* @module @lumenflow/metrics/dora
|
|
8
|
+
*/
|
|
9
|
+
export { calculateDeploymentFrequency, calculateLeadTime, calculateCFR, calculateMTTR, calculateDORAMetrics, identifyEmergencyFixes, } from './calculate-dora-metrics.js';
|
|
10
|
+
export { DEPLOYMENT_FREQUENCY, LEAD_TIME_HOURS, CFR_PERCENT, MTTR_HOURS, STATISTICS, } from './constants.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dora/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,4BAA4B,EAC5B,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,UAAU,EACV,UAAU,GACX,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bottleneck Analysis Module
|
|
3
|
+
*
|
|
4
|
+
* Provides dependency graph analysis for identifying bottlenecks and critical paths.
|
|
5
|
+
* Includes topological sort, impact scoring, and critical path algorithms.
|
|
6
|
+
*
|
|
7
|
+
* @module @lumenflow/metrics/flow
|
|
8
|
+
*/
|
|
9
|
+
import type { DependencyGraphNode, BottleneckResult, CriticalPathResult, BottleneckAnalysis } from '../types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Dependency graph type - Map of WU ID to node data
|
|
12
|
+
*/
|
|
13
|
+
export type DependencyGraph = Map<string, DependencyGraphNode>;
|
|
14
|
+
/**
|
|
15
|
+
* Result from topological sort
|
|
16
|
+
*/
|
|
17
|
+
export interface TopologicalSortResult {
|
|
18
|
+
order: string[];
|
|
19
|
+
hasCycle: boolean;
|
|
20
|
+
warning?: string;
|
|
21
|
+
cycleNodes?: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Perform topological sort on non-done WUs using Kahn's algorithm.
|
|
25
|
+
* Returns valid execution ordering where dependencies come before dependents.
|
|
26
|
+
* Handles cycles gracefully by returning partial ordering with warning.
|
|
27
|
+
*/
|
|
28
|
+
export declare function topologicalSort(graph: DependencyGraph): TopologicalSortResult;
|
|
29
|
+
/**
|
|
30
|
+
* Find the critical path (longest dependency chain) in the graph.
|
|
31
|
+
* Uses dynamic programming on the DAG to find the longest path.
|
|
32
|
+
* Excludes done WUs from the path.
|
|
33
|
+
*/
|
|
34
|
+
export declare function criticalPath(graph: DependencyGraph): CriticalPathResult;
|
|
35
|
+
/**
|
|
36
|
+
* Calculate the impact score for a WU.
|
|
37
|
+
* Impact score is the count of all downstream dependents (recursive).
|
|
38
|
+
* Excludes done WUs from the count.
|
|
39
|
+
*/
|
|
40
|
+
export declare function impactScore(graph: DependencyGraph, wuId: string): number;
|
|
41
|
+
/**
|
|
42
|
+
* Find the top N bottleneck WUs by impact score.
|
|
43
|
+
* A bottleneck is a WU that blocks many other WUs.
|
|
44
|
+
* Excludes done WUs from results.
|
|
45
|
+
*/
|
|
46
|
+
export declare function analyzeBottlenecks(graph: DependencyGraph, limit: number): BottleneckResult[];
|
|
47
|
+
/**
|
|
48
|
+
* Get complete bottleneck analysis including critical path
|
|
49
|
+
*/
|
|
50
|
+
export declare function getBottleneckAnalysis(graph: DependencyGraph, limit?: number): BottleneckAnalysis;
|
|
51
|
+
//# sourceMappingURL=analyze-bottlenecks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-bottlenecks.d.ts","sourceRoot":"","sources":["../../src/flow/analyze-bottlenecks.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAKrB;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAyGD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,qBAAqB,CAqB7E;AAsDD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,kBAAkB,CAmCvE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CA2BxE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAoB5F;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,EACtB,KAAK,GAAE,MAAW,GACjB,kBAAkB,CAKpB"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bottleneck Analysis Module
|
|
3
|
+
*
|
|
4
|
+
* Provides dependency graph analysis for identifying bottlenecks and critical paths.
|
|
5
|
+
* Includes topological sort, impact scoring, and critical path algorithms.
|
|
6
|
+
*
|
|
7
|
+
* @module @lumenflow/metrics/flow
|
|
8
|
+
*/
|
|
9
|
+
/** Default WU status for done items */
|
|
10
|
+
const WU_STATUS_DONE = 'done';
|
|
11
|
+
/**
|
|
12
|
+
* Filter graph to active (non-done) WUs only.
|
|
13
|
+
*/
|
|
14
|
+
function filterActiveGraph(graph) {
|
|
15
|
+
const activeGraph = new Map();
|
|
16
|
+
for (const [id, node] of graph.entries()) {
|
|
17
|
+
if (node.status !== WU_STATUS_DONE) {
|
|
18
|
+
activeGraph.set(id, node);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return activeGraph;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build in-degree map for topological sort
|
|
25
|
+
*/
|
|
26
|
+
function buildInDegreeMap(activeGraph) {
|
|
27
|
+
const inDegree = new Map();
|
|
28
|
+
for (const [id, node] of activeGraph.entries()) {
|
|
29
|
+
let count = 0;
|
|
30
|
+
for (const depId of node.blockedBy) {
|
|
31
|
+
if (activeGraph.has(depId)) {
|
|
32
|
+
count++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
inDegree.set(id, count);
|
|
36
|
+
}
|
|
37
|
+
return inDegree;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Find nodes with zero in-degree (no active dependencies)
|
|
41
|
+
*/
|
|
42
|
+
function findStartingNodes(inDegree) {
|
|
43
|
+
const queue = [];
|
|
44
|
+
for (const [id, degree] of inDegree.entries()) {
|
|
45
|
+
if (degree === 0) {
|
|
46
|
+
queue.push(id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return queue;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Process a single node in topological sort
|
|
53
|
+
*/
|
|
54
|
+
function processNode(current, activeGraph, inDegree, workQueue) {
|
|
55
|
+
const node = activeGraph.get(current);
|
|
56
|
+
if (!node)
|
|
57
|
+
return;
|
|
58
|
+
for (const depId of node.blocks) {
|
|
59
|
+
if (!activeGraph.has(depId))
|
|
60
|
+
continue;
|
|
61
|
+
const currentDegree = inDegree.get(depId) ?? 0;
|
|
62
|
+
const newDegree = currentDegree - 1;
|
|
63
|
+
inDegree.set(depId, newDegree);
|
|
64
|
+
if (newDegree === 0) {
|
|
65
|
+
workQueue.push(depId);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Find nodes involved in cycles
|
|
71
|
+
*/
|
|
72
|
+
function findCycleNodes(inDegree) {
|
|
73
|
+
const cycleNodes = [];
|
|
74
|
+
for (const [id, degree] of inDegree.entries()) {
|
|
75
|
+
if (degree > 0) {
|
|
76
|
+
cycleNodes.push(id);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return cycleNodes;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Process topological sort using Kahn's algorithm
|
|
83
|
+
*/
|
|
84
|
+
function processTopologicalSort(activeGraph, inDegree, queue) {
|
|
85
|
+
const sorted = [];
|
|
86
|
+
const workQueue = [...queue];
|
|
87
|
+
while (workQueue.length > 0) {
|
|
88
|
+
const current = workQueue.shift();
|
|
89
|
+
if (current) {
|
|
90
|
+
sorted.push(current);
|
|
91
|
+
processNode(current, activeGraph, inDegree, workQueue);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { sorted, cycleNodes: findCycleNodes(inDegree) };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Perform topological sort on non-done WUs using Kahn's algorithm.
|
|
98
|
+
* Returns valid execution ordering where dependencies come before dependents.
|
|
99
|
+
* Handles cycles gracefully by returning partial ordering with warning.
|
|
100
|
+
*/
|
|
101
|
+
export function topologicalSort(graph) {
|
|
102
|
+
const activeGraph = filterActiveGraph(graph);
|
|
103
|
+
if (activeGraph.size === 0) {
|
|
104
|
+
return { order: [], hasCycle: false };
|
|
105
|
+
}
|
|
106
|
+
const inDegree = buildInDegreeMap(activeGraph);
|
|
107
|
+
const queue = findStartingNodes(inDegree);
|
|
108
|
+
const { sorted, cycleNodes } = processTopologicalSort(activeGraph, inDegree, queue);
|
|
109
|
+
if (sorted.length < activeGraph.size) {
|
|
110
|
+
return {
|
|
111
|
+
order: sorted,
|
|
112
|
+
hasCycle: true,
|
|
113
|
+
warning: 'Cycle detected: some WUs have circular dependencies',
|
|
114
|
+
cycleNodes,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return { order: sorted, hasCycle: false };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Calculate distances for critical path using dynamic programming
|
|
121
|
+
*/
|
|
122
|
+
function calculateDistances(activeGraph, topoOrder) {
|
|
123
|
+
const distance = new Map();
|
|
124
|
+
const predecessor = new Map();
|
|
125
|
+
for (const id of topoOrder) {
|
|
126
|
+
distance.set(id, 1);
|
|
127
|
+
predecessor.set(id, null);
|
|
128
|
+
}
|
|
129
|
+
for (const current of topoOrder) {
|
|
130
|
+
const node = activeGraph.get(current);
|
|
131
|
+
if (!node)
|
|
132
|
+
continue;
|
|
133
|
+
for (const depId of node.blocks) {
|
|
134
|
+
if (!activeGraph.has(depId))
|
|
135
|
+
continue;
|
|
136
|
+
const currentDist = distance.get(current) ?? 0;
|
|
137
|
+
const newDistance = currentDist + 1;
|
|
138
|
+
const existingDist = distance.get(depId) ?? 0;
|
|
139
|
+
if (newDistance > existingDist) {
|
|
140
|
+
distance.set(depId, newDistance);
|
|
141
|
+
predecessor.set(depId, current);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { distance, predecessor };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Reconstruct path from predecessor map
|
|
149
|
+
*/
|
|
150
|
+
function reconstructPath(predecessor, endNode) {
|
|
151
|
+
const path = [];
|
|
152
|
+
let current = endNode;
|
|
153
|
+
while (current !== null) {
|
|
154
|
+
path.unshift(current);
|
|
155
|
+
current = predecessor.get(current) ?? null;
|
|
156
|
+
}
|
|
157
|
+
return path;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Find the critical path (longest dependency chain) in the graph.
|
|
161
|
+
* Uses dynamic programming on the DAG to find the longest path.
|
|
162
|
+
* Excludes done WUs from the path.
|
|
163
|
+
*/
|
|
164
|
+
export function criticalPath(graph) {
|
|
165
|
+
const activeGraph = filterActiveGraph(graph);
|
|
166
|
+
if (activeGraph.size === 0) {
|
|
167
|
+
return { path: [], length: 0 };
|
|
168
|
+
}
|
|
169
|
+
const topoResult = topologicalSort(graph);
|
|
170
|
+
if (topoResult.hasCycle) {
|
|
171
|
+
return {
|
|
172
|
+
path: [],
|
|
173
|
+
length: 0,
|
|
174
|
+
warning: topoResult.warning,
|
|
175
|
+
cycleNodes: topoResult.cycleNodes,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const { distance, predecessor } = calculateDistances(activeGraph, topoResult.order);
|
|
179
|
+
let maxDistance = 0;
|
|
180
|
+
let endNode = null;
|
|
181
|
+
for (const [id, dist] of distance.entries()) {
|
|
182
|
+
if (dist > maxDistance) {
|
|
183
|
+
maxDistance = dist;
|
|
184
|
+
endNode = id;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const path = reconstructPath(predecessor, endNode);
|
|
188
|
+
return {
|
|
189
|
+
path,
|
|
190
|
+
length: path.length,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Calculate the impact score for a WU.
|
|
195
|
+
* Impact score is the count of all downstream dependents (recursive).
|
|
196
|
+
* Excludes done WUs from the count.
|
|
197
|
+
*/
|
|
198
|
+
export function impactScore(graph, wuId) {
|
|
199
|
+
const activeGraph = filterActiveGraph(graph);
|
|
200
|
+
if (!activeGraph.has(wuId)) {
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
const visited = new Set();
|
|
204
|
+
const queue = [wuId];
|
|
205
|
+
visited.add(wuId);
|
|
206
|
+
while (queue.length > 0) {
|
|
207
|
+
const current = queue.shift();
|
|
208
|
+
if (!current)
|
|
209
|
+
continue;
|
|
210
|
+
const node = activeGraph.get(current);
|
|
211
|
+
if (!node)
|
|
212
|
+
continue;
|
|
213
|
+
for (const depId of node.blocks) {
|
|
214
|
+
if (!visited.has(depId) && activeGraph.has(depId)) {
|
|
215
|
+
visited.add(depId);
|
|
216
|
+
queue.push(depId);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return visited.size - 1;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Find the top N bottleneck WUs by impact score.
|
|
224
|
+
* A bottleneck is a WU that blocks many other WUs.
|
|
225
|
+
* Excludes done WUs from results.
|
|
226
|
+
*/
|
|
227
|
+
export function analyzeBottlenecks(graph, limit) {
|
|
228
|
+
const activeGraph = filterActiveGraph(graph);
|
|
229
|
+
if (activeGraph.size === 0) {
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
const scores = [];
|
|
233
|
+
for (const [id, node] of activeGraph.entries()) {
|
|
234
|
+
const score = impactScore(graph, id);
|
|
235
|
+
scores.push({
|
|
236
|
+
id,
|
|
237
|
+
score,
|
|
238
|
+
title: node.title,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
scores.sort((a, b) => b.score - a.score);
|
|
242
|
+
return scores.slice(0, limit);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get complete bottleneck analysis including critical path
|
|
246
|
+
*/
|
|
247
|
+
export function getBottleneckAnalysis(graph, limit = 10) {
|
|
248
|
+
return {
|
|
249
|
+
bottlenecks: analyzeBottlenecks(graph, limit),
|
|
250
|
+
criticalPath: criticalPath(graph),
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=analyze-bottlenecks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-bottlenecks.js","sourceRoot":"","sources":["../../src/flow/analyze-bottlenecks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculate WU flow state aggregation
|
|
3
|
+
*
|
|
4
|
+
* Application layer: Business logic for aggregating WU flow states
|
|
5
|
+
*
|
|
6
|
+
* @module @lumenflow/metrics/flow
|
|
7
|
+
*/
|
|
8
|
+
import type { WUMetrics, FlowState } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Calculate flow state from WU metrics
|
|
11
|
+
*/
|
|
12
|
+
export declare function calculateFlowState(wuMetrics: WUMetrics[]): FlowState;
|
|
13
|
+
//# sourceMappingURL=calculate-flow-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculate-flow-state.d.ts","sourceRoot":"","sources":["../../src/flow/calculate-flow-state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,SAAS,CAiBpE"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculate WU flow state aggregation
|
|
3
|
+
*
|
|
4
|
+
* Application layer: Business logic for aggregating WU flow states
|
|
5
|
+
*
|
|
6
|
+
* @module @lumenflow/metrics/flow
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Calculate flow state from WU metrics
|
|
10
|
+
*/
|
|
11
|
+
export function calculateFlowState(wuMetrics) {
|
|
12
|
+
const ready = wuMetrics.filter((wu) => wu.status === 'ready').length;
|
|
13
|
+
const inProgress = wuMetrics.filter((wu) => wu.status === 'in_progress').length;
|
|
14
|
+
const blocked = wuMetrics.filter((wu) => wu.status === 'blocked').length;
|
|
15
|
+
const waiting = wuMetrics.filter((wu) => wu.status === 'waiting').length;
|
|
16
|
+
const done = wuMetrics.filter((wu) => wu.status === 'done').length;
|
|
17
|
+
const totalActive = ready + inProgress + blocked + waiting;
|
|
18
|
+
return {
|
|
19
|
+
ready,
|
|
20
|
+
inProgress,
|
|
21
|
+
blocked,
|
|
22
|
+
waiting,
|
|
23
|
+
done,
|
|
24
|
+
totalActive,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=calculate-flow-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculate-flow-state.js","sourceRoot":"","sources":["../../src/flow/calculate-flow-state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAsB;IACvD,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACrE,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,MAAM,CAAC;IAChF,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAEnE,MAAM,WAAW,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;IAE3D,OAAO;QACL,KAAK;QACL,UAAU;QACV,OAAO;QACP,OAAO;QACP,IAAI;QACJ,WAAW;KACZ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Snapshot Capture
|
|
3
|
+
*
|
|
4
|
+
* Captures DORA metrics, lane health, and flow state snapshots.
|
|
5
|
+
*
|
|
6
|
+
* @module @lumenflow/metrics/flow
|
|
7
|
+
*/
|
|
8
|
+
import type { MetricsSnapshot, MetricsSnapshotInput } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Capture metrics snapshot based on type
|
|
11
|
+
*/
|
|
12
|
+
export declare function captureMetricsSnapshot(input: MetricsSnapshotInput): MetricsSnapshot;
|
|
13
|
+
//# sourceMappingURL=capture-metrics-snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-metrics-snapshot.d.ts","sourceRoot":"","sources":["../../src/flow/capture-metrics-snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAyB,MAAM,aAAa,CAAC;AAkIhG;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,GAAG,eAAe,CAkBnF"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Snapshot Capture
|
|
3
|
+
*
|
|
4
|
+
* Captures DORA metrics, lane health, and flow state snapshots.
|
|
5
|
+
*
|
|
6
|
+
* @module @lumenflow/metrics/flow
|
|
7
|
+
*/
|
|
8
|
+
import { calculateDORAMetrics } from '../dora/calculate-dora-metrics.js';
|
|
9
|
+
import { calculateFlowState } from './calculate-flow-state.js';
|
|
10
|
+
import { STATISTICS } from '../dora/constants.js';
|
|
11
|
+
/**
|
|
12
|
+
* Determine lane health status based on blocked ratio
|
|
13
|
+
*/
|
|
14
|
+
function determineLaneStatus(blocked, inProgress) {
|
|
15
|
+
if (blocked === 0)
|
|
16
|
+
return 'healthy';
|
|
17
|
+
if (inProgress > 0 && blocked <= inProgress)
|
|
18
|
+
return 'at-risk';
|
|
19
|
+
return 'blocked';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create empty lane accumulator
|
|
23
|
+
*/
|
|
24
|
+
function createLaneAccumulator(lane) {
|
|
25
|
+
return {
|
|
26
|
+
lane,
|
|
27
|
+
wusCompleted: 0,
|
|
28
|
+
wusInProgress: 0,
|
|
29
|
+
wusBlocked: 0,
|
|
30
|
+
cycleTimes: [],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Update lane accumulator with WU data
|
|
35
|
+
*/
|
|
36
|
+
function updateLaneAccumulator(acc, wu) {
|
|
37
|
+
switch (wu.status) {
|
|
38
|
+
case 'in_progress':
|
|
39
|
+
acc.wusInProgress++;
|
|
40
|
+
break;
|
|
41
|
+
case 'blocked':
|
|
42
|
+
acc.wusBlocked++;
|
|
43
|
+
break;
|
|
44
|
+
case 'done':
|
|
45
|
+
acc.wusCompleted++;
|
|
46
|
+
if (typeof wu.cycleTimeHours === 'number') {
|
|
47
|
+
acc.cycleTimes.push(wu.cycleTimeHours);
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Calculate median from sorted array
|
|
54
|
+
*/
|
|
55
|
+
function medianFromSorted(sorted) {
|
|
56
|
+
if (sorted.length === 0)
|
|
57
|
+
return 0;
|
|
58
|
+
const midIndex = Math.floor(sorted.length / 2);
|
|
59
|
+
// Use .at() to avoid object injection warning - midIndex is always valid
|
|
60
|
+
return sorted.at(midIndex) ?? 0;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Convert lane accumulator to health metrics
|
|
64
|
+
*/
|
|
65
|
+
function accumulatorToHealth(acc) {
|
|
66
|
+
const avgCycleTime = acc.cycleTimes.length > 0
|
|
67
|
+
? acc.cycleTimes.reduce((sum, t) => sum + t, 0) / acc.cycleTimes.length
|
|
68
|
+
: 0;
|
|
69
|
+
const sortedTimes = [...acc.cycleTimes].sort((a, b) => a - b);
|
|
70
|
+
const medianCycleTime = medianFromSorted(sortedTimes);
|
|
71
|
+
const status = determineLaneStatus(acc.wusBlocked, acc.wusInProgress);
|
|
72
|
+
return {
|
|
73
|
+
lane: acc.lane,
|
|
74
|
+
wusCompleted: acc.wusCompleted,
|
|
75
|
+
wusInProgress: acc.wusInProgress,
|
|
76
|
+
wusBlocked: acc.wusBlocked,
|
|
77
|
+
averageCycleTimeHours: Math.round(avgCycleTime * STATISTICS.ROUNDING_FACTOR) / STATISTICS.ROUNDING_FACTOR,
|
|
78
|
+
medianCycleTimeHours: Math.round(medianCycleTime * STATISTICS.ROUNDING_FACTOR) / STATISTICS.ROUNDING_FACTOR,
|
|
79
|
+
status,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Calculate lane-level metrics
|
|
84
|
+
*/
|
|
85
|
+
function calculateLaneMetrics(wuMetrics) {
|
|
86
|
+
const laneMap = new Map();
|
|
87
|
+
for (const wu of wuMetrics) {
|
|
88
|
+
if (!laneMap.has(wu.lane)) {
|
|
89
|
+
laneMap.set(wu.lane, createLaneAccumulator(wu.lane));
|
|
90
|
+
}
|
|
91
|
+
const laneData = laneMap.get(wu.lane);
|
|
92
|
+
if (laneData) {
|
|
93
|
+
updateLaneAccumulator(laneData, wu);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const laneMetrics = Array.from(laneMap.values()).map(accumulatorToHealth);
|
|
97
|
+
laneMetrics.sort((a, b) => a.lane.localeCompare(b.lane));
|
|
98
|
+
const activeStatuses = ['ready', 'in_progress', 'blocked', 'waiting'];
|
|
99
|
+
const totalActive = wuMetrics.filter((wu) => activeStatuses.includes(wu.status)).length;
|
|
100
|
+
const totalBlocked = wuMetrics.filter((wu) => wu.status === 'blocked').length;
|
|
101
|
+
const totalCompleted = wuMetrics.filter((wu) => wu.status === 'done').length;
|
|
102
|
+
return {
|
|
103
|
+
lanes: laneMetrics,
|
|
104
|
+
totalActive,
|
|
105
|
+
totalBlocked,
|
|
106
|
+
totalCompleted,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Capture metrics snapshot based on type
|
|
111
|
+
*/
|
|
112
|
+
export function captureMetricsSnapshot(input) {
|
|
113
|
+
const { commits, wuMetrics, skipGatesEntries, weekStart, weekEnd, type } = input;
|
|
114
|
+
const snapshot = {};
|
|
115
|
+
if (type === 'all' || type === 'dora') {
|
|
116
|
+
snapshot.dora = calculateDORAMetrics(commits, skipGatesEntries, wuMetrics, weekStart, weekEnd);
|
|
117
|
+
}
|
|
118
|
+
if (type === 'all' || type === 'lanes') {
|
|
119
|
+
snapshot.lanes = calculateLaneMetrics(wuMetrics);
|
|
120
|
+
}
|
|
121
|
+
if (type === 'all' || type === 'flow') {
|
|
122
|
+
snapshot.flow = calculateFlowState(wuMetrics);
|
|
123
|
+
}
|
|
124
|
+
return snapshot;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=capture-metrics-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-metrics-snapshot.js","sourceRoot":"","sources":["../../src/flow/capture-metrics-snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow Report Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates DORA/SPACE metrics flow reports from telemetry and WU data.
|
|
5
|
+
*
|
|
6
|
+
* @module @lumenflow/metrics/flow
|
|
7
|
+
*/
|
|
8
|
+
import type { FlowReportData, FlowReportInput } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Generate flow report from input data
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateFlowReport(input: FlowReportInput): FlowReportData;
|
|
13
|
+
//# sourceMappingURL=generate-flow-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-flow-report.d.ts","sourceRoot":"","sources":["../../src/flow/generate-flow-report.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAKhB,MAAM,aAAa,CAAC;AAmSrB;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,cAAc,CA6BzE"}
|