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 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 { for _, sm := range matchOutput.SuggestedMeasures {
entry, ok := measureByID[sm.MeasureID] if entry, ok := measureByID[sm.MeasureID]; ok {
if !ok { suggestedByMeasCat[entry.HazardCategory] = append(suggestedByMeasCat[entry.HazardCategory], entry)
continue
} }
hazardIDs := findHazardsForMeasureByCategory(entry.HazardCategory, hazardIDsByCategory) }
rt := iace.ReductionType(entry.ReductionType)
if rt == "" { for _, hazID := range allHazardIDs {
rt = iace.ReductionTypeInformation hazCat := hazardCatByID[hazID]
} measCat := patternCatToMeasureCat(hazCat)
for _, hazID := range hazardIDs { 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{ _, cerr := h.store.CreateMitigation(ctx, iace.CreateMitigationRequest{
HazardID: hazID, ReductionType: rt, HazardID: hazID, ReductionType: rt,
Name: entry.Name, Description: entry.Description, Name: entry.Name, Description: entry.Description,
}) })
if cerr == nil { if cerr == nil {
created++ created++
added++
} }
} }
}
// Fallback: assign category-based measures only to hazards that // Then: category fallback if still under limit
// didn't get any measures from pattern suggestions (max 3 per hazard) for _, m := range measuresByCat[measCat] {
hazardsWithMeasures := make(map[uuid.UUID]bool) if added >= maxMitigationsPerHazard {
for _, sm := range matchOutput.SuggestedMeasures { break
entry, ok := measureByID[sm.MeasureID]
if !ok {
continue
}
for _, ids := range hazardIDsByCategory {
for _, id := range ids {
_ = entry // suppress unused
hazardsWithMeasures[id] = true
} }
} rt := iace.ReductionType(m.ReductionType)
} if rt == "" {
for hazCat, hazIDs := range hazardIDsByCategory { rt = iace.ReductionTypeInformation
measCat := patternCatToMeasureCat(hazCat)
measures := measuresByCat[measCat]
for _, hazID := range hazIDs {
if hazardsWithMeasures[hazID] {
continue // Already has pattern-suggested measures
} }
added := 0 _, cerr := h.store.CreateMitigation(ctx, iace.CreateMitigationRequest{
for _, m := range measures { HazardID: hazID, ReductionType: rt,
if added >= 3 { Name: m.Name, Description: m.Description,
break })
} if cerr == nil {
rt := iace.ReductionType(m.ReductionType) created++
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++
}
added++ added++
} }
} }