diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/SignalActivityClassifierTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/SignalActivityClassifierTests.cs index 785d1860..825c7f2b 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/SignalActivityClassifierTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Orchestration/SignalActivityClassifierTests.cs @@ -37,6 +37,12 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Orchestration [InlineData("modern_deployment_log")] // Environment observation — battery drain on an idle device must not reset idle clocks. [InlineData("power_state_change")] + // A flapping link is environment too: it must neither keep the collectors awake nor + // reset the stall clock of the enrollment it is stalling. + [InlineData("network_state_change")] + [InlineData("network_connectivity_check")] + // Bookkeeping of health-script runs that were deliberately not reported. + [InlineData("script_recurrence_summary")] // Agent health / control / transport — not device/enrollment progress (P2). [InlineData("collector_degraded")] [InlineData("telemetry_upload_poisoned")] @@ -67,6 +73,18 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Orchestration Assert.True(SignalActivityClassifier.IsRealActivity(DecisionSignalKind.InformationalEvent, payload)); } + [Theory] + // Only the link-change pair is exempt. The device-info snapshots are emitted at start + // and at the end, not per flap, and stay activity. + [InlineData("network_interface_info")] + [InlineData("wifi_signal_info")] + [InlineData("network_bandwidth_estimate")] + public void Other_network_events_remain_activity(string eventType) + { + var payload = new Dictionary { [SignalPayloadKeys.EventType] = eventType }; + Assert.True(SignalActivityClassifier.IsRealActivity(DecisionSignalKind.InformationalEvent, payload)); + } + [Theory] // Genuine ModernDeployment warnings/errors stay real activity — only the Info/Debug // `modern_deployment_log` forwarding is excluded. diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core/Orchestration/SignalActivityClassifier.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core/Orchestration/SignalActivityClassifier.cs index 5f355e0e..432ddd84 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core/Orchestration/SignalActivityClassifier.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core/Orchestration/SignalActivityClassifier.cs @@ -61,6 +61,15 @@ namespace AutopilotMonitor.Agent.V2.Core.Orchestration // detection — the exact scenario the power watcher exists to observe. SharedConstants.EventTypes.PowerStateChange, + // Same for the network link: a dock or access point that flaps for hours (field: + // 288 lost/connected pairs in 11 h, each with its connectivity check) is not + // enrollment progress. Counted as activity it kept the periodic collectors running + // for the whole episode and hid the stall it was causing, because every flap reset + // the stall clock. Progress that follows a reconnect arrives as its own download, + // app or phase event. + SharedConstants.EventTypes.NetworkStateChange, + SharedConstants.EventTypes.NetworkConnectivityCheck, + // Same reasoning for the system timeline watcher: a clock step, a wake from standby, // or a backfilled episode at agent start is environment observation and must not // reset the idle/stall clocks.