| 337 | } |
| 338 | |
| 339 | func buildAuditComparison(current auditComparisonSnapshot, baselineRun *WorkflowRun, baseline *auditComparisonSnapshot) *AuditComparisonData { |
| 340 | if baselineRun == nil || baseline == nil { |
| 341 | return &AuditComparisonData{BaselineFound: false} |
| 342 | } |
| 343 | |
| 344 | reasonCodes := make([]string, 0, 4) |
| 345 | delta := &AuditComparisonDelta{ |
| 346 | Turns: AuditComparisonIntDelta{ |
| 347 | Before: baseline.Turns, |
| 348 | After: current.Turns, |
| 349 | Changed: baseline.Turns != current.Turns, |
| 350 | }, |
| 351 | Posture: AuditComparisonStringDelta{ |
| 352 | Before: baseline.Posture, |
| 353 | After: current.Posture, |
| 354 | Changed: baseline.Posture != current.Posture, |
| 355 | }, |
| 356 | BlockedRequests: AuditComparisonIntDelta{ |
| 357 | Before: baseline.BlockedRequests, |
| 358 | After: current.BlockedRequests, |
| 359 | Changed: baseline.BlockedRequests != current.BlockedRequests, |
| 360 | }, |
| 361 | } |
| 362 | |
| 363 | if current.Turns > baseline.Turns { |
| 364 | reasonCodes = append(reasonCodes, "turns_increase") |
| 365 | } else if current.Turns < baseline.Turns { |
| 366 | reasonCodes = append(reasonCodes, "turns_decrease") |
| 367 | } |
| 368 | if baseline.Posture != current.Posture { |
| 369 | reasonCodes = append(reasonCodes, "posture_changed") |
| 370 | } |
| 371 | if current.BlockedRequests > baseline.BlockedRequests { |
| 372 | reasonCodes = append(reasonCodes, "blocked_requests_increase") |
| 373 | } else if current.BlockedRequests < baseline.BlockedRequests { |
| 374 | reasonCodes = append(reasonCodes, "blocked_requests_decrease") |
| 375 | } |
| 376 | |
| 377 | newMCPFailure := len(baseline.MCPFailures) == 0 && len(current.MCPFailures) > 0 |
| 378 | mcpFailuresResolved := len(baseline.MCPFailures) > 0 && len(current.MCPFailures) == 0 |
| 379 | if newMCPFailure || len(baseline.MCPFailures) > 0 || len(current.MCPFailures) > 0 { |
| 380 | delta.MCPFailure = &AuditComparisonMCPFailureDelta{ |
| 381 | Before: baseline.MCPFailures, |
| 382 | After: current.MCPFailures, |
| 383 | NewlyPresent: newMCPFailure, |
| 384 | } |
| 385 | } |
| 386 | if newMCPFailure { |
| 387 | reasonCodes = append(reasonCodes, "new_mcp_failure") |
| 388 | } else if mcpFailuresResolved { |
| 389 | reasonCodes = append(reasonCodes, "mcp_failures_resolved") |
| 390 | } |
| 391 | |
| 392 | label := "stable" |
| 393 | switch { |
| 394 | case delta.Posture.Before == "read_only" && delta.Posture.After == "write_capable": |
| 395 | label = "risky" |
| 396 | case newMCPFailure: |