fix(iace): rename 58 duplicate HP-IDs in extended.go/extended2.go

Background: hazard_patterns_extended.go (HP045-074) and _extended2.go
(HP074-102) shared their entire ID range with the semantically-different
patterns in hazard_patterns_cobot.go, hazard_patterns_press.go,
hazard_patterns_operational.go and hazard_patterns_extended_dguv.go.
The collision had lived unnoticed because TestGetBuiltinHazardPatterns_-
UniqueIDs only checks the 44 builtin patterns (HP001-HP044).

Examples of the collision:
- HP059 = "Kollision Mensch-Roboter" (cobot.go) vs "Kupplung — mechanisch" (extended.go)
- HP060 = "Quetschen durch Werkzeug am Cobot" (cobot.go) vs "Diagnosemodul — Software" (extended.go)
- HP073 = "Wartung ohne LOTO" (operational.go) vs "Hydraulikventil — hydraulisch" (extended.go)

At runtime collectAllPatterns() returned both patterns under the same ID
which made downstream lookups (e.g. hazardPatternMeasures map keyed by
pattern_id) non-deterministic — last-loaded wins, dropping the other
pattern's mitigation set silently.

Rename strategy (no deletes — both patterns are real and earn their
SuggestedMeasureIDs after the category-filter work):
  extended.go  HP045..HP073 -> HP1800..HP1828 (29 IDs)
  extended2.go HP074..HP102 -> HP1830..HP1858 (29 IDs)

cobot/press/operational/extended_dguv keep their original IDs because:
- compliance_triggers.go references HP059/HP060 with the cobot meaning
- pattern_engine_test.go references HP073 with the LOTO/maintenance meaning
- phase3_4_test.go references HP073 the same way

New regression test:
- TestAllPatterns_UniqueIDs runs over collectAllPatterns() and fails if
  ANY pattern in the runtime set duplicates an ID. The old
  TestGetBuiltinHazardPatterns_UniqueIDs stays for the builtin subset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-16 22:00:06 +02:00
parent 6a3e96d54c
commit 80d62a0c5f
3 changed files with 92 additions and 58 deletions
@@ -113,6 +113,40 @@ func TestEveryPattern_HasCategoryCompatibleMeasure(t *testing.T) {
}
}
// TestAllPatterns_UniqueIDs pins that collectAllPatterns() — i.e. every
// pattern that the engine actually sees at runtime — has globally unique
// HP-IDs. The older TestGetBuiltinHazardPatterns_UniqueIDs only checks the
// 44 builtin ones and missed 58 duplicates between extended.go/extended2.go
// and cobot/press/operational/extended_dguv.go that lived undetected for
// months (a hazard with the same HP-ID but different scenario would silently
// shadow its sibling in the persistence layer).
func TestAllPatterns_UniqueIDs(t *testing.T) {
seen := map[string]string{} // HP-ID -> first NameDE
dups := []string{}
for _, p := range collectAllPatterns() {
if p.ID == "" {
t.Errorf("pattern with empty ID: %s", p.NameDE)
continue
}
if prev, ok := seen[p.ID]; ok {
dups = append(dups, p.ID+" ("+prev+" vs "+p.NameDE+")")
continue
}
seen[p.ID] = p.NameDE
}
if len(dups) > 0 {
t.Errorf("%d duplicate HP-IDs across all pattern sources:", len(dups))
const maxList = 20
for i, d := range dups {
if i >= maxList {
t.Errorf(" ... and %d more", len(dups)-maxList)
break
}
t.Errorf(" - %s", d)
}
}
}
func joinCats(cats []string) string {
out := ""
for i, c := range cats {