refactor(go/iace): split tech_file_generator, hazard_patterns, models, completeness
Split 4 oversized files (503-679 LOC each) into focused units all under 500 LOC: - tech_file_generator.go → +_prompts, +_prompt_builder, +_fallback - hazard_patterns_extended.go → +_extended2.go (HP074-HP102 extracted) - models.go → +_entities.go, +_api.go (enums / DB entities / API types) - completeness.go → +_gates.go (gate definitions extracted) All files remain in package iace. Zero behavior changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -44,310 +44,6 @@ type CompletenessResult struct {
|
||||
CanExport bool `json:"can_export"`
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Gate Definitions (25 CE Completeness Gates)
|
||||
// ============================================================================
|
||||
|
||||
// buildGateDefinitions returns the full set of 25 CE completeness gate definitions.
|
||||
func buildGateDefinitions() []GateDefinition {
|
||||
return []GateDefinition{
|
||||
// =====================================================================
|
||||
// Onboarding Gates (G01-G08) - Required
|
||||
// =====================================================================
|
||||
{
|
||||
ID: "G01",
|
||||
Category: "onboarding",
|
||||
Label: "Machine identity set",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return ctx.Project != nil && ctx.Project.MachineName != ""
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G02",
|
||||
Category: "onboarding",
|
||||
Label: "Intended use described",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return ctx.Project != nil && ctx.Project.Description != ""
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G03",
|
||||
Category: "onboarding",
|
||||
Label: "Operating limits defined",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return ctx.Project != nil && hasMetadataKey(ctx.Project.Metadata, "operating_limits")
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G04",
|
||||
Category: "onboarding",
|
||||
Label: "Foreseeable misuse documented",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return ctx.Project != nil && hasMetadataKey(ctx.Project.Metadata, "foreseeable_misuse")
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G05",
|
||||
Category: "onboarding",
|
||||
Label: "Component tree exists",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return len(ctx.Components) > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G06",
|
||||
Category: "onboarding",
|
||||
Label: "AI classification done (if applicable)",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
// If no AI present, this gate passes automatically
|
||||
if !ctx.HasAI {
|
||||
return true
|
||||
}
|
||||
return hasClassificationFor(ctx.Classifications, RegulationAIAct)
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G07",
|
||||
Category: "onboarding",
|
||||
Label: "Safety relevance marked",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
for _, comp := range ctx.Components {
|
||||
if comp.IsSafetyRelevant {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G08",
|
||||
Category: "onboarding",
|
||||
Label: "Manufacturer info present",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return ctx.Project != nil && ctx.Project.Manufacturer != ""
|
||||
},
|
||||
},
|
||||
|
||||
// =====================================================================
|
||||
// Pattern Matching Gate (G09) - Recommended
|
||||
// =====================================================================
|
||||
{
|
||||
ID: "G09",
|
||||
Category: "onboarding",
|
||||
Label: "Pattern matching performed",
|
||||
Required: false,
|
||||
Recommended: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return ctx.PatternMatchingPerformed
|
||||
},
|
||||
},
|
||||
|
||||
// =====================================================================
|
||||
// Classification Gates (G10-G13) - Required
|
||||
// =====================================================================
|
||||
{
|
||||
ID: "G10",
|
||||
Category: "classification",
|
||||
Label: "AI Act classification complete",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return hasClassificationFor(ctx.Classifications, RegulationAIAct)
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G11",
|
||||
Category: "classification",
|
||||
Label: "Machinery Regulation check done",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return hasClassificationFor(ctx.Classifications, RegulationMachineryRegulation)
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G12",
|
||||
Category: "classification",
|
||||
Label: "NIS2 check done",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return hasClassificationFor(ctx.Classifications, RegulationNIS2)
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G13",
|
||||
Category: "classification",
|
||||
Label: "CRA check done",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return hasClassificationFor(ctx.Classifications, RegulationCRA)
|
||||
},
|
||||
},
|
||||
|
||||
// =====================================================================
|
||||
// Hazard & Risk Gates (G20-G24) - Required
|
||||
// =====================================================================
|
||||
{
|
||||
ID: "G20",
|
||||
Category: "hazard_risk",
|
||||
Label: "Hazards identified",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return len(ctx.Hazards) > 0
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G21",
|
||||
Category: "hazard_risk",
|
||||
Label: "All hazards assessed",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
if len(ctx.Hazards) == 0 {
|
||||
return false
|
||||
}
|
||||
// Build a set of hazard IDs that have at least one assessment
|
||||
assessedHazards := make(map[string]bool)
|
||||
for _, a := range ctx.Assessments {
|
||||
assessedHazards[a.HazardID.String()] = true
|
||||
}
|
||||
for _, h := range ctx.Hazards {
|
||||
if !assessedHazards[h.ID.String()] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G22",
|
||||
Category: "hazard_risk",
|
||||
Label: "Critical/High risks mitigated",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
// Find all hazards that have a critical or high assessment
|
||||
criticalHighHazards := make(map[string]bool)
|
||||
for _, a := range ctx.Assessments {
|
||||
if a.RiskLevel == RiskLevelCritical || a.RiskLevel == RiskLevelHigh {
|
||||
criticalHighHazards[a.HazardID.String()] = true
|
||||
}
|
||||
}
|
||||
|
||||
// If no critical/high hazards, gate passes
|
||||
if len(criticalHighHazards) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check that every critical/high hazard has at least one mitigation
|
||||
mitigatedHazards := make(map[string]bool)
|
||||
for _, m := range ctx.Mitigations {
|
||||
mitigatedHazards[m.HazardID.String()] = true
|
||||
}
|
||||
|
||||
for hazardID := range criticalHighHazards {
|
||||
if !mitigatedHazards[hazardID] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G23",
|
||||
Category: "hazard_risk",
|
||||
Label: "Mitigations verified",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
// All mitigations must be in a terminal state (verified or rejected).
|
||||
// Planned and implemented mitigations block export — they haven't been
|
||||
// verified yet, so the project cannot be considered complete.
|
||||
if len(ctx.Mitigations) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, m := range ctx.Mitigations {
|
||||
if m.Status != MitigationStatusVerified && m.Status != MitigationStatusRejected {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G24",
|
||||
Category: "hazard_risk",
|
||||
Label: "Residual risk accepted",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
if len(ctx.Assessments) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, a := range ctx.Assessments {
|
||||
if !a.IsAcceptable && a.RiskLevel != RiskLevelLow && a.RiskLevel != RiskLevelNegligible {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
|
||||
// =====================================================================
|
||||
// Evidence Gate (G30) - Recommended
|
||||
// =====================================================================
|
||||
{
|
||||
ID: "G30",
|
||||
Category: "evidence",
|
||||
Label: "Test evidence linked",
|
||||
Required: false,
|
||||
Recommended: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return len(ctx.Evidence) > 0
|
||||
},
|
||||
},
|
||||
|
||||
// =====================================================================
|
||||
// Tech File Gates (G40-G42) - Required for completion
|
||||
// =====================================================================
|
||||
{
|
||||
ID: "G40",
|
||||
Category: "tech_file",
|
||||
Label: "Risk assessment report generated",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return hasTechFileSection(ctx.TechFileSections, "risk_assessment_report")
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G41",
|
||||
Category: "tech_file",
|
||||
Label: "Hazard log generated",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
return hasTechFileSection(ctx.TechFileSections, "hazard_log_combined")
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "G42",
|
||||
Category: "tech_file",
|
||||
Label: "AI documents present (if applicable)",
|
||||
Required: true,
|
||||
CheckFunc: func(ctx *CompletenessContext) bool {
|
||||
// If no AI present, this gate passes automatically
|
||||
if !ctx.HasAI {
|
||||
return true
|
||||
}
|
||||
hasIntendedPurpose := hasTechFileSection(ctx.TechFileSections, "ai_intended_purpose")
|
||||
hasModelDescription := hasTechFileSection(ctx.TechFileSections, "ai_model_description")
|
||||
return hasIntendedPurpose && hasModelDescription
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// CompletenessChecker
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user