fix(iace): pattern-specific measures take priority over category fallback
Each hazard now gets measures from its SOURCE PATTERN first (SuggestedMeasureIDs), then category fallback for remaining slots. Previously all mechanical hazards got the same generic top-5 measures (Gefahrstelle eliminieren, Sicherheitsabstaende, Scharfe Kanten...). Now a KSS-Schlauch hazard gets M420 (Druckfeste Auslegung) first. SuggestedMeasureIDs added to PatternMatch struct and passed through from pattern definition to hazard creation to measure assignment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -140,6 +140,7 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) {
|
|||||||
existingHazards, _ := h.store.ListHazards(ctx, projectID)
|
existingHazards, _ := h.store.ListHazards(ctx, projectID)
|
||||||
hazardStep := InitStep{Name: "Gefaehrdungen erstellt", Status: "skipped"}
|
hazardStep := InitStep{Name: "Gefaehrdungen erstellt", Status: "skipped"}
|
||||||
hazardIDsByCategory := make(map[string][]uuid.UUID)
|
hazardIDsByCategory := make(map[string][]uuid.UUID)
|
||||||
|
hazardPatternMeasures := make(map[uuid.UUID][]string)
|
||||||
|
|
||||||
if len(existingHazards) == 0 && len(matchOutput.MatchedPatterns) > 0 {
|
if len(existingHazards) == 0 && len(matchOutput.MatchedPatterns) > 0 {
|
||||||
comps, _ := h.store.ListComponents(ctx, projectID)
|
comps, _ := h.store.ListComponents(ctx, projectID)
|
||||||
@@ -226,6 +227,10 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) {
|
|||||||
created++
|
created++
|
||||||
catCount[cat]++
|
catCount[cat]++
|
||||||
hazardIDsByCategory[cat] = append(hazardIDsByCategory[cat], hz.ID)
|
hazardIDsByCategory[cat] = append(hazardIDsByCategory[cat], hz.ID)
|
||||||
|
// Remember this pattern's suggested measures for this hazard
|
||||||
|
if len(mp.SuggestedMeasureIDs) > 0 {
|
||||||
|
hazardPatternMeasures[hz.ID] = mp.SuggestedMeasureIDs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,42 +271,44 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For each hazard: assign up to maxMitigationsPerHazard measures
|
// For each hazard: assign up to maxMitigationsPerHazard measures
|
||||||
// Priority: pattern-suggested first, then category fallback
|
// Priority 1: Pattern-specific SuggestedMeasureIDs (from the pattern that created this hazard)
|
||||||
suggestedByMeasCat := make(map[string][]iace.ProtectiveMeasureEntry)
|
// Priority 2: Category fallback (generic measures for the hazard category)
|
||||||
for _, sm := range matchOutput.SuggestedMeasures {
|
|
||||||
if entry, ok := measureByID[sm.MeasureID]; ok {
|
|
||||||
suggestedByMeasCat[entry.HazardCategory] = append(suggestedByMeasCat[entry.HazardCategory], entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, hazID := range allHazardIDs {
|
for _, hazID := range allHazardIDs {
|
||||||
hazCat := hazardCatByID[hazID]
|
hazCat := hazardCatByID[hazID]
|
||||||
measCat := patternCatToMeasureCat(hazCat)
|
measCat := patternCatToMeasureCat(hazCat)
|
||||||
added := 0
|
added := 0
|
||||||
|
usedIDs := make(map[string]bool)
|
||||||
|
|
||||||
// First: pattern-suggested measures for this category
|
// Priority 1: Pattern-specific measures
|
||||||
for _, entry := range suggestedByMeasCat[measCat] {
|
if patternMIDs, ok := hazardPatternMeasures[hazID]; ok {
|
||||||
if added >= maxMitigationsPerHazard {
|
for _, mid := range patternMIDs {
|
||||||
break
|
if added >= maxMitigationsPerHazard {
|
||||||
}
|
break
|
||||||
rt := iace.ReductionType(entry.ReductionType)
|
}
|
||||||
if rt == "" {
|
entry, ok := measureByID[mid]
|
||||||
rt = iace.ReductionTypeInformation
|
if !ok {
|
||||||
}
|
continue
|
||||||
_, cerr := h.store.CreateMitigation(ctx, iace.CreateMitigationRequest{
|
}
|
||||||
HazardID: hazID, ReductionType: rt,
|
rt := iace.ReductionType(entry.ReductionType)
|
||||||
Name: entry.Name, Description: entry.Description,
|
if rt == "" {
|
||||||
})
|
rt = iace.ReductionTypeInformation
|
||||||
if cerr == nil {
|
}
|
||||||
created++
|
_, cerr := h.store.CreateMitigation(ctx, iace.CreateMitigationRequest{
|
||||||
added++
|
HazardID: hazID, ReductionType: rt,
|
||||||
|
Name: entry.Name, Description: entry.Description,
|
||||||
|
})
|
||||||
|
if cerr == nil {
|
||||||
|
created++
|
||||||
|
added++
|
||||||
|
usedIDs[mid] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then: category fallback if still under limit
|
// Priority 2: Category fallback (skip already-used IDs)
|
||||||
for _, m := range measuresByCat[measCat] {
|
for _, m := range measuresByCat[measCat] {
|
||||||
if added >= maxMitigationsPerHazard {
|
if added >= maxMitigationsPerHazard || usedIDs[m.ID] {
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
rt := iace.ReductionType(m.ReductionType)
|
rt := iace.ReductionType(m.ReductionType)
|
||||||
if rt == "" {
|
if rt == "" {
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ type PatternMatch struct {
|
|||||||
GeneratedHazardType string `json:"generated_hazard_type,omitempty"`
|
GeneratedHazardType string `json:"generated_hazard_type,omitempty"`
|
||||||
MatchedFailureModes []string `json:"matched_failure_modes,omitempty"`
|
MatchedFailureModes []string `json:"matched_failure_modes,omitempty"`
|
||||||
ApplicableLifecycles []string `json:"applicable_lifecycles,omitempty"`
|
ApplicableLifecycles []string `json:"applicable_lifecycles,omitempty"`
|
||||||
|
SuggestedMeasureIDs []string `json:"suggested_measure_ids,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HazardSuggestion is a suggested hazard from pattern matching.
|
// HazardSuggestion is a suggested hazard from pattern matching.
|
||||||
@@ -220,6 +221,7 @@ func (e *PatternEngine) Match(input MatchInput) *MatchOutput {
|
|||||||
GeneratedHazardType: p.GeneratedHazardType,
|
GeneratedHazardType: p.GeneratedHazardType,
|
||||||
MatchedFailureModes: matchedFMs,
|
MatchedFailureModes: matchedFMs,
|
||||||
ApplicableLifecycles: p.ApplicableLifecycles,
|
ApplicableLifecycles: p.ApplicableLifecycles,
|
||||||
|
SuggestedMeasureIDs: p.SuggestedMeasureIDs,
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, cat := range p.GeneratedHazardCats {
|
for _, cat := range p.GeneratedHazardCats {
|
||||||
|
|||||||
Reference in New Issue
Block a user