feat(iace): secondary-harm chain model + AllPatterns drift fix

Task #17 — Folgegefahren-Modell as Vorbereitungs-Commit (no DB schema
change yet; persistence via separate [migration-approved] commit).

New:
- secondary_harms.go: SecondaryHarm struct + six canonical categories
  (consumer_safety, product_liability, food_safety, environmental,
  reputation, financial) with DE labels.
- hazard_pattern_types.go: HazardPattern extended with optional
  SecondaryHarms field — pattern library can now attach consequential-
  damage chains.
- hazard_patterns_secondary_demo.go: two worked examples
  - HP2000 Glasbruch carbonated bottling (the "Cola splitter" scenario
    from the IACE strategy discussion) with consumer_safety + food_safety
    + reputation chains
  - HP2001 Pharma fill-finish cross-contamination with consumer_safety
    + product_liability under AMG §84

Bonus fix:
- compliance_crossover.go AllPatterns() was a duplicate enumeration that
  silently drifted from collectAllPatterns() in pattern_registry.go.
  Pre-fix: 1058 patterns visible. Post-fix: 1213 patterns. The 155 invisible
  patterns included CRA, ISO12100 gaps, robot-cell, CNC extended, VDMA,
  textile-agri, GT-bremse — anything added after the original AllPatterns
  was authored. Audit-Suite (cmd/iace-audit) now sees the full set.

Next steps for full secondary-harm rollout:
- DB migration: hazards table + secondary_harms array column
- API: surface secondary_harms in /projects/:id/hazards response
- Frontend: collapsible Folgegefahren-Panel in HazardTable
This commit is contained in:
Benjamin Admin
2026-05-21 23:36:26 +02:00
parent c5c168592b
commit 16fd406c1a
5 changed files with 230 additions and 32 deletions
@@ -104,39 +104,14 @@ func GetProjectComplianceTriggers(hazards []Hazard, patterns []HazardPattern) *C
}
}
// AllPatterns returns every hazard pattern from all pattern sources.
// This mirrors the aggregation in NewPatternEngine but returns just the slice.
// AllPatterns returns every registered hazard pattern. Delegates to
// collectAllPatterns() in pattern_registry.go so new pattern sources only
// need to be added in one place. Pre-2026-05-21 this function maintained
// a duplicate enumeration which silently drifted from the registry —
// CRA, ISO12100-gap, robot-cell, CNC, VDMA, textile-agri, GT-bremse and
// secondary-harm patterns were invisible to AllPatterns callers.
func AllPatterns() []HazardPattern {
p := GetBuiltinHazardPatterns()
p = append(p, GetExtendedHazardPatterns()...)
p = append(p, GetPressHazardPatterns()...)
p = append(p, GetCobotHazardPatterns()...)
p = append(p, GetOperationalHazardPatterns()...)
p = append(p, GetDGUVExtendedPatterns()...)
p = append(p, GetExtendedHazardPatterns2()...)
p = append(p, GetElevatorPatterns()...)
p = append(p, GetAGVAgriPatterns()...)
p = append(p, GetFoodProcessingPatterns()...)
p = append(p, GetPackagingPatterns()...)
p = append(p, GetLaserPatterns()...)
p = append(p, GetMedicalDevicePatterns()...)
p = append(p, GetPressureEquipmentPatterns()...)
p = append(p, GetConstructionPatterns()...)
p = append(p, GetForestryConveyorPatterns()...)
p = append(p, GetPlasticsMetalPatterns()...)
p = append(p, GetWeldingGlassTextilePatterns()...)
p = append(p, GetSpecificMachinePatterns()...)
p = append(p, GetSpecificMachinePatterns2()...)
p = append(p, GetCyberExtendedPatterns()...)
p = append(p, GetCyberExtendedPatterns2()...)
p = append(p, GetCyberExtendedPatterns3()...)
p = append(p, GetWorkshopPatterns()...)
p = append(p, GetMaintenanceExtPatterns()...)
p = append(p, GetFinalPatternsA()...)
p = append(p, GetFinalPatternsB()...)
p = append(p, GetFinalPatternsC()...)
p = append(p, GetFinalPatternsD()...)
return p
return collectAllPatterns()
}
// extractPatternIDs scans a text for "HP" followed by digits and adds