feat(iace): cross-domain precision overhaul + component review + schema reconcile
Engine precision (stop foreign-machine patterns leaking into a project):
- Wire project.MachineType into the engine machine-type gate (empty input no
longer fires every machine class — press/cnc/excavator/crane/medical...).
- Capability-domain gating extended by 7 domains (outdoor, ventilation,
machining, bulk, palletizer, playground, fitness) so domain-specific hazards
only fire when the narrative names that domain; emitted via keyword_dictionary.
- Relevance backstop moved into iace (single gating contract, testable), and its
dominant false-anchor class removed (a long pattern word no longer matches a
short common token; prepositions/leitung added to the generic stoplist).
- New guard tests: TestCrossDomainPrecision (full pipeline, 0 foreign per GT) and
TestPatternReachability now asserts 0 dead patterns. Both GTs keep coverage 1.0.
Reachability fix: the 51 dead patterns required electrical/pneumatic/hydraulic
tags nothing produced — renamed to the canonical electrical_energy/
pneumatic_pressure/hydraulic_pressure/hydraulic_part.
Component review (negation is best-effort + expert-correctable):
- Parser surfaces negated components (ComponentMatch.Negated) instead of dropping
them; negated contribute no tags/energy → no phantom hazards.
- presence_status (vorhanden|nicht_vorhanden|geloescht) + ce_marked on components;
only `vorhanden` feed matching. CE+safety-relevant flags the PL/SIL obligation.
- Force re-seed preserves the expert's component decisions instead of wiping them.
- Tag-based component→hazard assignment (was: all on the first component).
- Negation-aware narrative parsing ("keine Pneumatik" no longer extracts it).
Local-dev DB: ai-sdk sets search_path=compliance,core,public; reconcile migrations
152-156 bring the consolidated local iace tables to the current schema + add the
presence_status/ce_marked columns. Machine-type vocabulary endpoint for the form.
[migration-approved]
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -339,81 +339,6 @@ func containsSubstring(haystack, needle string) bool {
|
||||
)
|
||||
}
|
||||
|
||||
// genericSafetyTerms are words that appear in almost all risk assessments
|
||||
// and should NOT be used to determine machine-specificity.
|
||||
var genericSafetyTerms = map[string]bool{
|
||||
"maschine": true, "anlage": true, "bereich": true, "gesamte": true,
|
||||
"arbeitsplatz": true, "gefahrbereich": true, "gefahrstelle": true,
|
||||
"gefahrenstelle": true, "person": true, "werker": true, "bediener": true,
|
||||
"steuerung": true, "schutzeinrichtung": true, "sicherheit": true,
|
||||
"betrieb": true, "wartung": true, "instandhaltung": true, "reinigung": true,
|
||||
"bewegung": true, "beweglich": true, "feststehend": true, "teil": true,
|
||||
"teile": true, "oeffnung": true, "zugang": true, "gefahr": true,
|
||||
"verletzung": true, "quetsch": true, "scher": true, "schneid": true,
|
||||
"stoss": true, "schlag": true, "einzug": true, "brand": true,
|
||||
"motor": true, "antrieb": true, "achse": true, "achsen": true,
|
||||
"kabel": true, "leitung": true, "schaltschrank": true, "spannung": true,
|
||||
"schutz": true, "gehaeuse": true, "oberflaeche": true, "boden": true,
|
||||
"leitfaehig": true, "elektrisch": true, "mechanisch": true,
|
||||
"bedienfeld": true, "display": true, "anzeige": true,
|
||||
"energie": true, "druck": true, "temperatur": true,
|
||||
// Abbreviations and synonyms that should not trigger relevance filter
|
||||
"kss": true, "emv": true, "esd": true, "dcs": true, "plr": true, "sil": true,
|
||||
"hmi": true, "sps": true, "rcd": true, "loto": true, "psa": true,
|
||||
// Common action words
|
||||
"bersten": true, "platzen": true, "abspringen": true, "spritzen": true,
|
||||
"einatmen": true, "ausrutschen": true, "herabfallen": true,
|
||||
"durchschlaegen": true, "wegschleudern": true,
|
||||
// Common structural terms that don't indicate a specific machine
|
||||
"gesamter": true, "gesamtes": true, "bereichs": true, "stelle": true,
|
||||
"innen": true, "aussen": true, "transport": true, "seite": true,
|
||||
"front": true, "rueck": true, "ober": true, "unter": true,
|
||||
"fuehrung": true, "lager": true, "verschleiss": true, "welle": true,
|
||||
"getriebe": true, "kette": true, "riemen": true, "feder": true,
|
||||
"spindel": true, "werkzeug": true, "werkstueck": true, "flucht": true,
|
||||
}
|
||||
|
||||
// isPatternRelevant checks whether a pattern match is relevant to the actual
|
||||
// machine described in the narrative. Uses narrative vocabulary overlap:
|
||||
// if the pattern's zone/scenario contains machine-specific words (not generic
|
||||
// safety terms) and NONE of them appear in the narrative → irrelevant.
|
||||
func isPatternRelevant(mp iace.PatternMatch, narrative string, compNames []string) bool {
|
||||
patternText := iace.NormalizeDEPublic(mp.ZoneDE + " " + mp.ScenarioDE + " " + mp.PatternName)
|
||||
narrativeNorm := iace.NormalizeDEPublic(narrative)
|
||||
|
||||
// Extract machine-specific words from pattern (not generic safety terms)
|
||||
patternWords := strings.Fields(patternText)
|
||||
var specificWords []string
|
||||
for _, w := range patternWords {
|
||||
// Clean punctuation
|
||||
w = strings.Trim(w, ".,;:!?()/-")
|
||||
if len(w) < 5 || genericSafetyTerms[w] {
|
||||
continue
|
||||
}
|
||||
specificWords = append(specificWords, w)
|
||||
}
|
||||
|
||||
// If pattern has no specific words, it's generic → always relevant
|
||||
if len(specificWords) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if at least one specific word appears in the narrative or components
|
||||
for _, sw := range specificWords {
|
||||
if strings.Contains(narrativeNorm, sw) {
|
||||
return true
|
||||
}
|
||||
for _, cn := range compNames {
|
||||
if strings.Contains(cn, sw) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No specific word found in narrative → pattern is for a different machine
|
||||
return false
|
||||
}
|
||||
|
||||
// categoryHazardCap returns the maximum number of hazards to generate per category.
|
||||
// Caps are based on typical ISO 12100 risk assessment proportions:
|
||||
// - Core physical categories (mechanical, electrical): scale with component count
|
||||
|
||||
Reference in New Issue
Block a user