Split 6 oversized files (719–882 LOC each) into focused files under 500 LOC: - policy_engine.go → types, loader, eval, gen (4 files) - legal_rag.go → types, client, http, context, scroll (5 files) - ai_act_module.go → module, yaml, obligations (3 files) - nis2_module.go → module, yaml, obligations + shared obligation_yaml_types.go (3+1 files) - financial_policy.go → types, engine (2 files) - dsgvo_module.go → module, yaml, obligations (3 files) All in package ucca, zero exported symbol renames, go test ./internal/ucca/... passes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
package ucca
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
func (m *AIActModule) loadFromYAML() error {
|
|
searchPaths := []string{
|
|
"policies/obligations/ai_act_obligations.yaml",
|
|
filepath.Join(".", "policies", "obligations", "ai_act_obligations.yaml"),
|
|
filepath.Join("..", "policies", "obligations", "ai_act_obligations.yaml"),
|
|
filepath.Join("..", "..", "policies", "obligations", "ai_act_obligations.yaml"),
|
|
"/app/policies/obligations/ai_act_obligations.yaml",
|
|
}
|
|
|
|
var data []byte
|
|
var err error
|
|
for _, path := range searchPaths {
|
|
data, err = os.ReadFile(path)
|
|
if err == nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("AI Act obligations YAML not found: %w", err)
|
|
}
|
|
|
|
var config NIS2ObligationsConfig // Reuse same config structure
|
|
if err := yaml.Unmarshal(data, &config); err != nil {
|
|
return fmt.Errorf("failed to parse AI Act YAML: %w", err)
|
|
}
|
|
|
|
m.convertObligations(config.Obligations)
|
|
m.convertControls(config.Controls)
|
|
m.convertIncidentDeadlines(config.IncidentDeadlines)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *AIActModule) convertObligations(yamlObls []ObligationYAML) {
|
|
for _, y := range yamlObls {
|
|
obl := Obligation{
|
|
ID: y.ID,
|
|
RegulationID: "ai_act",
|
|
Title: y.Title,
|
|
Description: y.Description,
|
|
AppliesWhen: y.AppliesWhen,
|
|
Category: ObligationCategory(y.Category),
|
|
Responsible: ResponsibleRole(y.Responsible),
|
|
Priority: ObligationPriority(y.Priority),
|
|
ISO27001Mapping: y.ISO27001,
|
|
HowToImplement: y.HowTo,
|
|
}
|
|
|
|
for _, lb := range y.LegalBasis {
|
|
obl.LegalBasis = append(obl.LegalBasis, LegalReference{
|
|
Norm: lb.Norm,
|
|
Article: lb.Article,
|
|
})
|
|
}
|
|
|
|
if y.Deadline != nil {
|
|
obl.Deadline = &Deadline{
|
|
Type: DeadlineType(y.Deadline.Type),
|
|
Duration: y.Deadline.Duration,
|
|
}
|
|
if y.Deadline.Date != "" {
|
|
if t, err := time.Parse("2006-01-02", y.Deadline.Date); err == nil {
|
|
obl.Deadline.Date = &t
|
|
}
|
|
}
|
|
}
|
|
|
|
if y.Sanctions != nil {
|
|
obl.Sanctions = &SanctionInfo{
|
|
MaxFine: y.Sanctions.MaxFine,
|
|
PersonalLiability: y.Sanctions.PersonalLiability,
|
|
}
|
|
}
|
|
|
|
for _, e := range y.Evidence {
|
|
obl.Evidence = append(obl.Evidence, EvidenceItem{Name: e, Required: true})
|
|
}
|
|
|
|
m.obligations = append(m.obligations, obl)
|
|
}
|
|
}
|
|
|
|
func (m *AIActModule) convertControls(yamlCtrls []ControlYAML) {
|
|
for _, y := range yamlCtrls {
|
|
ctrl := ObligationControl{
|
|
ID: y.ID,
|
|
RegulationID: "ai_act",
|
|
Name: y.Name,
|
|
Description: y.Description,
|
|
Category: y.Category,
|
|
WhatToDo: y.WhatToDo,
|
|
ISO27001Mapping: y.ISO27001,
|
|
Priority: ObligationPriority(y.Priority),
|
|
}
|
|
m.controls = append(m.controls, ctrl)
|
|
}
|
|
}
|
|
|
|
func (m *AIActModule) convertIncidentDeadlines(yamlDeadlines []IncidentDeadlineYAML) {
|
|
for _, y := range yamlDeadlines {
|
|
deadline := IncidentDeadline{
|
|
RegulationID: "ai_act",
|
|
Phase: y.Phase,
|
|
Deadline: y.Deadline,
|
|
Content: y.Content,
|
|
Recipient: y.Recipient,
|
|
}
|
|
for _, lb := range y.LegalBasis {
|
|
deadline.LegalBasis = append(deadline.LegalBasis, LegalReference{
|
|
Norm: lb.Norm,
|
|
Article: lb.Article,
|
|
})
|
|
}
|
|
m.incidentDeadlines = append(m.incidentDeadlines, deadline)
|
|
}
|
|
}
|