Phase 16 of the Klaerungen / risk-assessment polish. Sources from
EN ISO 12100 Anhang B Tabelle B.1 are now first-class:
A) HazardPattern.ISO12100Section identifier (string), persisted only as
the section number (e.g. "6.3.5.5") — not the norm text. Keeps the
library urheberrechtlich neutral (DIN/Beuth license). 57 patterns
labeled today; rest will follow on touch.
B) Category split per ISO 12100 Nr. 4 vs Nr. 5:
- 16 patterns reclassified noise_vibration -> noise_hazard
- 7 patterns reclassified noise_vibration -> vibration_hazard
- 1 pattern (HP228 UV-/Laermexposition) kept multi-cat
acceptableMeasureCategories now accepts both new aliases plus the
legacy noise_vibration. Coverage test recognises both as valid.
C) 5 new ISO-12100-Annex-B gap patterns (HP1900-HP1904):
- HP1900 Vakuum-Verletzung (6.3.5.5)
- HP1901 Federenergie / elastische Elemente (6.2.10)
- HP1902 Rutschen/Stolpern auf rauer Oberflaeche (6.3.5.6)
- HP1903 Hochdruckinjektion (6.3.5.4) — includes clarifying
"no hand-locating of leaks" question
- HP1904 Ersticken durch Brustkorbquetschung (6.3.5.2)
The library now mirrors the ISO 12100 Annex B structure for the gaps
the Bremse benchmark surfaced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
[migration-approved]
Three pieces complete the Klaerungen lifecycle:
1. Migration 028: iace_clarifications + iace_clarification_comments +
iace_clarification_history. Deterministic clarification_key
(UNIQUE per project) so engine re-inits don't lose answers.
History table logs every status/answer transition. The previous
JSONB-in-metadata storage is kept as read-only fallback for
pre-migration projects until a one-shot upcopy script runs.
2. Multi-User-Workflow:
- assigned_to field on every clarification (free-text user kuerzel
for now; an FK to users can be added in a follow-up).
- Comment thread per clarification (POST .../comment, GET
.../detail returns the thread).
- Status-history log written by UpsertClarification when the
status or answer actually changes.
- Frontend Modal: Zugewiesen-an + Bearbeiter fields, comment
thread with inline post, collapsible history section.
3. PDF-Export via print-friendly HTML:
- GET /clarifications.html returns a standalone A4-styled
document with status badges, norm references, affected hazards
and a signature row at the bottom. The Bediener opens the link
and uses Strg-P / Cmd-P to save as PDF. No server-side PDF
dependency added.
- Frontend "PDF / Druck" button next to CSV export.
Backend:
- internal/iace/store_clarifications.go: UpsertClarification,
ListClarificationsForProject, GetClarificationByKey,
AddClarificationComment, ListClarificationComments,
ListClarificationHistory.
- internal/api/handlers/iace_handler_clarifications.go:
- AnswerClarification now writes the SQL row, falls back to legacy
JSONB read on list.
- PostClarificationComment, ListClarificationDetail,
ExportClarificationsHTML added.
Migration must be applied manually on Mac Mini and prod via
psql -f /migrations/028_iace_clarifications.sql — pattern as in
scripts/apply_*_migration.sh.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three pieces complete the Klaerungen UX:
1. Sidebar-Counter: layout.tsx polls /clarifications and shows a
colored open-count badge on the "Klaerungen" nav item. Refreshes
whenever the user changes route.
2. CSV-Export: new backend endpoint
GET /sdk/v1/iace/projects/:id/clarifications.csv produces a UTF-8-
BOM-prefixed semicolon-separated CSV (Excel-friendly) with ID,
Quelle, Kategorie, Frage, Status, Antwort, Begruendung, Bearbeiter,
answered_at, anzahl Gefaehrdungen, Gefaehrdungs-Namen, Norm-Refs.
Frontend Klaerungen-Seite bekommt einen "CSV-Export"-Button.
3. Hazard-Banner statt Fragentext im Benchmark-Detail: the previous
bulleted clarification list was duplicated across 48 hazards for a
single FANUC question. Phase 2 replaces it with a compact status
badge — "N offene Klaerung(en) — Klaerungen-Seite oeffnen" (orange)
or "Alle N Klaerungen beantwortet" (green) with a direct link.
Backend cleanup: iace_handler_init.go no longer appends the "Mit
Anlagenbauer zu klaeren" block to Hazard.Description. The description
stays focused on the scenario; clarifications live in the dedicated
endpoint and answers persist across re-inits via project.metadata.
The aggregated "Referenzierte Normen" line on the hazard is kept.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New page "Klaerungen" between Massnahmen and Verifikation.
Backend:
- internal/iace/clarifications.go: Clarification struct + ClarificationAnswer +
BuildProjectClarifications() — aggregates pattern-level + manufacturer-
level questions from collectAllPatterns + GetManufacturerSafetyFeatures.
Deterministic IDs ("pattern:HP1640:0", "manuf:fanuc:dual-check-safety-dcs:1")
so persisted answers survive every re-init.
- internal/api/handlers/iace_handler_clarifications.go:
- GET /projects/:id/clarifications returns aggregated list with affected
hazard names + persisted answer state, sorted (open first).
- POST /projects/:id/clarifications/:cid/answer writes status/answer/
reasoning/answered_by/answered_at to project.metadata.clarification_-
answers — no DB schema change.
Frontend:
- admin-compliance/app/sdk/iace/layout.tsx: new "Klaerungen" nav item.
- app/sdk/iace/[projectId]/clarifications/page.tsx: table grouped by
source (FANUC / Pattern HP1640 / …), Filter Offen/Beantwortet/Alle,
search field, Antwort-Modal with status/answer/Begruendung/Bearbeiter.
A clarification answered once applies to ALL referenced hazards — the
operator no longer has to answer the same FANUC DCS question on 48
mechanical hazards individually.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a curated database of safety-relevant features for the major
manufacturers across mechanical/plant engineering, written entirely in
own words with norm anchors. No verbatim manufacturer texts — therefore
no copyright issue:
- Markennennung (§ 23 MarkenG nominative use) is permitted.
- Fakten ueber Produkt-Sicherheitsfunktionen are not protected by § 2
UrhG (only Werke, not facts).
- NormReferences contain only the identifiers (e.g. "EN ISO 13849-1
PLd Kat.3"), never the norm text itself.
Coverage (52 entries across 12 categories):
Industrieroboter (10): FANUC DCS, KUKA SafeOperation, ABB SafeMove,
Yaskawa FSU, Staeubli CS9, Kawasaki Cubic-S, Mitsubishi MELFA,
Universal Robots PolyScope, Doosan PRS, Comau SafeNet
CNC/WZM (8): DMG MORI, Mazak, TRUMPF, Okuma, Hermle, Heidenhain
SPLC, GROB, Heller
Pneumatik (4): Festo, SMC, AVENTICS, Parker
Hydraulik (3): Bosch Rexroth, HAWE, HYDAC
Safety-PLC / Sicherheitstechnik (8): PILZ, SICK, Schmersal, Euchner,
Leuze, Phoenix Contact, Banner, Wieland
Standard-PLC (5): Siemens, Beckhoff, Rockwell, Schneider, B&R
Pressen (3): Schuler, Bruderer, AIDA
Spritzguss (3): Arburg, KraussMaffei, ENGEL
Verpackung (2): Krones, Bosch Packaging/Syntegon
Laser/Schweissen (3): Bystronic, Amada, Fronius
Foerdertechnik (2): Interroll, SEW EURODRIVE
Engine integration:
- LookupManufacturerFeaturesInText() scans the project narrative for
any of the manufacturer aliases (case-insensitive, umlaut-tolerant).
- Init-Handler appends matched feature clarifications to the relevant
hazard's "Mit Anlagenbauer zu klaeren:" block — for the right
HazardCategory only (e.g. FANUC DCS only on mechanical_hazard).
- For a Bremse project narrative mentioning "Fanuc Robodrill", the
engine now adds clarification questions like "Ist DCS am Roboter
konfiguriert?" to relevant mechanical hazards automatically.
Tests: 7 new pin tests — manufacturer count, norm prefixes, FANUC/KUKA
detection in narrative, umlaut robustness (Staeubli vs Staubli).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Library measures carry NormReferences (EN/IEC/ISO/DIN/TRBS/TRGS Ziff./Kap./
Pos.) but they were dropped on persist: CreateMitigationRequest only
wrote Name + Description. The Fachmann benchmark file lists Normen for
34 of 60 hazards — the engine had this data already but lost it on the
way to the UI.
Fix without DB schema change:
- Mitigation.Description gets a "Normen: EN 60204-1 Ziff. 6.2 | EN 61140"
line appended when the measure has NormReferences. Pipe separator keeps
the inline panel short and grep-friendly.
- After all mitigations land, the aggregated dedup'd norm list for the
hazard is appended to Hazard.Description as a single "Referenzierte
Normen: ..." line so the UI can show one panel per hazard without
scanning every mitigation.
Audit of library coverage (per-pattern) showed GT-Bremse Normen are
generally present and richer:
- HP1640 covers GT 2.2 (EN 60204-1 Ziff. 6.2, Ziff. 8.2.3, EN 61140 +)
- HP1641 covers GT 2.4 (EN 60204-1 Ziff. 8.2.6 +)
- HP1605 covers GT 1.7 (ISO 10218-1 Ziff. 5.6.2, 5.8.3 — Ziff. 5.7.3 fehlt)
- HP1671 covers GT 1.30 (EN 12417 — Pos. detail fehlt)
Followup: 2 fine-grained sub-paragraph references (5.7.3, Pos. 1.1.4)
can be added later as measure-text updates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drei nachhaltige Verbesserungen, getrieben durch die Bremse-Benchmark-
Faelle GT 1.4, GT 1.30 und GT 7.4. Die Engine erfindet weiterhin
keine Fachmann-Kommentare — Kommentare bleiben aus, weil sie ein
Verstaendnis der konkreten Anlage erfordern, das die Engine nicht
hat. Statt dessen liefert die Engine norm-basierte Klaerungsfragen
und ein praeziseres Pattern-Vokabular.
A) HazardPattern.ClarificationQuestionsDE — neues optionales Feld:
- Pattern hinterlegt prueffaehige Fragen, die der Bediener mit dem
Anlagenbauer abklaert. Beispiele:
- HP1640: "Liegt ein Pruefprotokoll nach EN 60204-1 vor?"
- HP1666: "Ist die WZM als CE-konformes Subsystem integriert?"
- HP1604: "Ist DCS am Roboter konfiguriert und validiert?"
- Init-Handler haengt die Fragen an Hazard.Description an mit dem
Marker "Mit Anlagenbauer zu klaeren:". Kein DB-Schema-Aenderungs-
bedarf.
- 11 Patterns mit Klaerungsfragen versehen (HP1602, HP1604, HP1611,
HP1612, HP1620, HP1622, HP1637, HP1640, HP1641, HP1666, HP1685).
B) HP1632 "Bersten druckbeaufschlagter Pneumatik-Komponente" — neues
Pattern, semantisch DISTINKT zu HP1630 "Abspringen":
- Bersten = Material-/Druckversagen der Komponente, Mediumaustritt
- Abspringen = Verbindung loest sich, Peitscheneffekt
Bremse-Benchmark GT 1.4 sprach von Bersten, HP1630 nur von
Abspringen — ein 66%-Frontend-Match war eine Sackgasse. Mit
HP1632 feuert die Engine ein eigenes Hazard, das auf GT 1.4
einen sauberen Volltreffer liefert.
C) HP1637 "Einatmen von KSS-Aerosolen" — Massnahmen vervollstaendigt:
Vorher nur M141 (Sicherheitszeichen), neu zusaetzlich M405 (KSS-
Aerosolabsaugung), M418 (AGW-Ueberwachung), M526 (WZM-Tueren
geschlossen waehrend Bearbeitung), M408 (Hautschutzplan).
Klaerungsfrage: "Wurde die Aerosolkonzentration nach Bearbeitungs-
ende messtechnisch ermittelt und mit dem AGW verglichen?"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two-part nachhaltiger fix replacing the previous "fill to 5 mitigations
no matter what" behavior that the GT-Bremse benchmark proved
unfaithful (e.g. HP1625 "scharfe Kanten" returning M005 "Rotations-
bewegung vermeiden" via category fallback; HP1651 "Wiederanlauf
Roboter" returning M054 "Sichere thermische Auslegung" via
mismatched pattern reference).
PART A — Set-based category filter (handlers package):
- acceptableMeasureCategories: replaces 1:1 patternCatToMeasureCat
with a curated set per pattern category, so e.g.
safety_function_failure now accepts software_control measures
(watchdogs, plausibility checks) and emc_hazard accepts both
electrical and software_control measures
- isCategoryCompatible: gate every measure id against the accepted
set before creating a mitigation; mismatches log MEASURE-SKIP
- The old category fallback is REMOVED. A hazard whose pattern has
no category-compatible measure is now created with zero mitigations
and logged as COVERAGE-GAP — the operator must consult an expert.
No more silent invention of generic defaults.
PART B — 235 pattern author-error fixes across 26 files:
- HP040-HP044 (AI): M101/M102/M103 (Auffangwanne/Absauganlage) ->
M133 Anomalieerkennung + M214 Plausibilitaet + M213 Sensor-Redundanz
+ M044 Zweikanalige Steuerung + others
- HP011-HP015, HP104-HP109, HP1085-HP1095, HP1281-HP1334 (electrical):
M001-M005/M054/M061 placeholders -> M481/M482 Isolation +
M511-M522 PE/Schutzleiter/RCD/Hauptschalter
- HP110-HP1331 (material_environmental): M101-M103 -> M384-M395
Brandschutz/Laserschutz + M533/M408 SDB/PSA
- HP800-HP858, HP1178-HP1264 (software/sensor/hmi):
M101/M104 -> M105/M106/M107/M214 SPS/Watchdog/Plausibilitaet
- HP026, HP611-HP1690 (ergonomic): M001/M082 -> M353-M360 +
M530-M532 Hebehilfe/ergonomische Hoehe
- HP201-HP1697 (mechanical): M054/M051 -> M002/M008/M061/M141 +
M487/M488 Tueroeffnung-Stillsetzung/Wiederanlauf
- Plus EMF/Strahlung/Brand/Lärm/Vibration/Kommunikation/Cyber
Coverage shift (Pattern-Author-Fehler bei aktiviertem Set-Filter):
start: 237 patterns with zero category-compatible measures
after Stufe 1A: 5 (AI)
after Stufe 1B: 20 (mechanical Bestand)
after Stufe 1C: 35 (electrical Bestand)
after Stufe 1D: 29 (material_environmental)
after Stufe 1E: 29 (software/sensor/hmi)
after Stufe 1F: 20 (ergonomic)
after Stufe 1G: 80 (thermal/comm/radiation/fire/safety)
final: 0 (28 extended.go/extended2.go duplicates fixed)
New regression tests:
- TestEveryPattern_HasCategoryCompatibleMeasure: every pattern in
collectAllPatterns() must reference at least one category-compatible
measure; gaps must be explicitly listed in AllowlistKnownGaps
(currently empty). Fails CI for any new pattern that drifts.
- TestAcceptableMeasureCategories: pins the set-mapping for the
7 most-bug-prone pattern categories.
- TestIsCategoryCompatible_EmptyMeasureCat: protects legacy entries.
A separate task #11 tracks 58 HP-ID duplicates between
extended.go/extended2.go and cobot.go/press.go/operational.go —
patterns are semantically different and TestGetBuiltinHazardPatterns_-
UniqueIDs misses them because it only checks HP001-HP044.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
KSS, EMV, ESD, DCS, PLR, SIL, HMI, SPS, RCD, LOTO, PSA are
abbreviations that should NOT trigger the relevance filter.
bersten, platzen, abspringen, spritzen, einatmen, ausrutschen,
herabfallen, durchschlaegen, wegschleudern are action words that
appear in many patterns and don't indicate a specific machine.
Fixes: HP1633-HP1675 (KSS patterns) were filtered out because
"kss" was not in the narrative but also not in genericSafetyTerms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Robot cell patterns now fire BEFORE generic patterns (Priority 96-99
vs generic 85-95). This ensures pattern-specific SuggestedMeasureIDs
(M420 for KSS, M410 for Potentialausgleich) reach the hazard.
Removed debug fmt.Println statements.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When multiple patterns match the same category+zone, the first creates
the hazard and later patterns add their SuggestedMeasureIDs to the
existing hazard. This ensures KSS-specific measures (M420) reach the
hazard even if a generic pattern created it first.
seenCatZone changed from map[string]bool to map[string]uuid.UUID
to track which hazard ID was created for each dedupKey.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each hazard now gets measures from its SOURCE PATTERN first
(SuggestedMeasureIDs), then category fallback for remaining slots.
Previously all mechanical hazards got the same generic top-5 measures
(Gefahrstelle eliminieren, Sicherheitsabstaende, Scharfe Kanten...).
Now a KSS-Schlauch hazard gets M420 (Druckfeste Auslegung) first.
SuggestedMeasureIDs added to PatternMatch struct and passed through
from pattern definition to hazard creation to measure assignment.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced category-broadcast logic with per-hazard loop:
each hazard gets up to 5 measures (pattern-suggested first, then
category fallback). Expected: 108 × 5 = max 540 total.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pattern-suggested measures go to all hazards in category (correct).
Category-based fallback only for hazards WITHOUT pattern suggestions
(max 3 per hazard). Prevents 1654 mitigations explosion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
hazardIDsByCategory changed from map[string]uuid.UUID to
map[string][]uuid.UUID — measures are now distributed to every
hazard in a category, not just the last one created.
Previously 94/108 hazards had no measures, now all get them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Store ALL applicable lifecycles (comma-separated) not just first
- Frontend maps internal keys to German labels (normal_operation ->
Automatikbetrieb, maintenance -> Wartung, etc.)
- Show Betroffene Personen in engine detail column
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ApplicableLifecycles field in HazardPattern: patterns now declare which
lifecycle phases the hazard applies to (Output, not just filter)
- Init handler writes first applicable lifecycle into Hazard.LifecyclePhase
- Robot cell patterns HP1600-1601 broadened: "Betrieb, Einrichten, Reinigung,
Wartung, Fehlersuche" instead of only "Teach-Betrieb"
- All robot cell patterns get ApplicableLifecycles for proper phase display
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend:
- hazard_blocks.go: ComputeHazardBlocks() groups hazards by category +
component + zone. Parent = highest risk in group. Children covered by
parent's measures are flagged (no separate assessment needed).
- iace_handler_blocks.go: GET /projects/:id/hazard-blocks endpoint
with summary stats (blocks, covered children, assessments saved)
Frontend:
- HazardBlockView.tsx: Expandable block view with summary cards,
parent-child hierarchy, coverage badges, and "abgedeckt" indicators
- hazards/page.tsx: New "Bloecke" tab alongside "Hazard-Liste" and
"Risikobewertung"
No database schema changes — grouping is computed at runtime.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace machine-specific term blacklist with generic vocabulary overlap:
- Extract significant words (>=5 chars, not generic safety terms) from
pattern zone/scenario
- If pattern has specific words but NONE appear in narrative → filter
- genericSafetyTerms whitelist with ~50 terms that appear in all assessments
- Truly generic approach: works for any machine type without maintenance
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- isPatternRelevant() filters patterns whose zone/scenario mentions
machine-specific terms (extruder, stanzpresse, spielplatz, etc.)
absent from the actual machine narrative
- normalizeZoneKey() clusters similar zones for smarter dedup
(e.g. "Schaltschrank, Sammelschiene" = "Schaltschrank-Innenraum")
- machineSpecificTerms list with 40+ terms for generic filtering
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Erklaerteil-Template fuer Risikobeurteilungen (risk_assessment_template.go)
in PDF-Export, Markdown-Export und Frontend ReportPrintView eingebaut
- Ground Truth Benchmark-System: Datenmodell, Fuzzy-Matching-Engine,
3 API Endpoints (import-gt, benchmark, benchmark/summary)
- Frontend Benchmark-Tab mit Score-Cards, Kategorie-Breakdown,
Hazard-Vergleichstabelle (Zugeordnet/Fehlend/Extra), Business Impact
- Erster Benchmark: 13.3% Coverage (Baseline) gegen 60 GT-Eintraege
- Dedup-Fix: seenCat[cat] -> seenCatZone[cat+zone] erlaubt mehrere
Gefaehrdungen pro Kategorie an verschiedenen Gefahrenstellen
- Komponenten-spezifische Hazard-Namen und Zone-basierte Zuordnung
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Changes the compile flow to always query Master Controls from DB first:
1. doc_check_controls → Mode A (deterministic)
2. LLM generation via Ollama/Claude → Mode B
3. Derive from MC name → fallback
4. Template hardcoded questions → absolute fallback
Previously, templates with pre-defined questions just returned those
without ever hitting the DB. Now MC-compiled questions take priority
and template questions fill gaps for uncovered topics.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
POST /projects/:id/components/:cid/suggest-fms
- Baut FMEA-Experten-Prompt aus Komponentenname + Maschinenkontext
- LLM antwortet mit 5 FMs als JSON (Mode, Effect, S/O/D)
- Fallback auf Bibliotheks-FMs wenn LLM nicht verfuegbar
- Nutzt ProviderRegistry (Ollama primary, Anthropic fallback)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Neues Feld "Einsatzbereich" auf Interview-Seite (Sektion 7) mit 15 Branchen.
Pattern Engine bekommt MachineTypes aus MatchInput → branchenfremde Patterns
(Medizin, Aufzug, Bau etc.) feuern nur wenn die Branche ausgewählt ist.
Refactoring: iace_handler_init.go aufgeteilt in init + init_helpers (LOC-Limit).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- POST /initialize?force=true loescht bestehende Hazards + Mitigations
und erstellt sie neu mit aktuellen Betriebszustaenden
- Orange "Neu initialisieren" Button auf Interview-Seite (mit Confirm-Dialog)
- DeleteHazard Store-Methode (kaskadiert Risk Assessments)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hazards zeigen jetzt farbige Badges mit den Betriebszustaenden die sie
ausgeloest haben (z.B. "Wartung", "Not-Halt"). Mitigations erben die
States ihrer verknuepften Hazards.
Backend: OperationalStates im Function-Feld encodiert (kein DB-Schema),
beim Lesen als operational_states[] JSON-Feld zurueckgegeben.
Frontend: Indigo-Badges in HazardTable + MitigationCard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Betriebszustand-UI saved states to metadata.operational_states but
the initialize handler only read states from the parsed narrative text.
Now merges both sources so the UI selection actually affects which
patterns fire during initialization.
Added integration E2E test that verifies: 2 states → fewer patterns,
9 states → more patterns.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PROBLEM: Cobot-Projekt hatte 52 Pressen-Hazards weil Keywords wie
"stempel" und "stoessel" ohne Maschinentyp-Kontext matchten.
FIX an 3 Stellen:
1. KeywordEntry.MachineTypes — Pressen-Keywords nur fuer press/*_press
2. ParseNarrative(text, machineType) — Parser laedt Maschinentyp aus Projekt
3. HazardPattern.MachineTypes — Pressen-Patterns (HP045-HP058) nur fuer Pressen
Verhindert zukuenftig falsche Zuordnungen bei neuen Kundenprojekten.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>