fix(iace): max 5 mitigations per hazard — clean per-hazard assignment

Replaced category-broadcast logic with per-hazard loop:
each hazard gets up to 5 measures (pattern-suggested first, then
category fallback). Expected: 108 × 5 = max 540 total.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-14 23:45:41 +02:00
parent dc55253b9d
commit 708c61e50d
@@ -253,67 +253,66 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) {
}
created := 0
const maxMitigationsPerHazard = 5
// Assign suggested measures to ALL hazards in matching category
// Build a flat list of all hazard IDs for iteration
var allHazardIDs []uuid.UUID
hazardCatByID := make(map[uuid.UUID]string)
for cat, ids := range hazardIDsByCategory {
for _, id := range ids {
allHazardIDs = append(allHazardIDs, id)
hazardCatByID[id] = cat
}
}
// For each hazard: assign up to maxMitigationsPerHazard measures
// Priority: pattern-suggested first, then category fallback
suggestedByMeasCat := make(map[string][]iace.ProtectiveMeasureEntry)
for _, sm := range matchOutput.SuggestedMeasures {
entry, ok := measureByID[sm.MeasureID]
if !ok {
continue
if entry, ok := measureByID[sm.MeasureID]; ok {
suggestedByMeasCat[entry.HazardCategory] = append(suggestedByMeasCat[entry.HazardCategory], entry)
}
hazardIDs := findHazardsForMeasureByCategory(entry.HazardCategory, hazardIDsByCategory)
rt := iace.ReductionType(entry.ReductionType)
if rt == "" {
rt = iace.ReductionTypeInformation
}
for _, hazID := range hazardIDs {
}
for _, hazID := range allHazardIDs {
hazCat := hazardCatByID[hazID]
measCat := patternCatToMeasureCat(hazCat)
added := 0
// First: pattern-suggested measures for this category
for _, entry := range suggestedByMeasCat[measCat] {
if added >= maxMitigationsPerHazard {
break
}
rt := iace.ReductionType(entry.ReductionType)
if rt == "" {
rt = iace.ReductionTypeInformation
}
_, cerr := h.store.CreateMitigation(ctx, iace.CreateMitigationRequest{
HazardID: hazID, ReductionType: rt,
Name: entry.Name, Description: entry.Description,
})
if cerr == nil {
created++
added++
}
}
}
// Fallback: assign category-based measures only to hazards that
// didn't get any measures from pattern suggestions (max 3 per hazard)
hazardsWithMeasures := make(map[uuid.UUID]bool)
for _, sm := range matchOutput.SuggestedMeasures {
entry, ok := measureByID[sm.MeasureID]
if !ok {
continue
}
for _, ids := range hazardIDsByCategory {
for _, id := range ids {
_ = entry // suppress unused
hazardsWithMeasures[id] = true
// Then: category fallback if still under limit
for _, m := range measuresByCat[measCat] {
if added >= maxMitigationsPerHazard {
break
}
}
}
for hazCat, hazIDs := range hazardIDsByCategory {
measCat := patternCatToMeasureCat(hazCat)
measures := measuresByCat[measCat]
for _, hazID := range hazIDs {
if hazardsWithMeasures[hazID] {
continue // Already has pattern-suggested measures
rt := iace.ReductionType(m.ReductionType)
if rt == "" {
rt = iace.ReductionTypeInformation
}
added := 0
for _, m := range measures {
if added >= 3 {
break
}
rt := iace.ReductionType(m.ReductionType)
if rt == "" {
rt = iace.ReductionTypeInformation
}
_, cerr := h.store.CreateMitigation(ctx, iace.CreateMitigationRequest{
HazardID: hazID, ReductionType: rt,
Name: m.Name, Description: m.Description,
})
if cerr == nil {
created++
}
_, cerr := h.store.CreateMitigation(ctx, iace.CreateMitigationRequest{
HazardID: hazID, ReductionType: rt,
Name: m.Name, Description: m.Description,
})
if cerr == nil {
created++
added++
}
}