Files
breakpilot-compliance/ai-compliance-sdk/internal/ucca/escalation_models.go
Benjamin Boenisch 4435e7ea0a Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance,
AI-Compliance-SDK, Consent-SDK, Developer-Portal,
PCA-Platform, DSMS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:28 +01:00

287 lines
11 KiB
Go

package ucca
import (
"time"
"github.com/google/uuid"
)
// EscalationLevel represents the escalation level (E0-E3).
type EscalationLevel string
const (
EscalationLevelE0 EscalationLevel = "E0" // Auto-Approve
EscalationLevelE1 EscalationLevel = "E1" // Team-Lead Review
EscalationLevelE2 EscalationLevel = "E2" // DSB Consultation
EscalationLevelE3 EscalationLevel = "E3" // DSB + Legal Review
)
// EscalationStatus represents the status of an escalation.
type EscalationStatus string
const (
EscalationStatusPending EscalationStatus = "pending"
EscalationStatusAssigned EscalationStatus = "assigned"
EscalationStatusInReview EscalationStatus = "in_review"
EscalationStatusApproved EscalationStatus = "approved"
EscalationStatusRejected EscalationStatus = "rejected"
EscalationStatusReturned EscalationStatus = "returned"
)
// EscalationDecision represents the decision made on an escalation.
type EscalationDecision string
const (
EscalationDecisionApprove EscalationDecision = "approve"
EscalationDecisionReject EscalationDecision = "reject"
EscalationDecisionModify EscalationDecision = "modify"
EscalationDecisionEscalate EscalationDecision = "escalate"
)
// Escalation represents an escalation record for a UCCA assessment.
type Escalation struct {
ID uuid.UUID `json:"id" db:"id"`
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
AssessmentID uuid.UUID `json:"assessment_id" db:"assessment_id"`
EscalationLevel EscalationLevel `json:"escalation_level" db:"escalation_level"`
EscalationReason string `json:"escalation_reason" db:"escalation_reason"`
AssignedTo *uuid.UUID `json:"assigned_to,omitempty" db:"assigned_to"`
AssignedRole *string `json:"assigned_role,omitempty" db:"assigned_role"`
AssignedAt *time.Time `json:"assigned_at,omitempty" db:"assigned_at"`
Status EscalationStatus `json:"status" db:"status"`
ReviewerID *uuid.UUID `json:"reviewer_id,omitempty" db:"reviewer_id"`
ReviewerNotes *string `json:"reviewer_notes,omitempty" db:"reviewer_notes"`
ReviewedAt *time.Time `json:"reviewed_at,omitempty" db:"reviewed_at"`
Decision *EscalationDecision `json:"decision,omitempty" db:"decision"`
DecisionNotes *string `json:"decision_notes,omitempty" db:"decision_notes"`
DecisionAt *time.Time `json:"decision_at,omitempty" db:"decision_at"`
Conditions []string `json:"conditions" db:"conditions"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
DueDate *time.Time `json:"due_date,omitempty" db:"due_date"`
NotificationSent bool `json:"notification_sent" db:"notification_sent"`
NotificationSentAt *time.Time `json:"notification_sent_at,omitempty" db:"notification_sent_at"`
}
// EscalationHistory represents an audit trail entry for escalation changes.
type EscalationHistory struct {
ID uuid.UUID `json:"id" db:"id"`
EscalationID uuid.UUID `json:"escalation_id" db:"escalation_id"`
Action string `json:"action" db:"action"`
OldStatus string `json:"old_status,omitempty" db:"old_status"`
NewStatus string `json:"new_status,omitempty" db:"new_status"`
OldLevel string `json:"old_level,omitempty" db:"old_level"`
NewLevel string `json:"new_level,omitempty" db:"new_level"`
ActorID uuid.UUID `json:"actor_id" db:"actor_id"`
ActorRole string `json:"actor_role,omitempty" db:"actor_role"`
Notes string `json:"notes,omitempty" db:"notes"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// DSBPoolMember represents a member of the DSB review pool.
type DSBPoolMember struct {
ID uuid.UUID `json:"id" db:"id"`
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
UserID uuid.UUID `json:"user_id" db:"user_id"`
UserName string `json:"user_name" db:"user_name"`
UserEmail string `json:"user_email" db:"user_email"`
Role string `json:"role" db:"role"`
IsActive bool `json:"is_active" db:"is_active"`
MaxConcurrentReviews int `json:"max_concurrent_reviews" db:"max_concurrent_reviews"`
CurrentReviews int `json:"current_reviews" db:"current_reviews"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// EscalationSLA represents SLA configuration for an escalation level.
type EscalationSLA struct {
ID uuid.UUID `json:"id" db:"id"`
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
EscalationLevel EscalationLevel `json:"escalation_level" db:"escalation_level"`
ResponseHours int `json:"response_hours" db:"response_hours"`
ResolutionHours int `json:"resolution_hours" db:"resolution_hours"`
NotifyOnCreation bool `json:"notify_on_creation" db:"notify_on_creation"`
NotifyOnApproachingSLA bool `json:"notify_on_approaching_sla" db:"notify_on_approaching_sla"`
NotifyOnSLABreach bool `json:"notify_on_sla_breach" db:"notify_on_sla_breach"`
ApproachingSLAHours int `json:"approaching_sla_hours" db:"approaching_sla_hours"`
AutoEscalateOnBreach bool `json:"auto_escalate_on_breach" db:"auto_escalate_on_breach"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// EscalationWithAssessment combines escalation with assessment summary.
type EscalationWithAssessment struct {
Escalation
AssessmentTitle string `json:"assessment_title"`
AssessmentFeasibility string `json:"assessment_feasibility"`
AssessmentRiskScore int `json:"assessment_risk_score"`
AssessmentDomain string `json:"assessment_domain"`
}
// EscalationStats provides statistics for escalations.
type EscalationStats struct {
TotalPending int `json:"total_pending"`
TotalInReview int `json:"total_in_review"`
TotalApproved int `json:"total_approved"`
TotalRejected int `json:"total_rejected"`
ByLevel map[EscalationLevel]int `json:"by_level"`
OverdueSLA int `json:"overdue_sla"`
ApproachingSLA int `json:"approaching_sla"`
AvgResolutionHours float64 `json:"avg_resolution_hours"`
}
// EscalationTrigger contains the logic to determine escalation level.
type EscalationTrigger struct {
// Thresholds
E1RiskThreshold int // Risk score threshold for E1 (default: 20)
E2RiskThreshold int // Risk score threshold for E2 (default: 40)
E3RiskThreshold int // Risk score threshold for E3 (default: 60)
}
// DefaultEscalationTrigger returns the default escalation trigger configuration.
func DefaultEscalationTrigger() *EscalationTrigger {
return &EscalationTrigger{
E1RiskThreshold: 20,
E2RiskThreshold: 40,
E3RiskThreshold: 60,
}
}
// DetermineEscalationLevel determines the appropriate escalation level for an assessment.
func (t *EscalationTrigger) DetermineEscalationLevel(result *AssessmentResult) (EscalationLevel, string) {
reasons := []string{}
// E3: Highest priority checks
// - BLOCK rules triggered
// - Risk > 60
// - Art. 22 risk (automated individual decisions)
hasBlock := false
for _, rule := range result.TriggeredRules {
if rule.Severity == "BLOCK" {
hasBlock = true
reasons = append(reasons, "BLOCK-Regel ausgelöst: "+rule.Code)
break
}
}
if hasBlock || result.RiskScore > t.E3RiskThreshold || result.Art22Risk {
if result.RiskScore > t.E3RiskThreshold {
reasons = append(reasons, "Risiko-Score über 60")
}
if result.Art22Risk {
reasons = append(reasons, "Art. 22 DSGVO Risiko (automatisierte Entscheidungen)")
}
return EscalationLevelE3, joinReasons(reasons, "E3 erforderlich: ")
}
// E2: Medium priority checks
// - Art. 9 data (special categories)
// - DSFA recommended
// - Risk 40-60
hasArt9 := false
for _, rule := range result.TriggeredRules {
if rule.Code == "R-002" || rule.Code == "A-002" { // Art. 9 rules
hasArt9 = true
reasons = append(reasons, "Besondere Datenkategorien (Art. 9 DSGVO)")
break
}
}
if hasArt9 || result.DSFARecommended || result.RiskScore > t.E2RiskThreshold {
if result.DSFARecommended {
reasons = append(reasons, "DSFA empfohlen")
}
if result.RiskScore > t.E2RiskThreshold {
reasons = append(reasons, "Risiko-Score 40-60")
}
return EscalationLevelE2, joinReasons(reasons, "DSB-Konsultation erforderlich: ")
}
// E1: Low priority checks
// - WARN rules triggered
// - Risk 20-40
hasWarn := false
for _, rule := range result.TriggeredRules {
if rule.Severity == "WARN" {
hasWarn = true
reasons = append(reasons, "WARN-Regel ausgelöst")
break
}
}
if hasWarn || result.RiskScore > t.E1RiskThreshold {
if result.RiskScore > t.E1RiskThreshold {
reasons = append(reasons, "Risiko-Score 20-40")
}
return EscalationLevelE1, joinReasons(reasons, "Team-Lead Review erforderlich: ")
}
// E0: Auto-approve
// - Only INFO rules
// - Risk < 20
return EscalationLevelE0, "Automatische Freigabe: Nur INFO-Regeln, niedriges Risiko"
}
// GetDefaultSLA returns the default SLA for an escalation level.
func GetDefaultSLA(level EscalationLevel) (responseHours, resolutionHours int) {
switch level {
case EscalationLevelE0:
return 0, 0 // Auto-approve, no SLA
case EscalationLevelE1:
return 24, 72 // 1 day response, 3 days resolution
case EscalationLevelE2:
return 8, 48 // 8 hours response, 2 days resolution
case EscalationLevelE3:
return 4, 24 // 4 hours response, 1 day resolution (urgent)
default:
return 24, 72
}
}
// GetRoleForLevel returns the required role for reviewing an escalation level.
func GetRoleForLevel(level EscalationLevel) string {
switch level {
case EscalationLevelE0:
return "" // No review needed
case EscalationLevelE1:
return "team_lead"
case EscalationLevelE2:
return "dsb"
case EscalationLevelE3:
return "dsb" // DSB + Legal, but DSB is primary
default:
return "dsb"
}
}
func joinReasons(reasons []string, prefix string) string {
if len(reasons) == 0 {
return prefix
}
result := prefix
for i, r := range reasons {
if i > 0 {
result += "; "
}
result += r
}
return result
}
// CreateEscalationRequest is the request to create an escalation.
type CreateEscalationRequest struct {
AssessmentID uuid.UUID `json:"assessment_id" binding:"required"`
}
// AssignEscalationRequest is the request to assign an escalation.
type AssignEscalationRequest struct {
AssignedTo uuid.UUID `json:"assigned_to" binding:"required"`
}
// DecideEscalationRequest is the request to make a decision on an escalation.
type DecideEscalationRequest struct {
Decision EscalationDecision `json:"decision" binding:"required"`
DecisionNotes string `json:"decision_notes"`
Conditions []string `json:"conditions,omitempty"`
}