gantt
This commit is contained in:
68
src/lib/macroTrafficLight.ts
Normal file
68
src/lib/macroTrafficLight.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import type { StoryGroup } from '../types/jira'
|
||||
import type { FunctionalGapBadge } from './dashboardConfig'
|
||||
import { detectWorkLane, type LaneLabelsConfig } from './laneDetection'
|
||||
import type { StatusBucketConfig } from './statusBuckets'
|
||||
import { isIssueCanceled, isIssueDone } from './statusBuckets'
|
||||
import { storyMatchesGapBadge } from './functionalGaps'
|
||||
import { stepperStates } from './storyMetrics'
|
||||
|
||||
export type MacroTrafficLight = 'green' | 'amber' | 'red'
|
||||
|
||||
export type MacroPipelineHealth = {
|
||||
light: MacroTrafficLight
|
||||
title: string
|
||||
detail: string
|
||||
violatingStoryKeys: string[]
|
||||
hasCriticalViolation: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Feux macro (DSI) : rouge / orange si l’intégration (piste I) est encore ouverte
|
||||
* alors que l’étape Design du stepper n’est pas validée à 100 %.
|
||||
*/
|
||||
export function computeMacroPipelineHealth(
|
||||
groups: StoryGroup[],
|
||||
bucketCfg: StatusBucketConfig,
|
||||
laneCfg: LaneLabelsConfig,
|
||||
gapBadges: FunctionalGapBadge[] | undefined,
|
||||
): MacroPipelineHealth {
|
||||
const violating: { key: string; critical: boolean }[] = []
|
||||
const badges = gapBadges ?? []
|
||||
|
||||
for (const g of groups) {
|
||||
const { story, subtasks } = g
|
||||
if (subtasks.length === 0) continue
|
||||
const steps = stepperStates(subtasks, bucketCfg, laneCfg)
|
||||
const designValidated = steps.design
|
||||
const integrationStillOpen = subtasks.some((st) => {
|
||||
if (isIssueDone(st, bucketCfg) || isIssueCanceled(st, bucketCfg)) return false
|
||||
return detectWorkLane(st, laneCfg) === 'integration'
|
||||
})
|
||||
if (integrationStillOpen && !designValidated) {
|
||||
const critical = badges.some((b) => b.criticalFlow && storyMatchesGapBadge(g, b))
|
||||
violating.push({ key: story.key, critical })
|
||||
}
|
||||
}
|
||||
|
||||
if (violating.length === 0) {
|
||||
return {
|
||||
light: 'green',
|
||||
title: 'Séquence A → D → I',
|
||||
detail:
|
||||
'Aucun conflit détecté : pas d’intégration active tant que le design n’est pas validé sur une même story.',
|
||||
violatingStoryKeys: [],
|
||||
hasCriticalViolation: false,
|
||||
}
|
||||
}
|
||||
|
||||
const hasCriticalViolation = violating.some((v) => v.critical)
|
||||
const light: MacroTrafficLight = hasCriticalViolation ? 'red' : 'amber'
|
||||
|
||||
return {
|
||||
light,
|
||||
title: hasCriticalViolation ? 'Alerte — flux critique' : 'Attention — séquence',
|
||||
detail: `${violating.length} story(s) avec intégration ouverte sans validation design complète.`,
|
||||
violatingStoryKeys: violating.map((v) => v.key),
|
||||
hasCriticalViolation,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user