diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core/SignalAdapters/ImeLogTrackerAdapter.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core/SignalAdapters/ImeLogTrackerAdapter.cs
index 42fba8cc..4ae9bd24 100644
--- a/src/Agent/AutopilotMonitor.Agent.V2.Core/SignalAdapters/ImeLogTrackerAdapter.cs
+++ b/src/Agent/AutopilotMonitor.Agent.V2.Core/SignalAdapters/ImeLogTrackerAdapter.cs
@@ -745,23 +745,25 @@ namespace AutopilotMonitor.Agent.V2.Core.SignalAdapters
{
if (script == null || string.IsNullOrEmpty(script.PolicyId)) return;
- // Outcome classification — phase-aware. The Microsoft IME Health-Script (Proactive
- // Remediation) lifecycle treats the detection scripts as compliance reporters:
- // - detection exit 0 = compliant
- // - detection exit non-zero = non-compliant (script ran perfectly, just reporting state)
- // - remediation exit 0 = remediation succeeded
- // - remediation exit non-zero = remediation script failed (real script crash)
- // - post-detection same as detection (exit non-zero = remediation didn't take, NOT a crash)
- // So a non-compliant detection that gets remediated must NOT count as a script failure
- // in metrics or render as red in the timeline. Only the remediation phase + platform
- // scripts route non-zero exit to script_failed. The cycle-level outcome is conveyed
- // separately via RemediationStatus (2=Remediated, 3=RemediationFailed) on each event.
+ // Outcome classification — three rules in priority order:
+ // 1. stderr present → script_failed. Per user UX preference (debrief 2026-05-11):
+ // consistent failure semantics across script types — any time a script writes
+ // to stderr the user wants visibility, even if exit was 0 and IME reported
+ // compliant. Eliminates the confusing case where a platform script with stderr
+ // shows red but a remediation detection with stderr shows green.
+ // 2. Phase-aware exit handling: detection / post-detection use exit code as a
+ // compliance verdict (not a crash signal), so non-zero exit alone is NOT
+ // failure for those phases. Only remediation phase + platform scripts route
+ // non-zero exit to script_failed.
+ // 3. Defensive: explicit Result=="Failed" → script_failed (V1 parity).
var isHealthScript = IsRemediation(script.ScriptType);
var isHealthComplianceReport = isHealthScript
&& (string.Equals(script.ScriptPart, "detection", StringComparison.OrdinalIgnoreCase)
|| string.Equals(script.ScriptPart, "post-detection", StringComparison.OrdinalIgnoreCase));
- var isFailure = string.Equals(script.Result, "Failed", StringComparison.OrdinalIgnoreCase)
+ var hasStderr = !string.IsNullOrWhiteSpace(script.Stderr);
+ var isFailure = hasStderr
+ || string.Equals(script.Result, "Failed", StringComparison.OrdinalIgnoreCase)
|| (!isHealthComplianceReport
&& script.ExitCode.HasValue && script.ExitCode.Value != 0);
var severity = isFailure ? EventSeverity.Error : EventSeverity.Info;
diff --git a/src/Agent/AutopilotMonitor.Agent.V2/buildcounter.txt b/src/Agent/AutopilotMonitor.Agent.V2/buildcounter.txt
index 9084fe1a..a3888f7a 100644
--- a/src/Agent/AutopilotMonitor.Agent.V2/buildcounter.txt
+++ b/src/Agent/AutopilotMonitor.Agent.V2/buildcounter.txt
@@ -1 +1 @@
-684
+687
diff --git a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt
index d7725305..6fa4b873 100644
--- a/src/Agent/AutopilotMonitor.Agent/buildcounter.txt
+++ b/src/Agent/AutopilotMonitor.Agent/buildcounter.txt
@@ -1 +1 @@
-1300
+1301
diff --git a/src/Web/autopilot-monitor-web/components/ScriptExecutions.tsx b/src/Web/autopilot-monitor-web/components/ScriptExecutions.tsx
index 20d293f5..a8454450 100644
--- a/src/Web/autopilot-monitor-web/components/ScriptExecutions.tsx
+++ b/src/Web/autopilot-monitor-web/components/ScriptExecutions.tsx
@@ -4,10 +4,12 @@ import { useEffect, useMemo, useState } from "react";
import { compareVersions } from "@/utils/bootstrapVersion";
import {
buildScriptItemLabel,
+ getPhaseBadge,
isDetectOnlyRow,
isNonCompliantReport,
mapRemediationStatus,
reduceScriptEvents,
+ scriptItemKey,
STALE_RUNNING_THRESHOLD_SECONDS,
type ScriptInputEvent,
type ScriptItem,
@@ -49,7 +51,7 @@ export default function ScriptExecutions({ events, showScriptOutput, latestBoots
Script Executions
{runningCount > 0 && (
-
+
{runningCount} running
)}
@@ -89,7 +91,7 @@ export default function ScriptExecutions({ events, showScriptOutput, latestBoots
{scripts.map((item) => (
{item.state === "Running" ? (
-