005a2ed711
- Domain-gate ~15 foreign machine classes (pool, amusement, paint booth, tank farm, reactor, lathe/chips, saw, film/carton, robot, mobile cab, asbestos, playground swing) in pattern_domain_gates.go so ungated hazard patterns stop leaking into unrelated machines; matching emit keywords added in keyword_dictionary.go (gate+emit share one vocabulary). - Extend the cross-domain precision guard to 6 machine classes (press, cobot, motor, welding + the 2 GTs) with per-case homeDomains, so a machine's own domain terms are never flagged. GT coverage stays 100%. - Reconcile the fine-grained norm machine-type vocabulary (455 keys) with the 68 canonical dropdown keys via canonicalMachineType() family folding in matchNorm — welding 0->17, robotics_cobot 0->6, press 8->13, circular_saw 1->35 machine-specific C-norms. Pattern gating left strict. - Fix initialize?force=true summary index-shift that mislabeled counts (reported matched-patterns as "hazards"); now uses named step vars. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
161 lines
8.5 KiB
Go
161 lines
8.5 KiB
Go
package iace
|
|
|
|
import "strings"
|
|
|
|
// Capability-Domain-Gating — the cure for cross-domain leakage.
|
|
//
|
|
// Many domain-specific hazard patterns were authored gated only by a GENERIC
|
|
// capability tag (e.g. "rotating_part"), so they fire for every machine that
|
|
// has rotating parts — a lift, a robot cell — even though the hazard belongs to
|
|
// a press, a spinning machine or a PV array. This is the precision-killing
|
|
// inverse of ghost patterns; both stem from inconsistent applicability.
|
|
//
|
|
// The fix is capability-driven (NOT a machine-type whitelist hack): a pattern
|
|
// whose OWN scenario text names a foreign machine gets that domain's capability
|
|
// tag appended to its RequiredComponentTags. The same tag is emitted by the
|
|
// domain's narrative keywords (keyword_dictionary.go), so the pattern still
|
|
// fires for its real domain but no longer leaks into unrelated machines.
|
|
//
|
|
// INVARIANT: every tag below MUST be emittable via keyword_dictionary.go,
|
|
// otherwise the gated pattern becomes a ghost. TestTagVocabulary_GhostPatterns
|
|
// is the regression guard for this.
|
|
|
|
// domainGateTerms maps a machine-betraying term (umlaut-normalised, lowercase)
|
|
// to the domain capability tag that gates patterns mentioning it.
|
|
var domainGateTerms = map[string]string{
|
|
// Pressen / Stanzen / Umformen
|
|
"stanzhub": "dom_press", "pressenhub": "dom_press", "pressenstoessel": "dom_press",
|
|
"dauerhub": "dom_press", "exzenterpresse": "dom_press", "beinpresse": "dom_press",
|
|
"stanzpresse": "dom_press", "umformpresse": "dom_press",
|
|
"pressenteil": "dom_press", "pressraum": "dom_press", "blechbearbeitung": "dom_press",
|
|
"werkzeugraum der presse": "dom_press",
|
|
// Glas-Bearbeitung
|
|
"glasschneid": "dom_glass", "glasbearbeitung": "dom_glass", "glasscheibe": "dom_glass",
|
|
"glaskante": "dom_glass",
|
|
// Kunststoff / Spritzguss / Extrusion
|
|
"spritzgie": "dom_plastics", "extruder": "dom_plastics", "extrusion": "dom_plastics",
|
|
"kunststoffschmelze": "dom_plastics", "schliesseinheit": "dom_plastics",
|
|
// Walzen / Kalander / Laminieren
|
|
"walzenspalt": "dom_rolling", "zweiwalzenwerk": "dom_rolling", "kalander": "dom_rolling",
|
|
"walzwerk": "dom_rolling", "laminieranlage": "dom_rolling", "laminier": "dom_rolling",
|
|
// Textil
|
|
"spinnmaschine": "dom_textile", "webmaschine": "dom_textile", "spinnerei": "dom_textile",
|
|
// Schleifen
|
|
"schleifscheibe": "dom_grinding", "schleifbock": "dom_grinding",
|
|
// Schweissen
|
|
"widerstandsschweiss": "dom_welding", "lichtbogenschweiss": "dom_welding",
|
|
"schutzgasschweiss": "dom_welding", "punktschweiss": "dom_welding",
|
|
"schweisselektrod": "dom_welding", "elektrodenspalt": "dom_welding",
|
|
// Solar / PV
|
|
"pv-modul": "dom_solar", "photovoltaik": "dom_solar", "pv-anlage": "dom_solar",
|
|
"dc-steckverbindung": "dom_solar", "solarmodul": "dom_solar",
|
|
// Windkraft
|
|
"gondel": "dom_wind", "rotorblatt": "dom_wind", "windenergieanlage": "dom_wind",
|
|
// CNC / Zerspanung
|
|
"drehmaschine": "dom_cnc", "fraesmaschine": "dom_cnc",
|
|
// Landwirtschaft
|
|
"maehdrescher": "dom_agri", "ballenpresse": "dom_agri", "feldhaecksler": "dom_agri",
|
|
// Roll-/Fahrtreppe
|
|
"rolltreppe": "dom_escalator", "fahrtreppe": "dom_escalator",
|
|
// Aussen-/Witterungs-/Bioarbeit (Forst, Bau im Freien)
|
|
"zecke": "dom_outdoor", "zeckenstich": "dom_outdoor", "fsme": "dom_outdoor",
|
|
"borreliose": "dom_outdoor", "im freien": "dom_outdoor", "freigelaende": "dom_outdoor",
|
|
"aussengelaende": "dom_outdoor", "ausseneinsatz": "dom_outdoor", "witterung": "dom_outdoor",
|
|
"winterarbeit": "dom_outdoor", "nagerkot": "dom_outdoor", "hantavirus": "dom_outdoor",
|
|
// Lueftung/Feuchte (Schimmel)
|
|
"schimmel": "dom_ventilation", "schimmelspor": "dom_ventilation",
|
|
"lueftungsanlage": "dom_ventilation", "lueftungskanal": "dom_ventilation",
|
|
// Zerspanung / Kuehlschmierstoff
|
|
"kuehlschmierstoff": "dom_machining", "kss-kreislauf": "dom_machining",
|
|
"kss-aufbereitung": "dom_machining", "kuehlturm": "dom_machining",
|
|
"bearbeitungszentrum": "dom_machining",
|
|
// Schuettgut / Silo / Gaerbehaelter (Confined Space mit Schuettgut)
|
|
"silo": "dom_bulk", "schuettgut": "dom_bulk", "gaerbehaelter": "dom_bulk",
|
|
"getreidesilo": "dom_bulk", "mehlsilo": "dom_bulk",
|
|
// Palettierer
|
|
"palettierer": "dom_palletizer", "palettieranlage": "dom_palletizer",
|
|
// Spielplatz / Spielgeraet
|
|
"klettergeraet": "dom_playground", "spielplatz": "dom_playground", "spielgeraet": "dom_playground",
|
|
// Fitness / Kraftgeraet
|
|
"gewichtstapel": "dom_fitness", "langhantel": "dom_fitness", "bankdrueck": "dom_fitness",
|
|
"kniebeug": "dom_fitness", "kraftstation": "dom_fitness",
|
|
// Schwimmbad / Aquatik (Entrapment, Nassbereich-Strom, Beckenrand)
|
|
"schwimmbecken": "dom_aquatic", "schwimmbad": "dom_aquatic", "schwimmhalle": "dom_aquatic",
|
|
"beckenumrandung": "dom_aquatic", "beckenrand": "dom_aquatic", "massageduese": "dom_aquatic",
|
|
"badegaeste": "dom_aquatic", "sprungturm": "dom_aquatic", "schwimmbadtechnik": "dom_aquatic",
|
|
// Fahrgeschaeft / Vergnuegungspark
|
|
"karussell": "dom_amusement", "fahrgeschaeft": "dom_amusement", "riesenrad": "dom_amusement",
|
|
"achterbahn": "dom_amusement",
|
|
// Mobile Maschine mit Fahrerstand (Ganzkoerpervibration etc.)
|
|
"fahrersitz": "dom_mobile_cab", "fahrerkabine": "dom_mobile_cab", "fahrerstand": "dom_mobile_cab",
|
|
"fahrerhaus": "dom_mobile_cab",
|
|
// Lackieren / Beschichten (Loesemittel, ESD-Zuendung Lackierzone)
|
|
"lackier": "dom_coating", "loesemitteldampf": "dom_coating", "pulverbeschicht": "dom_coating",
|
|
"spritzlackier": "dom_coating",
|
|
// Ex-Prozessanlage / Tanklager
|
|
"tanklager": "dom_exproc", "raffinerie": "dom_exproc", "tankfarm": "dom_exproc",
|
|
// Chemie-Reaktor / Mischanlage
|
|
"reaktor": "dom_chem", "mischbereich": "dom_chem", "exotherme reaktion": "dom_chem",
|
|
"ruehrkessel": "dom_chem",
|
|
// Sauerstoff-/Gasversorgungsanlage
|
|
"sauerstoffanreicherung": "dom_o2", "sauerstoff-versorgung": "dom_o2",
|
|
// Drehmaschine / Zerspanung (Spannfutter, Spaeneflug, Spindelumgebung)
|
|
"drehfutter": "dom_cnc", "spannfutterbacke": "dom_cnc", "spannbacke": "dom_cnc",
|
|
"spaeneflug": "dom_cnc", "spanflug": "dom_cnc", "spindelumgebung": "dom_cnc",
|
|
"werkzeugmaschine": "dom_cnc",
|
|
// Roboter / Cobot (ungated Roboterzellen-Hazards)
|
|
"roboterzelle": "dom_robot", "roboterarm": "dom_robot", "roboter-arbeitsraum": "dom_robot",
|
|
"schwenkbereich roboter": "dom_robot", "knickarmroboter": "dom_robot", "teach-zone": "dom_robot",
|
|
// Saege (Bandsaege, Gattersaege)
|
|
"bandsaege": "dom_sawing", "saegeband": "dom_sawing", "gattersaege": "dom_sawing",
|
|
// Folien-/Karton-Konfektionierung (Wickler, Trennmesser)
|
|
"folienwickler": "dom_converting", "folieneinlauf": "dom_converting", "wickelachse": "dom_converting",
|
|
"folientrennbereich": "dom_converting", "kartonschneider": "dom_converting",
|
|
// Kunststoff Blasformen (ergaenzt dom_plastics)
|
|
"blasformwerkzeug": "dom_plastics", "blasstation": "dom_plastics", "blasform": "dom_plastics",
|
|
// Textil-Zuschnitt / Konfektionierung (ergaenzt dom_textile)
|
|
"stoffauflage": "dom_textile", "konfektionierung": "dom_textile", "schneidkopfbereich": "dom_textile",
|
|
// Abgelegener / untertage Einzelarbeitsplatz (kein Notruf-Empfang)
|
|
"kein empfang": "dom_remote", "unterirdisch": "dom_remote", "untertage": "dom_remote",
|
|
// Asbest-Altanlagen
|
|
"asbest": "dom_asbestos",
|
|
// Spielplatz-Schaukel (ergaenzt dom_playground: Kettenglied-Fingerfang)
|
|
"schaukelkette": "dom_playground", "nestschaukel": "dom_playground", "schaukelsitz": "dom_playground",
|
|
}
|
|
|
|
// applyDomainGates appends a domain capability tag to every pattern whose own
|
|
// text betrays that domain, so domain-specific hazards stop leaking into
|
|
// unrelated machines. Idempotent; safe to run once after pattern collection.
|
|
func applyDomainGates(patterns []HazardPattern) []HazardPattern {
|
|
for i := range patterns {
|
|
text := normalizeGateText(patterns[i].NameDE + " " + patterns[i].ScenarioDE + " " +
|
|
patterns[i].TriggerDE + " " + patterns[i].HarmDE + " " + patterns[i].ZoneDE)
|
|
|
|
present := make(map[string]bool, len(patterns[i].RequiredComponentTags))
|
|
for _, t := range patterns[i].RequiredComponentTags {
|
|
present[t] = true
|
|
}
|
|
for term, tag := range domainGateTerms {
|
|
if present[tag] {
|
|
continue
|
|
}
|
|
if strings.Contains(text, term) {
|
|
patterns[i].RequiredComponentTags = append(patterns[i].RequiredComponentTags, tag)
|
|
present[tag] = true
|
|
}
|
|
}
|
|
}
|
|
return patterns
|
|
}
|
|
|
|
// normalizeGateText lowercases and folds umlauts, matching keyword_dictionary's
|
|
// normalisation so gate terms and emit keywords use one vocabulary.
|
|
func normalizeGateText(s string) string {
|
|
s = strings.ToLower(s)
|
|
s = strings.ReplaceAll(s, "ä", "ae")
|
|
s = strings.ReplaceAll(s, "ö", "oe")
|
|
s = strings.ReplaceAll(s, "ü", "ue")
|
|
s = strings.ReplaceAll(s, "ß", "ss")
|
|
return s
|
|
}
|