From 69729ef6ac2a8bad15f66c7b2af34b37578d154a Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Sat, 16 May 2026 22:51:50 +0200 Subject: [PATCH] feat(iace): norm references in mitigations + aggregated norm panel per hazard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Library measures carry NormReferences (EN/IEC/ISO/DIN/TRBS/TRGS Ziff./Kap./ Pos.) but they were dropped on persist: CreateMitigationRequest only wrote Name + Description. The Fachmann benchmark file lists Normen for 34 of 60 hazards — the engine had this data already but lost it on the way to the UI. Fix without DB schema change: - Mitigation.Description gets a "Normen: EN 60204-1 Ziff. 6.2 | EN 61140" line appended when the measure has NormReferences. Pipe separator keeps the inline panel short and grep-friendly. - After all mitigations land, the aggregated dedup'd norm list for the hazard is appended to Hazard.Description as a single "Referenzierte Normen: ..." line so the UI can show one panel per hazard without scanning every mitigation. Audit of library coverage (per-pattern) showed GT-Bremse Normen are generally present and richer: - HP1640 covers GT 2.2 (EN 60204-1 Ziff. 6.2, Ziff. 8.2.3, EN 61140 +) - HP1641 covers GT 2.4 (EN 60204-1 Ziff. 8.2.6 +) - HP1605 covers GT 1.7 (ISO 10218-1 Ziff. 5.6.2, 5.8.3 — Ziff. 5.7.3 fehlt) - HP1671 covers GT 1.30 (EN 12417 — Pos. detail fehlt) Followup: 2 fine-grained sub-paragraph references (5.7.3, Pos. 1.1.4) can be added later as measure-text updates. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../api/handlers/iace_handler_init.go | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/ai-compliance-sdk/internal/api/handlers/iace_handler_init.go b/ai-compliance-sdk/internal/api/handlers/iace_handler_init.go index fc1940d3..235416a0 100644 --- a/ai-compliance-sdk/internal/api/handlers/iace_handler_init.go +++ b/ai-compliance-sdk/internal/api/handlers/iace_handler_init.go @@ -304,6 +304,11 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) { hazCat := hazardCatByID[hazID] accepted := acceptableMeasureCategories(hazCat) added := 0 + // Aggregate norm references across all kept mitigations for this + // hazard so we can attach a single "Referenzierte Normen" line + // to the hazard description below. + var hazardNorms []string + seenNorm := map[string]bool{} if patternMIDs, ok := hazardPatternMeasures[hazID]; ok { for _, mid := range patternMIDs { @@ -324,9 +329,19 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) { if rt == "" { rt = iace.ReductionTypeInformation } + mitDesc := entry.Description + if len(entry.NormReferences) > 0 { + mitDesc += "\n\nNormen: " + strings.Join(entry.NormReferences, " | ") + for _, n := range entry.NormReferences { + if !seenNorm[n] { + seenNorm[n] = true + hazardNorms = append(hazardNorms, n) + } + } + } _, cerr := h.store.CreateMitigation(ctx, iace.CreateMitigationRequest{ HazardID: hazID, ReductionType: rt, - Name: entry.Name, Description: entry.Description, + Name: entry.Name, Description: mitDesc, }) if cerr != nil { fmt.Printf("MEASURE-ERROR: mid=%s name=%s err=%v\n", mid, entry.Name, cerr) @@ -336,6 +351,18 @@ func (h *IACEHandler) InitializeProject(c *gin.Context) { } } } + // Append the aggregated norm list to the hazard so the UI shows + // a single "Referenzierte Normen" panel per hazard. + if len(hazardNorms) > 0 { + if existing, getErr := h.store.GetHazard(ctx, hazID); getErr == nil && existing != nil { + if !strings.Contains(existing.Description, "Referenzierte Normen:") { + newDesc := existing.Description + "\n\nReferenzierte Normen: " + strings.Join(hazardNorms, " | ") + _, _ = h.store.UpdateHazard(ctx, hazID, map[string]interface{}{ + "description": newDesc, + }) + } + } + } if added == 0 { zeroMitigationHazards++