Files
breakpilot-compliance/ai-compliance-sdk/internal/iace/architecture.go
T
Benjamin Admin 32ba8d16b1 feat(iace): add data-driven Architektur & Datenfluss explainer tab
Adds an auditor-facing view of the IACE engine: a clickable 10-stage
pipeline flow (Grenzen-Formular → ParseNarrative → Pattern-Gates →
Relevanz → Caps → Gefährdungen → Maßnahmen → Risiko → Normen → Matrix),
plus live library counts, the data-source/license register (incl. the
DIN/Beuth + DGUV exclusions), and the norm-matching logic that reconciles
DIN/ISO/OSHA machine-type vocabulary via canonicalMachineType folding.

Backend: BuildArchitecture() with LIVE counts so the diagram can never
drift; GET /iace/architecture; collectAllNorms() extracted from
SuggestNorms as the single source of truth for the norm-library count.
Frontend: useArchitecture hook + page + new IACE nav tab.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-11 09:35:37 +02:00

167 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package iace
// Data-driven self-description of the IACE engine for the "Architektur &
// Datenfluss" explainer. Counts are LIVE (derived from the running engine) so
// the diagram can never drift from reality; the stage/source prose is curated.
// Purpose: let an auditor see WHERE each datum comes from and HOW a risk
// assessment is reached — every gate, library and data source, in order.
// ArchStage is one step of the deterministic pipeline.
type ArchStage struct {
ID string `json:"id"`
Title string `json:"title"`
Summary string `json:"summary"`
Input string `json:"input"`
Logic string `json:"logic"`
DataSource string `json:"data_source"` // code/library it draws from
Example string `json:"example"`
}
// ArchLibrary is one knowledge base with a LIVE entry count.
type ArchLibrary struct {
Name string `json:"name"`
Count int `json:"count"`
SourceFile string `json:"source_file"`
Description string `json:"description"`
}
// ArchDataSource is an external statistic/standard with its license + status.
type ArchDataSource struct {
Name string `json:"name"`
License string `json:"license"`
Usage string `json:"usage"`
Status string `json:"status"` // "verwendet" | "ausgeschlossen"
}
// Architecture is the full self-description returned by GET /iace/architecture.
type Architecture struct {
Stages []ArchStage `json:"stages"`
Libraries []ArchLibrary `json:"libraries"`
DataSources []ArchDataSource `json:"data_sources"`
NormMatching []string `json:"norm_matching"`
Evidence []RiskEvidence `json:"evidence"`
}
// distinctDomainGates counts the distinct dom_* capability tags the engine gates on.
func distinctDomainGates() int {
seen := map[string]bool{}
for _, tag := range domainGateTerms {
seen[tag] = true
}
return len(seen)
}
// BuildArchitecture assembles the engine self-description with live counts.
func BuildArchitecture() Architecture {
return Architecture{
Stages: []ArchStage{
{
ID: "grenzen", Title: "1 · Grenzen-Formular",
Summary: "Maschinenbeschreibung nach EN ISO 12100 (Verwendungs-, räumliche, zeitliche Grenzen).",
Input: "17 Felder: Beschreibung, Verwendung, Fehlanwendung, Schnittstellen (elektrisch/mechanisch/pneumatisch-hydraulisch), Umgebung, Personen …",
Logic: "Alle Felder werden zu einer Narrative zusammengeführt (kein Whitelist — jedes Feld ist eine potenzielle Gefährdungsquelle).",
DataSource: "project.metadata.limits_form",
Example: "„Hubantrieb über Kette … 230 V … keine pneumatischen Schnittstellen.“",
},
{
ID: "parse", Title: "2 · ParseNarrative",
Summary: "Deterministische Extraktion von Komponenten, Energiequellen, Domänen-Tags und Negationen.",
Input: "Narrative-Text + Maschinentyp",
Logic: "Keyword-Wörterbuch (Substring, umlaut-normalisiert) → Komponenten + Energie + dom_*-Tags. Negation („keine Pneumatik“) ⇒ Komponente als verneint markiert, liefert KEINE Tags.",
DataSource: "keyword_dictionary.go",
Example: "„Kette“→Komponente, „230 V“→electrical_energy, „Presse“→dom_press.",
},
{
ID: "match", Title: "3 · Pattern-Engine (Gates)",
Summary: "Jedes Gefährdungsmuster wird gegen die Maschine geprüft — harte UND-Gates.",
Input: "Komponenten-Tags, Energie-Tags, Lebensphasen, Maschinentyp, dom_*-Tags",
Logic: "patternMatches: MachineType ∧ Required-Component-Tags ∧ Required-Energy-Tags ∧ Lifecycle ∧ Operational-States. Capability-Domain-Gates (dom_*) verhindern Cross-Domänen-Leaks (z. B. Schwimmbad-Muster feuert nicht für eine Presse). Default-open, wenn ein Gate-Input leer ist.",
DataSource: "pattern_engine.go + pattern_domain_gates.go + hazard_patterns_*.go",
Example: "Presse-Muster feuert nur, wenn machine_type∈Presse-Familie UND high_force-Tag vorhanden.",
},
{
ID: "relevance", Title: "4 · Relevanz-Backstop",
Summary: "Generischer Filter gegen Rest-Leaks ungegateter Muster.",
Input: "Gefeuertes Muster + Narrative + Komponenten-Namen",
Logic: "IsPatternRelevant: Token-Grenzen + Stoppwort-Liste — ein Muster wird verworfen, wenn sein spezifischer Anker nicht in der Narrative vorkommt.",
DataSource: "pattern_relevance.go",
Example: "Verwirft „Bandsäge“-Hazard, wenn die Narrative keine Säge nennt.",
},
{
ID: "caps", Title: "5 · Kategorie-Caps",
Summary: "Begrenzung der Gefährdungen je Kategorie (skaliert mit Komponentenzahl).",
Input: "Gefeuerte Muster je Gefährdungskategorie",
Logic: "categoryHazardCap: pro Kategorie ein Maximum (verhindert Über-Flutung); Dedupe über Kategorie × Zone.",
DataSource: "iace_handler_init.go",
Example: "max. N mechanical_hazard-Gefährdungen je Projekt.",
},
{
ID: "hazards", Title: "6 · Gefährdungen",
Summary: "Die erzeugten Gefährdungen (Szenario, Auslöser, Schaden, Zone, betroffene Person).",
Input: "Überlebende Muster + zugeordnete Komponente",
Logic: "Pro Muster: Szenario/Trigger/Harm/Zone aus dem Muster; Komponentenzuordnung tag-basiert (pickComponentForPattern).",
DataSource: "iace_hazards (DB)",
Example: "„Quetschen im Werkzeugeinbauraum zwischen Ober- und Unterwerkzeug.“",
},
{
ID: "measures", Title: "7 · Maßnahmen",
Summary: "Schutzmaßnahmen je Gefährdung — kategorie-gefiltert, KEINE generischen Defaults.",
Input: "Gefährdung + musterspezifische Suggested-Measure-IDs",
Logic: "Nur Maßnahmen, deren Kategorie zur Gefährdung passt (isCategoryCompatible). Ohne passende Maßnahme ⇒ 0 Maßnahmen + Coverage-Gap (ehrlich, statt Unsinn).",
DataSource: "measures_library*.go",
Example: "Sharp-edge-Gefährdung ⇒ keine „Rotation vermeiden“-Maßnahme.",
},
{
ID: "risk", Title: "8 · Risiko (S/F/W/P + Konfidenz)",
Summary: "Konfidenz-bewusste Risikoschätzung je Gefährdung — als Bereich, nicht Punktwert.",
Input: "Gefährdungskategorie + Szenario (Kontaktart) + Lebensphasen",
Logic: "EstimateSeverity/Frequency/ProbabilityW/AvoidabilityP → R = S×(F+W+P), Band + Bereich (±1 je validierter Genauigkeit) + Konfidenz (Verletzungsmechanismus eindeutig?). W verankert am ESAW-Kontaktmodus-Ranking; eigenes Modell, KEINE Norm-Tabelle.",
DataSource: "risk_estimation.go + risk_data_sources.go (ESAW, CC BY 4.0)",
Example: "Elektrischer Schlag: R≈32 (Bereich 2145, mittelkritisch), Konfidenz hoch.",
},
{
ID: "norms", Title: "9 · Normen (A/B/C + Familien-Matching)",
Summary: "Passende Normen je Maschinentyp und Gefährdung; DIN/ISO/OSHA-Vokabular versöhnt.",
Input: "Maschinentyp + Gefährdungskategorien + Tags",
Logic: "SuggestNorms: C-Normen exakt per Maschinentyp-FAMILIE (canonicalMachineType: welding_machine→welding); B-Normen per Gefährdungskategorie/Tags; A-Normen gelten immer. Normen werden nur referenziert, Tabellen nie reproduziert.",
DataSource: "norms_engine.go + machine_type_families.go + norms_library*.go",
Example: "Schweißanlage ⇒ EN 60974-x (Lichtbogenschweißen), obwohl Norm auf „welding_machine“ getaggt.",
},
{
ID: "matrix", Title: "10 · Risiko-Matrix / GT-Benchmark",
Summary: "Projektweite Risiko-Matrix (Schwere × Wahrscheinlichkeit) und Abgleich gegen Experten-Ground-Truth.",
Input: "Alle Gefährdungen + (optional) GT-Projekt",
Logic: "BuildRiskMatrix aggregiert je Zelle; Benchmark vergleicht Tool-S/F/W/P + Fine-Kinney gegen Fachmann-GT (Übereinstimmung within±1, Rang-Konkordanz).",
DataSource: "risk_matrix.go + risk_benchmark.go",
Example: "Kistenhub vs. eigene GT: S±1 94 %, Ranking 86 %.",
},
},
Libraries: []ArchLibrary{
{Name: "Hazard-Pattern-Bibliothek", Count: len(AllPatterns()), SourceFile: "hazard_patterns_*.go", Description: "Gefährdungsmuster mit Gates (MachineType/Tags/Energy/Lifecycle) + Szenario/Trigger/Harm/Zone."},
{Name: "Maßnahmen-Bibliothek", Count: len(GetProtectiveMeasureLibrary()), SourceFile: "measures_library*.go", Description: "Schutzmaßnahmen mit Reduktionstyp + Norm-Referenzen, kategorie-gefiltert."},
{Name: "Normen-Bibliothek (A/B/C)", Count: len(collectAllNorms()), SourceFile: "norms_library*.go", Description: "A-/B-/C-Normen mit Maschinentypen, Gefährdungskategorien und Tags."},
{Name: "Komponenten-Bibliothek", Count: len(GetComponentLibrary()), SourceFile: "component_library.go", Description: "Bauteiltypen mit Capability-Tags für das Pattern-Gating."},
{Name: "Energiequellen", Count: len(GetEnergySources()), SourceFile: "component_library.go", Description: "Energiearten (elektrisch/pneumatisch/hydraulisch …) für Energie-Gates."},
{Name: "Maschinentyp-Vokabular", Count: len(MachineTypeVocabulary()), SourceFile: "machine_types.go", Description: "Kanonische Dropdown-Maschinentypen, auf die Patterns gaten."},
{Name: "Domänen-Capability-Gates", Count: distinctDomainGates(), SourceFile: "pattern_domain_gates.go", Description: "dom_*-Tags, die domänenspezifische Muster auf ihre echte Maschine begrenzen (Leak-Schutz)."},
{Name: "Kontaktmodus-Tiers", Count: len(contactModeTable), SourceFile: "risk_estimation.go", Description: "Verletzungsmechanismen mit W/P/S-Tiers (ESAW-verankert, GT-kalibriert)."},
{Name: "Kontaktmodus-Evidenz", Count: len(contactModeEvidence), SourceFile: "risk_data_sources.go", Description: "Belegte öffentliche Statistik-Quoten (ESAW) als Zitat-/Audit-Schicht."},
},
DataSources: []ArchDataSource{
{Name: "Eurostat ESAW (Kontaktmodus-Unfallstatistik)", License: "CC BY 4.0", Usage: "Anker für Wahrscheinlichkeits-Tiers (W) + zitierbare Quoten", Status: "verwendet"},
{Name: "US BLS / OSHA (Arbeitsunfälle)", License: "Public Domain", Usage: "Ergänzende Häufigkeits-/Schwere-Anker + OSHA-Maßnahmen", Status: "verwendet"},
{Name: "UK HSE (RIDDOR)", License: "Open Government Licence v3", Usage: "Zulässige Ergänzung (Attribution)", Status: "verwendet"},
{Name: "DGUV-Statistik", License: "nur redaktionell, keine Bearbeitung", Usage: "—", Status: "ausgeschlossen"},
{Name: "DIN/Beuth/ISO/IEC Risikograph-Tabellen", License: "urheberrechtlich", Usage: "Nur als Referenz genannt, NIE reproduziert/re-implementiert", Status: "ausgeschlossen"},
},
NormMatching: []string{
"C-Normen (maschinenspezifisch): Match nur über die kanonische Maschinentyp-FAMILIE — `canonicalMachineType` faltet das feingranulare Normen-Vokabular (455 Keys: welding_machine, band_saw, mobile_crane …) auf die 68 Dropdown-Keys. Ohne Familien-Match wird die C-Norm verworfen (kein Tag/Kategorie-Fallback → keine Fremd-Domänen-Normen).",
"B-Normen (gefährdungsspezifisch): Match über Gefährdungskategorie und Komponenten-/Energie-Tags.",
"A-Normen (Grundnormen): gelten immer (z. B. EN ISO 12100).",
"DIN/ISO/OSHA-Versöhnung: Normen tragen teils OSHA-/ISO-/DIN-nahe Maschinen-Keys; die Familien-Faltung sorgt dafür, dass z. B. eine „welding_machine“-Norm für eine „welding“-Maschine matched.",
"Lizenz-Leitplanke: Norm-Tabellen/Risikographen werden NIE reproduziert — nur Norm-Referenzen ausgegeben.",
},
Evidence: AllRiskEvidence(),
}
}