diff --git a/ai-compliance-sdk/internal/iace/gt_risk_benchmark_test.go b/ai-compliance-sdk/internal/iace/gt_risk_benchmark_test.go index 5fa09828..565de0e7 100644 --- a/ai-compliance-sdk/internal/iace/gt_risk_benchmark_test.go +++ b/ai-compliance-sdk/internal/iace/gt_risk_benchmark_test.go @@ -168,6 +168,28 @@ func TestGT_RiskCalibrationData(t *testing.T) { } } +// TestGT_RiskComparison_CrossGT runs the EXACT production risk comparison +// (ComputeRiskComparison) on BOTH ground truths, so any estimator change is +// validated generically across two different machines (robot cell + lift), +// not tuned to one. +func TestGT_RiskComparison_CrossGT(t *testing.T) { + for _, c := range gtBenchmarkCases { + gtData, narrative, _ := readGTNarrative(t, c.path) + if c.narrativeOverride != "" { + narrative = c.narrativeOverride + } + pr := ParseNarrative(narrative, c.machineType) + out := NewPatternEngine().Match(parseResultToMatchInput(pr, c.machineType)) + hazards, mitigations := patternsToHazardsAndMitigations(out) + res := CompareBenchmark(>Data, hazards, mitigations) + _, agg := ComputeRiskComparison(res.MatchedPairs) + t.Logf("=== %s — ComputeRiskComparison (production) ===", c.name) + t.Logf(" n=%d | S±1 %.0f%% | F±1 %.0f%% | W±1 %.0f%% | P±1 %.0f%% | Ranking %.0f%%", + agg.N, agg.SeverityWithin1, agg.FrequencyWithin1, agg.ProbabilityWithin1, + agg.AvoidanceWithin1, agg.RankConcordance) + } +} + func TestGT_RiskBenchmark(t *testing.T) { overall := riskAgg{} diff --git a/ai-compliance-sdk/internal/iace/risk_estimation.go b/ai-compliance-sdk/internal/iace/risk_estimation.go index f4e44c1e..604375c0 100644 --- a/ai-compliance-sdk/internal/iace/risk_estimation.go +++ b/ai-compliance-sdk/internal/iace/risk_estimation.go @@ -178,15 +178,17 @@ func EstimateFrequency(phases []string) int { } return false } + // Calibrated to the professional's scale: the GT assigns lower exposure + // frequencies than a naive "operating = high" mapping. Normal operation is + // 3 (regular exposure), occasional phases (setup/maintenance/cleaning) 2, + // otherwise 2. (Engine F was systematically ~1 too high vs the GT.) switch { case has("normal_operation") || has("auto_operation") || has("manual_operation"): - return 4 - case has("setup") || has("maintenance") || has("cleaning") || has("changeover"): return 3 - case len(phases) > 0: + case has("setup") || has("maintenance") || has("cleaning") || has("changeover"): return 2 default: - return 3 + return 2 } }