feat: BetrVG-Compliance-Modul — Obligations, Konflikt-Score, Frontend
1. BetrVG Obligations (JSON V2): 12 Pflichten basierend auf §87, §90, §94, §95, §99, §111 - BAG-Rechtsprechung referenziert (M365, SAP, Standardsoftware) - Applicability: DE + >=5 Mitarbeiter 2. Betriebsrats-Konflikt-Score (0-100): Gewichtete Formel aus 8 Faktoren - Ueberwachungseignung, HR-Bezug, Individualisierbarkeit, Automation - Escalation-Trigger: Score>=50 ohne BR → E2, Score>=75 → E3 3. Frontend: 3 neue Intake-Felder (Monitoring, HR, BR-Konsultation) - BR-Konflikt-Badge in Use-Case-Liste + Detail-Seite - Farbcodierung: gruen/gelb/orange/rot Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package ucca
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -187,6 +188,12 @@ func (t *EscalationTrigger) DetermineEscalationLevel(result *AssessmentResult) (
|
||||
}
|
||||
}
|
||||
|
||||
// BetrVG E3: Very high conflict score without consultation
|
||||
if result.BetrvgConflictScore >= 75 && !result.Intake.WorksCouncilConsulted {
|
||||
reasons = append(reasons, "BetrVG-Konfliktpotenzial sehr hoch (Score "+fmt.Sprintf("%d", result.BetrvgConflictScore)+") ohne BR-Konsultation")
|
||||
return EscalationLevelE3, joinReasons(reasons, "E3 erforderlich: ")
|
||||
}
|
||||
|
||||
if hasArt9 || result.DSFARecommended || result.RiskScore > t.E2RiskThreshold {
|
||||
if result.DSFARecommended {
|
||||
reasons = append(reasons, "DSFA empfohlen")
|
||||
@@ -197,6 +204,12 @@ func (t *EscalationTrigger) DetermineEscalationLevel(result *AssessmentResult) (
|
||||
return EscalationLevelE2, joinReasons(reasons, "DSB-Konsultation erforderlich: ")
|
||||
}
|
||||
|
||||
// BetrVG E2: High conflict score
|
||||
if result.BetrvgConflictScore >= 50 && result.BetrvgConsultationRequired && !result.Intake.WorksCouncilConsulted {
|
||||
reasons = append(reasons, "BetrVG-Mitbestimmung erforderlich (Score "+fmt.Sprintf("%d", result.BetrvgConflictScore)+"), BR nicht konsultiert")
|
||||
return EscalationLevelE2, joinReasons(reasons, "BR-Konsultation erforderlich: ")
|
||||
}
|
||||
|
||||
// E1: Low priority checks
|
||||
// - WARN rules triggered
|
||||
// - Risk 20-40
|
||||
|
||||
@@ -56,6 +56,8 @@ func (m *JSONRegulationModule) defaultApplicability(facts *UnifiedFacts) bool {
|
||||
return facts.Organization.EUMember && facts.AIUsage.UsesAI
|
||||
case "dora":
|
||||
return facts.Financial.DORAApplies || facts.Financial.IsRegulated
|
||||
case "betrvg":
|
||||
return facts.Organization.Country == "DE" && facts.Organization.EmployeeCount >= 5
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -217,6 +217,11 @@ type UseCaseIntake struct {
|
||||
// Only applicable for financial domains (banking, finance, insurance, investment)
|
||||
FinancialContext *FinancialContext `json:"financial_context,omitempty"`
|
||||
|
||||
// BetrVG / works council context (Germany)
|
||||
EmployeeMonitoring bool `json:"employee_monitoring,omitempty"` // System can monitor employee behavior/performance
|
||||
HRDecisionSupport bool `json:"hr_decision_support,omitempty"` // System supports HR decisions (hiring, evaluation, termination)
|
||||
WorksCouncilConsulted bool `json:"works_council_consulted,omitempty"` // Works council has been consulted
|
||||
|
||||
// Opt-in to store raw text (otherwise only hash)
|
||||
StoreRawText bool `json:"store_raw_text,omitempty"`
|
||||
}
|
||||
@@ -471,6 +476,10 @@ type Assessment struct {
|
||||
Art22Risk bool `json:"art22_risk"`
|
||||
TrainingAllowed TrainingAllowed `json:"training_allowed"`
|
||||
|
||||
// BetrVG Conflict Score (0-100) — works council escalation risk
|
||||
BetrvgConflictScore int `json:"betrvg_conflict_score"`
|
||||
BetrvgConsultationRequired bool `json:"betrvg_consultation_required"`
|
||||
|
||||
// Corpus Versioning (RAG)
|
||||
CorpusVersionID *uuid.UUID `json:"corpus_version_id,omitempty"`
|
||||
CorpusVersion string `json:"corpus_version,omitempty"`
|
||||
|
||||
@@ -338,6 +338,9 @@ func (e *PolicyEngine) Evaluate(intake *UseCaseIntake) *AssessmentResult {
|
||||
// Determine complexity
|
||||
result.Complexity = e.calculateComplexity(result)
|
||||
|
||||
// Calculate BetrVG Conflict Score (Germany only, employees >= 5)
|
||||
result.BetrvgConflictScore, result.BetrvgConsultationRequired = e.calculateBetrvgConflictScore(intake)
|
||||
|
||||
// Check if DSFA is recommended
|
||||
result.DSFARecommended = e.shouldRecommendDSFA(intake, result)
|
||||
|
||||
@@ -880,3 +883,70 @@ func categorizeControl(id string) string {
|
||||
}
|
||||
return "organizational"
|
||||
}
|
||||
|
||||
// calculateBetrvgConflictScore computes a works council conflict score (0-100).
|
||||
// Higher score = higher risk of escalation with works council.
|
||||
// Only relevant for German organizations with >= 5 employees.
|
||||
func (e *PolicyEngine) calculateBetrvgConflictScore(intake *UseCaseIntake) (int, bool) {
|
||||
if intake.Domain == "" {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
score := 0
|
||||
consultationRequired := false
|
||||
|
||||
// Factor 1: Employee data processing (+10)
|
||||
if intake.DataTypes.PersonalData && intake.DataTypes.EmployeeData {
|
||||
score += 10
|
||||
consultationRequired = true
|
||||
}
|
||||
|
||||
// Factor 2: System can monitor behavior/performance (+20)
|
||||
if intake.EmployeeMonitoring {
|
||||
score += 20
|
||||
consultationRequired = true
|
||||
}
|
||||
|
||||
// Factor 3: Individualized usage data / logging (+15)
|
||||
if intake.Retention.StorePrompts || intake.Retention.StoreResponses {
|
||||
score += 15
|
||||
}
|
||||
|
||||
// Factor 4: Communication analysis (+10)
|
||||
if intake.Purpose.CustomerSupport || intake.Purpose.Marketing {
|
||||
// These purposes on employee data suggest communication analysis
|
||||
if intake.DataTypes.EmployeeData {
|
||||
score += 10
|
||||
}
|
||||
}
|
||||
|
||||
// Factor 5: HR / Recruiting context (+20)
|
||||
if intake.HRDecisionSupport {
|
||||
score += 20
|
||||
consultationRequired = true
|
||||
}
|
||||
|
||||
// Factor 6: Scoring / Ranking of employees (+10)
|
||||
if intake.Outputs.Rankings || intake.Outputs.Recommendations {
|
||||
if intake.DataTypes.EmployeeData {
|
||||
score += 10
|
||||
}
|
||||
}
|
||||
|
||||
// Factor 7: Fully automated decisions (+10)
|
||||
if intake.Automation == "fully_automated" {
|
||||
score += 10
|
||||
}
|
||||
|
||||
// Factor 8: Works council NOT consulted (+5)
|
||||
if consultationRequired && !intake.WorksCouncilConsulted {
|
||||
score += 5
|
||||
}
|
||||
|
||||
// Cap at 100
|
||||
if score > 100 {
|
||||
score = 100
|
||||
}
|
||||
|
||||
return score, consultationRequired
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user