refactor(go): split training/store, ucca/rules, ucca_handlers, document_export under 500 LOC

Each of the four oversized files (training/store.go 1569 LOC, ucca/rules.go 1231 LOC,
ucca_handlers.go 1135 LOC, document_export.go 1101 LOC) is split by logical group
into same-package files, all under the 500-line hard cap. Zero behavior changes,
no renamed exported symbols. Also fixed pre-existing hazard_library split (missing
functions and duplicate UUID keys from a prior session).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-19 09:29:54 +02:00
parent 3f306fb6f0
commit 9f96061631
36 changed files with 9416 additions and 9365 deletions

View File

@@ -0,0 +1,323 @@
package ucca
func init() {
AllRules = append(AllRules, rulesFJ()...)
}
// rulesFJ returns rules for categories FJ (R-050 to R-100)
func rulesFJ() []Rule {
return []Rule{
// =========================================================================
// F. Hosting (R-050 bis R-052)
// =========================================================================
{
Code: "R-050",
Category: "F. Hosting",
Title: "Third Country Transfer with PII",
TitleDE: "Drittlandtransfer mit PII",
Description: "Transferring PII to third countries requires safeguards",
DescriptionDE: "Übermittlung von PII in Drittländer erfordert Schutzmaßnahmen",
Severity: SeverityWARN,
ScoreDelta: 20,
GDPRRef: "Art. 44-49 DSGVO",
Controls: []string{"C-SCC", "C-ENCRYPTION"},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return intake.Hosting.Region == "third_country" && intake.DataTypes.PersonalData
},
Rationale: func(intake *UseCaseIntake) string {
return "Übermittlung personenbezogener Daten in Drittländer erfordert Standardvertragsklauseln oder andere geeignete Garantien."
},
},
{
Code: "R-051",
Category: "F. Hosting",
Title: "EU Hosting - Compliant",
TitleDE: "EU-Hosting - Konform",
Description: "Hosting within EU is compliant with GDPR",
DescriptionDE: "Hosting innerhalb der EU ist DSGVO-konform",
Severity: SeverityINFO,
ScoreDelta: 0,
GDPRRef: "",
Controls: []string{},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return intake.Hosting.Region == "eu"
},
Rationale: func(intake *UseCaseIntake) string {
return "Hosting innerhalb der EU/EWR erfüllt grundsätzlich die DSGVO-Anforderungen an den Datenstandort."
},
},
{
Code: "R-052",
Category: "F. Hosting",
Title: "On-Premise Hosting",
TitleDE: "On-Premise-Hosting",
Description: "On-premise hosting gives most control",
DescriptionDE: "On-Premise-Hosting gibt die meiste Kontrolle",
Severity: SeverityINFO,
ScoreDelta: 0,
GDPRRef: "",
Controls: []string{"C-ENCRYPTION"},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return intake.Hosting.Region == "on_prem"
},
Rationale: func(intake *UseCaseIntake) string {
return "On-Premise-Hosting bietet maximale Kontrolle über Daten, erfordert aber eigene Sicherheitsmaßnahmen."
},
},
// =========================================================================
// G. Transparenz (R-060 bis R-062)
// =========================================================================
{
Code: "R-060",
Category: "G. Transparenz",
Title: "No Human Review for Decisions",
TitleDE: "Keine menschliche Überprüfung bei Entscheidungen",
Description: "Decisions affecting individuals need human review option",
DescriptionDE: "Entscheidungen, die Personen betreffen, benötigen menschliche Überprüfungsoption",
Severity: SeverityWARN,
ScoreDelta: 15,
GDPRRef: "Art. 22(3) DSGVO",
Controls: []string{"C-HITL", "C-DSR-PROCESS"},
Patterns: []string{"P-HITL-ENFORCED"},
Condition: func(intake *UseCaseIntake) bool {
return (intake.Outputs.LegalEffects || intake.Outputs.AccessDecisions || intake.Purpose.DecisionMaking) &&
intake.Automation != AutomationAssistive
},
Rationale: func(intake *UseCaseIntake) string {
return "Betroffene haben das Recht auf menschliche Überprüfung bei automatisierten Entscheidungen."
},
},
{
Code: "R-061",
Category: "G. Transparenz",
Title: "External Recommendations",
TitleDE: "Externe Empfehlungen",
Description: "Recommendations to users need transparency",
DescriptionDE: "Empfehlungen an Nutzer erfordern Transparenz",
Severity: SeverityINFO,
ScoreDelta: 5,
GDPRRef: "Art. 13/14 DSGVO",
Controls: []string{"C-TRANSPARENCY"},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return intake.Outputs.RecommendationsToUsers && intake.DataTypes.PersonalData
},
Rationale: func(intake *UseCaseIntake) string {
return "Personalisierte Empfehlungen erfordern Information der Nutzer über die KI-Verarbeitung."
},
},
{
Code: "R-062",
Category: "G. Transparenz",
Title: "Content Generation without Disclosure",
TitleDE: "Inhaltsgenerierung ohne Offenlegung",
Description: "AI-generated content should be disclosed",
DescriptionDE: "KI-generierte Inhalte sollten offengelegt werden",
Severity: SeverityINFO,
ScoreDelta: 5,
GDPRRef: "EU-AI-Act Art. 52",
Controls: []string{"C-TRANSPARENCY"},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return intake.Outputs.ContentGeneration
},
Rationale: func(intake *UseCaseIntake) string {
return "KI-generierte Inhalte sollten als solche gekennzeichnet werden (EU-AI-Act Transparenzpflicht)."
},
},
// =========================================================================
// H. Domain-spezifisch (R-070 bis R-074)
// =========================================================================
{
Code: "R-070",
Category: "H. Domain-spezifisch",
Title: "Education + Scoring = Blocked",
TitleDE: "Bildung + Scoring = Blockiert",
Description: "Automated scoring of students is prohibited",
DescriptionDE: "Automatisches Scoring von Schülern ist verboten",
Severity: SeverityBLOCK,
ScoreDelta: 50,
GDPRRef: "Art. 8, Art. 22 DSGVO",
Controls: []string{},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return intake.Domain == DomainEducation &&
intake.DataTypes.MinorData &&
(intake.Purpose.EvaluationScoring || intake.Outputs.RankingsOrScores)
},
Rationale: func(intake *UseCaseIntake) string {
return "Automatisches Scoring oder Ranking von Schülern/Minderjährigen ist aufgrund des besonderen Schutzes unzulässig."
},
},
{
Code: "R-071",
Category: "H. Domain-spezifisch",
Title: "Healthcare + Automated Diagnosis",
TitleDE: "Gesundheit + Automatische Diagnose",
Description: "Automated medical decisions require strict controls",
DescriptionDE: "Automatische medizinische Entscheidungen erfordern strenge Kontrollen",
Severity: SeverityBLOCK,
ScoreDelta: 45,
GDPRRef: "Art. 9, Art. 22 DSGVO",
Controls: []string{"C-HITL", "C-DSFA", "C-ART9-BASIS"},
Patterns: []string{"P-HITL-ENFORCED"},
Condition: func(intake *UseCaseIntake) bool {
return intake.Domain == DomainHealthcare &&
intake.Automation == AutomationFullyAutomated &&
intake.Purpose.DecisionMaking
},
Rationale: func(intake *UseCaseIntake) string {
return "Vollautomatisierte medizinische Diagnosen oder Behandlungsentscheidungen sind ohne ärztliche Überprüfung unzulässig."
},
},
{
Code: "R-072",
Category: "H. Domain-spezifisch",
Title: "Finance + Automated Credit Scoring",
TitleDE: "Finanzen + Automatisches Credit-Scoring",
Description: "Automated credit decisions require transparency",
DescriptionDE: "Automatische Kreditentscheidungen erfordern Transparenz",
Severity: SeverityWARN,
ScoreDelta: 20,
GDPRRef: "Art. 22 DSGVO",
Controls: []string{"C-HITL", "C-TRANSPARENCY", "C-DSR-PROCESS"},
Patterns: []string{"P-HITL-ENFORCED"},
Condition: func(intake *UseCaseIntake) bool {
return intake.Domain == DomainFinance &&
(intake.Purpose.EvaluationScoring || intake.Outputs.RankingsOrScores) &&
intake.DataTypes.FinancialData
},
Rationale: func(intake *UseCaseIntake) string {
return "Automatische Kreditwürdigkeitsprüfung erfordert Erklärbarkeit und Widerspruchsmöglichkeit."
},
},
{
Code: "R-073",
Category: "H. Domain-spezifisch",
Title: "Utilities + RAG Chatbot = Low Risk",
TitleDE: "Versorgungsunternehmen + RAG-Chatbot = Niedriges Risiko",
Description: "RAG-based customer service chatbot is low risk",
DescriptionDE: "RAG-basierter Kundenservice-Chatbot ist risikoarm",
Severity: SeverityINFO,
ScoreDelta: 0,
GDPRRef: "",
Controls: []string{},
Patterns: []string{"P-RAG-ONLY"},
Condition: func(intake *UseCaseIntake) bool {
return intake.Domain == DomainUtilities &&
intake.ModelUsage.RAG &&
intake.Purpose.CustomerSupport &&
!intake.DataTypes.PersonalData
},
Rationale: func(intake *UseCaseIntake) string {
return "Ein RAG-basierter Kundenservice-Chatbot ohne Speicherung personenbezogener Daten ist ein Best-Practice-Beispiel."
},
},
{
Code: "R-074",
Category: "H. Domain-spezifisch",
Title: "Public Sector + Automated Decisions",
TitleDE: "Öffentlicher Sektor + Automatische Entscheidungen",
Description: "Public sector automated decisions need special care",
DescriptionDE: "Automatische Entscheidungen im öffentlichen Sektor erfordern besondere Sorgfalt",
Severity: SeverityWARN,
ScoreDelta: 20,
GDPRRef: "Art. 22 DSGVO",
Controls: []string{"C-HITL", "C-TRANSPARENCY", "C-DSFA"},
Patterns: []string{"P-HITL-ENFORCED"},
Condition: func(intake *UseCaseIntake) bool {
return intake.Domain == DomainPublic &&
intake.Purpose.DecisionMaking &&
intake.Automation != AutomationAssistive
},
Rationale: func(intake *UseCaseIntake) string {
return "Verwaltungsentscheidungen, die Bürger betreffen, erfordern besondere Transparenz und Überprüfungsmöglichkeiten."
},
},
// =========================================================================
// I. Aggregation (R-090 bis R-092) - Implicit in Evaluate()
// =========================================================================
{
Code: "R-090",
Category: "I. Aggregation",
Title: "Block Rules Triggered",
TitleDE: "Blockierungsregeln ausgelöst",
Description: "Any BLOCK severity results in NO feasibility",
DescriptionDE: "Jede BLOCK-Schwere führt zu NEIN-Machbarkeit",
Severity: SeverityBLOCK,
ScoreDelta: 0,
GDPRRef: "",
Controls: []string{},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return false // handled in aggregation logic
},
Rationale: func(intake *UseCaseIntake) string {
return "Eine oder mehrere kritische Regelverletzungen führen zur Einstufung als nicht umsetzbar."
},
},
{
Code: "R-091",
Category: "I. Aggregation",
Title: "Warning Rules Only",
TitleDE: "Nur Warnungsregeln",
Description: "Only WARN severity results in CONDITIONAL",
DescriptionDE: "Nur WARN-Schwere führt zu BEDINGT",
Severity: SeverityWARN,
ScoreDelta: 0,
GDPRRef: "",
Controls: []string{},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return false // handled in aggregation logic
},
Rationale: func(intake *UseCaseIntake) string {
return "Warnungen erfordern Maßnahmen, blockieren aber nicht die Umsetzung."
},
},
{
Code: "R-092",
Category: "I. Aggregation",
Title: "Info Only - Clear Path",
TitleDE: "Nur Info - Freier Weg",
Description: "Only INFO severity results in YES",
DescriptionDE: "Nur INFO-Schwere führt zu JA",
Severity: SeverityINFO,
ScoreDelta: 0,
GDPRRef: "",
Controls: []string{},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return false // handled in aggregation logic
},
Rationale: func(intake *UseCaseIntake) string {
return "Keine kritischen oder warnenden Regeln ausgelöst - Umsetzung empfohlen."
},
},
// =========================================================================
// J. Erklärung (R-100)
// =========================================================================
{
Code: "R-100",
Category: "J. Erklärung",
Title: "Rejection Must Include Reason and Alternative",
TitleDE: "Ablehnung muss Begründung und Alternative enthalten",
Description: "When feasibility is NO, provide reason and alternative",
DescriptionDE: "Bei Machbarkeit NEIN, Begründung und Alternative angeben",
Severity: SeverityINFO,
ScoreDelta: 0,
GDPRRef: "",
Controls: []string{},
Patterns: []string{},
Condition: func(intake *UseCaseIntake) bool {
return false // handled in summary generation
},
Rationale: func(intake *UseCaseIntake) string {
return "Jede Ablehnung enthält eine klare Begründung und einen alternativen Ansatz."
},
},
}
}