diff --git a/pkg/cli/logs_report.go b/pkg/cli/logs_report.go index 1c36afca462..7c4dc202e3f 100644 --- a/pkg/cli/logs_report.go +++ b/pkg/cli/logs_report.go @@ -80,6 +80,7 @@ type RunData struct { Agent string `json:"agent,omitempty" console:"header:Agent,omitempty"` Status string `json:"status" console:"header:Status"` Conclusion string `json:"conclusion,omitempty" console:"-"` + Classification string `json:"classification" console:"-"` Duration string `json:"duration,omitempty" console:"header:Duration,omitempty"` ActionMinutes float64 `json:"action_minutes,omitempty" console:"header:Action Minutes,omitempty"` TokenUsage int `json:"token_usage,omitempty" console:"header:Tokens,format:number,omitempty"` @@ -218,6 +219,7 @@ func buildLogsData(processedRuns []ProcessedRun, outputDir string, continuation Agent: agentID, Status: run.Status, Conclusion: run.Conclusion, + Classification: deriveRunClassification(comparison), TokenUsage: run.TokenUsage, EffectiveTokens: run.EffectiveTokens, EstimatedCost: run.EstimatedCost, @@ -334,6 +336,30 @@ func buildLogsData(processedRuns []ProcessedRun, outputDir string, continuation } } +// deriveRunClassification maps a run's AuditComparisonData to one of four +// human-readable classification labels: +// +// - "risky" – comparison detected a risk signal (e.g. posture change, new MCP failure). +// - "normal" – comparison found no risk signals (stable or minor changes). +// - "baseline" – no prior successful run was available to compare against; +// this run acts as its own baseline. +// - "unclassified" – comparison data is absent or incomplete. +func deriveRunClassification(comparison *AuditComparisonData) string { + if comparison == nil { + return "unclassified" + } + if !comparison.BaselineFound { + return "baseline" + } + if comparison.Classification == nil { + return "unclassified" + } + if comparison.Classification.Label == "risky" { + return "risky" + } + return "normal" +} + // isValidToolName checks if a tool name appears to be valid // Filters out single words, common words, and other garbage that shouldn't be tools func isValidToolName(toolName string) bool { diff --git a/pkg/cli/logs_report_test.go b/pkg/cli/logs_report_test.go index fdc49e65b6c..fa1fa3d93d6 100644 --- a/pkg/cli/logs_report_test.go +++ b/pkg/cli/logs_report_test.go @@ -847,3 +847,64 @@ func TestBuildLogsDataIncludesDateFields(t *testing.T) { t.Errorf("Expected UpdatedAt = %v, got %v", updatedAt, run.UpdatedAt) } } + +// TestDeriveRunClassification tests the classification mapping helper. +func TestDeriveRunClassification(t *testing.T) { + tests := []struct { + name string + comparison *AuditComparisonData + want string + }{ + { + name: "nil comparison returns unclassified", + comparison: nil, + want: "unclassified", + }, + { + name: "no baseline found returns baseline", + comparison: &AuditComparisonData{BaselineFound: false}, + want: "baseline", + }, + { + name: "nil classification with baseline returns unclassified", + comparison: &AuditComparisonData{ + BaselineFound: true, + Classification: nil, + }, + want: "unclassified", + }, + { + name: "risky label returns risky", + comparison: &AuditComparisonData{ + BaselineFound: true, + Classification: &AuditComparisonClassification{Label: "risky"}, + }, + want: "risky", + }, + { + name: "stable label returns normal", + comparison: &AuditComparisonData{ + BaselineFound: true, + Classification: &AuditComparisonClassification{Label: "stable"}, + }, + want: "normal", + }, + { + name: "changed label returns normal", + comparison: &AuditComparisonData{ + BaselineFound: true, + Classification: &AuditComparisonClassification{Label: "changed"}, + }, + want: "normal", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := deriveRunClassification(tt.comparison) + if got != tt.want { + t.Errorf("deriveRunClassification() = %q, want %q", got, tt.want) + } + }) + } +} diff --git a/pkg/cli/mcp_logs_guardrail.go b/pkg/cli/mcp_logs_guardrail.go index 54ccf1df96a..51d5fe49421 100644 --- a/pkg/cli/mcp_logs_guardrail.go +++ b/pkg/cli/mcp_logs_guardrail.go @@ -105,7 +105,7 @@ func getLogsDataSchema() LogsDataSchema { }, "runs": { Type: "array", - Description: "Array of workflow run data (database_id, workflow_name, agent, status, conclusion, duration, token_usage, estimated_cost, turns, error_count, warning_count, missing_tool_count, created_at, url, logs_path, event, branch)", + Description: "Array of workflow run data (database_id, workflow_name, agent, status, conclusion, classification, duration, token_usage, estimated_cost, turns, error_count, warning_count, missing_tool_count, created_at, url, logs_path, event, branch). classification is one of: risky, normal, baseline, unclassified.", }, "tool_usage": { Type: "array",