feat(iace): generic cross-domain leak gates + norm vocab reconciliation
- 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>
This commit is contained in:
@@ -33,6 +33,11 @@ type gtCase struct {
|
||||
// keyword-stuffed — they represent how an engineer would describe the
|
||||
// machine, so the benchmark stays honest about extraction quality.
|
||||
narrativeOverride string
|
||||
// homeDomains lists the foreignDomainTerms domains that are NATIVE to this
|
||||
// machine, so the cross-domain precision guard does not flag a press's own
|
||||
// "stoessel"/"werkzeugeinbauraum" or a robot cell's "roboterzelle" as a leak.
|
||||
// Empty for machines whose domain has no entry in foreignDomainTerms.
|
||||
homeDomains []string
|
||||
}
|
||||
|
||||
// gtBenchmarkCases is the registry the harness iterates over. Add a new GT
|
||||
@@ -50,6 +55,7 @@ var gtBenchmarkCases = []gtCase{
|
||||
"Pneumatische Greifer und Spannvorrichtungen. Betrieb im Automatikbetrieb, Einrichten " +
|
||||
"und Einlernen (Teachen), Wartung und Stoerungsbeseitigung. Gefaehrdungen durch " +
|
||||
"Quetschen und Einzug bei Roboterbewegung, elektrische Energie und Druckluft.",
|
||||
homeDomains: []string{"robot"},
|
||||
},
|
||||
{
|
||||
name: "Kistenhub (Hebevorrichtung)",
|
||||
@@ -65,6 +71,59 @@ var gtBenchmarkCases = []gtCase{
|
||||
},
|
||||
}
|
||||
|
||||
// precisionOnlyCases are real machines from breakpilot-core/docs-src that have a
|
||||
// Grenzen description but NO expert GT hazard list, so they cannot be coverage-
|
||||
// benchmarked — only checked for cross-domain precision (no foreign-domain
|
||||
// nonsense). They diversify the gating guard beyond the 2 ground truths (lift +
|
||||
// robot cell) across a press, a cobot, a motor and a welding system. Each leak
|
||||
// they would otherwise produce (pool, carousel, paint booth, tank farm, lathe
|
||||
// chuck, band saw, robot-into-press ...) is now a permanent regression guard.
|
||||
var precisionOnlyCases = []gtCase{
|
||||
{
|
||||
name: "Kniehebelpresse (Presse)",
|
||||
machineType: "mechanical_press",
|
||||
homeDomains: []string{"press"},
|
||||
narrativeOverride: "Vollautomatische Kniehebelpresse zur Kaltmassivumformung metallischer " +
|
||||
"Rohlinge. Eine Transferanlage fuehrt Rohlinge ueber ein Foerderband in die Presse, wo sie " +
|
||||
"in mehreren Stufen im Werkzeugeinbauraum zwischen Ober- und Unterwerkzeug umgeformt werden. " +
|
||||
"Stoessel mit Schwungradantrieb, Hydraulikoel und Druckluft im System, integrierte " +
|
||||
"Schmieranlage und Absaugung. Schutzumhausung mit verriegelten Tueren. Elektrische " +
|
||||
"Versorgung 400 V, Steuerung ueber SPS. Betrieb vollautomatisch, Einrichten und Umruesten, " +
|
||||
"Instandhaltung. Impulslaerm und heisse Werkstuecke beim Pressvorgang.",
|
||||
},
|
||||
{
|
||||
name: "Eigenbauzelle (Cobot)",
|
||||
machineType: "robotics_cobot",
|
||||
homeDomains: []string{"robot"},
|
||||
narrativeOverride: "Arbeitstisch mit integriertem kollaborierendem Roboterarm (Cobot) zur " +
|
||||
"Bestueckung von Maschinen. Ein Sicherheitsscanner setzt den Roboterarm bei Annaeherung " +
|
||||
"still. Programmierung ueber Touchscreen. Spannungsversorgung 230 V. Quetsch- und " +
|
||||
"Stossgefahr im Roboterarbeitsraum durch Bewegung des Roboterarms. Betrieb kollaborierend " +
|
||||
"und nicht kollaborierend, Teachen und Programmieren, Reinigung, Instandhaltung.",
|
||||
},
|
||||
{
|
||||
name: "Elektromotoren (Antrieb)",
|
||||
machineType: "general_industry",
|
||||
homeDomains: nil,
|
||||
narrativeOverride: "Gleichstrom- und Asynchronmotoren mit oder ohne integriertes Getriebe als " +
|
||||
"Antrieb in Maschinen. Energieversorgung 24 bis 400 V Gleich- und Wechselstrom. Rotierende " +
|
||||
"Welle und bewegliche Teile des Motors, Gehaeuse mit Stromschlag- und Erhitzungsgefahr, " +
|
||||
"elektrische Anschluesse, Uebertemperaturueberwachung und Schutzleiter. Betrieb, Montage, " +
|
||||
"Reinigung, Instandhaltung, Demontage.",
|
||||
},
|
||||
{
|
||||
name: "Schwingarm (Rundschweissanlage)",
|
||||
machineType: "welding",
|
||||
homeDomains: []string{"welding"},
|
||||
narrativeOverride: "Rundschweissanlage Schwingarm als Auf-Tisch-Version zum Schweissen von " +
|
||||
"Rundnaehten. Pneumatisch bewegter Brennerarm, Anschluss an MIG/MAG- und TIG-Stromquellen, " +
|
||||
"maximaler Schweissstrom 350 A. Werkstuecke werden in zwei Backenfuttern eingespannt und " +
|
||||
"pneumatisch gesichert, rotierende Werkstueckaufnahme mit Reitstock. Formiergas durch die " +
|
||||
"Hohlwelle. Leitfaehige Gehaeuseoberflaechen, Brenner mit Verbrennungsgefahr. Bedienung " +
|
||||
"ueber Fusspedal, integrierte Steuerung.",
|
||||
},
|
||||
}
|
||||
|
||||
// readGTNarrative extracts a machine narrative from the raw GT JSON, trying the
|
||||
// richer machine_description field before the generic description.
|
||||
func readGTNarrative(t *testing.T, path string) (gt GroundTruth, narrative, machineName string) {
|
||||
@@ -233,8 +292,39 @@ var foreignDomainTerms = map[string]string{
|
||||
// playground / fitness
|
||||
"klettergeraet": "playground", "spielplatz": "playground", "kraftstation": "fitness",
|
||||
"bankdrueck": "fitness", "kniebeug": "fitness",
|
||||
"schaukelkette": "playground", "nestschaukel": "playground",
|
||||
// palletizer
|
||||
"palettierer": "palletizer",
|
||||
// aquatic / pool
|
||||
"schwimmbecken": "aquatic", "schwimmbad": "aquatic", "beckenumrandung": "aquatic",
|
||||
"massageduese": "aquatic", "schwimmbadtechnik": "aquatic", "sprungturm": "aquatic",
|
||||
// amusement
|
||||
"karussell": "amusement", "fahrgeschaeft": "amusement", "riesenrad": "amusement",
|
||||
// mobile machine with driver cab
|
||||
"fahrersitz": "mobile_cab", "fahrerkabine": "mobile_cab", "fahrerstand": "mobile_cab",
|
||||
// coating / paint booth
|
||||
"lackier": "coating", "loesemitteldampf": "coating", "pulverbeschicht": "coating",
|
||||
// ex process / tank farm
|
||||
"tanklager": "exproc", "raffinerie": "exproc",
|
||||
// chemical reactor
|
||||
"reaktor": "chem", "mischbereich": "chem", "exotherme reaktion": "chem",
|
||||
// oxygen / gas supply
|
||||
"sauerstoffanreicherung": "o2", "sauerstoff-versorgung": "o2",
|
||||
// lathe / chip machining
|
||||
"drehfutter": "cnc", "spannfutterbacke": "cnc", "spaeneflug": "cnc",
|
||||
"spanflug": "cnc", "spindelumgebung": "cnc",
|
||||
// sawing
|
||||
"bandsaege": "sawing", "saegeband": "sawing",
|
||||
// film / carton converting
|
||||
"folienwickler": "converting", "folientrennbereich": "converting", "kartonschneider": "converting",
|
||||
// blow molding (plastics)
|
||||
"blasformwerkzeug": "plastics", "blasstation": "plastics",
|
||||
// textile cutting
|
||||
"stoffauflage": "textile", "konfektionierung": "textile",
|
||||
// asbestos legacy
|
||||
"asbest": "asbestos",
|
||||
// robot (home for cobot/robot-cell cases via homeDomains)
|
||||
"roboterzelle": "robot", "schwenkbereich roboter": "robot", "roboter-arbeitsraum": "robot",
|
||||
}
|
||||
|
||||
// TestGT_DomainLeakage names the patterns that leak across domains. For each GT
|
||||
@@ -252,6 +342,10 @@ func TestGT_DomainLeakage(t *testing.T) {
|
||||
if c.narrativeOverride != "" {
|
||||
narrative = c.narrativeOverride
|
||||
}
|
||||
home := make(map[string]bool, len(c.homeDomains))
|
||||
for _, d := range c.homeDomains {
|
||||
home[d] = true
|
||||
}
|
||||
pr := ParseNarrative(narrative, c.machineType)
|
||||
out := NewPatternEngine().Match(parseResultToMatchInput(pr, c.machineType))
|
||||
|
||||
@@ -259,6 +353,9 @@ func TestGT_DomainLeakage(t *testing.T) {
|
||||
for _, pm := range out.MatchedPatterns {
|
||||
text := normalizeDE(pm.PatternName + " " + pm.ScenarioDE)
|
||||
for term, domain := range foreignDomainTerms {
|
||||
if home[domain] {
|
||||
continue // native to this machine — not a leak
|
||||
}
|
||||
if strings.Contains(text, term) {
|
||||
leaks = append(leaks, pm.PatternID)
|
||||
leakCount[pm.PatternID]++
|
||||
|
||||
Reference in New Issue
Block a user