Files
breakpilot-compliance/ai-compliance-sdk/internal/api/handlers/iace_handler_delta.go
T
Benjamin Admin 9a9a11b248 feat(iace): Sprint 4C — Delta Impact Analysis
Neuer Endpoint POST /projects/:id/delta-analysis:
- Input: aktuelle + vorgeschlagene Aenderung (Components, Energy, States, Roles)
- Output: Diff der Pattern-Matches (added/removed Patterns, Hazards, Measures)
- DeltaMatch() auf PatternEngine: Match(current) vs Match(proposed)
- DeltaResult mit AddedPatterns, RemovedPatterns, Counts, SummaryDE

Beispiel-Output: SPS hinzufuegen → +55 Patterns, +5 Hazard-Kategorien, +17 Massnahmen
Maintenance-State hinzufuegen → +10 Patterns, +2 Hazards, +2 Massnahmen

7 Tests: NoChange, AddComponent, RemoveComponent, AddState, AddRole, Summary, Symmetric

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-10 21:23:46 +02:00

102 lines
3.3 KiB
Go

package handlers
import (
"net/http"
"github.com/breakpilot/ai-compliance-sdk/internal/iace"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
// DeltaAnalysisRequest is the request body for POST /projects/:id/delta-analysis.
type DeltaAnalysisRequest struct {
// Current state — if empty, loaded from project's existing components/energy
CurrentComponents []string `json:"current_components,omitempty"`
CurrentEnergy []string `json:"current_energy,omitempty"`
CurrentStates []string `json:"current_states,omitempty"`
CurrentRoles []string `json:"current_roles,omitempty"`
// Proposed additions
AddComponents []string `json:"add_components,omitempty"`
AddEnergy []string `json:"add_energy,omitempty"`
AddStates []string `json:"add_states,omitempty"`
AddRoles []string `json:"add_roles,omitempty"`
// Proposed removals
RemoveComponents []string `json:"remove_components,omitempty"`
RemoveEnergy []string `json:"remove_energy,omitempty"`
RemoveStates []string `json:"remove_states,omitempty"`
RemoveRoles []string `json:"remove_roles,omitempty"`
}
// DeltaAnalysis handles POST /projects/:id/delta-analysis.
// It computes the impact of a proposed change on the hazard/measure landscape.
func (h *IACEHandler) DeltaAnalysis(c *gin.Context) {
projectID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid project ID"})
return
}
var req DeltaAnalysisRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Build current MatchInput — from request or from project's existing data
currentComps := req.CurrentComponents
currentEnergy := req.CurrentEnergy
currentStates := req.CurrentStates
currentRoles := req.CurrentRoles
_ = projectID // Used for future: auto-load current state from project DB
currentInput := iace.MatchInput{
ComponentLibraryIDs: currentComps,
EnergySourceIDs: currentEnergy,
OperationalStates: currentStates,
HumanRoles: currentRoles,
}
// Build proposed MatchInput = current + additions - removals
proposedComps := applyDelta(currentComps, req.AddComponents, req.RemoveComponents)
proposedEnergy := applyDelta(currentEnergy, req.AddEnergy, req.RemoveEnergy)
proposedStates := applyDelta(currentStates, req.AddStates, req.RemoveStates)
proposedRoles := applyDelta(currentRoles, req.AddRoles, req.RemoveRoles)
proposedInput := iace.MatchInput{
ComponentLibraryIDs: proposedComps,
EnergySourceIDs: proposedEnergy,
OperationalStates: proposedStates,
HumanRoles: proposedRoles,
}
// Run delta analysis
engine := iace.NewPatternEngine()
result := engine.DeltaMatch(currentInput, proposedInput)
c.JSON(http.StatusOK, result)
}
// applyDelta computes: current + additions - removals
func applyDelta(current, add, remove []string) []string {
removeSet := make(map[string]bool)
for _, r := range remove {
removeSet[r] = true
}
result := make([]string, 0, len(current)+len(add))
seen := make(map[string]bool)
for _, s := range current {
if !removeSet[s] && !seen[s] {
result = append(result, s)
seen[s] = true
}
}
for _, s := range add {
if !seen[s] {
result = append(result, s)
seen[s] = true
}
}
return result
}