diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Office/OfficeInstallDetectorTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Office/OfficeInstallDetectorTests.cs index 6193c5db..85fc2ca0 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Office/OfficeInstallDetectorTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Office/OfficeInstallDetectorTests.cs @@ -292,6 +292,8 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Office Assert.Equal(0L, summary["bytesFromPeers"]); Assert.Equal(80, summary["percentFromCacheServer"]); Assert.Equal(20, summary["percentFromCdn"]); + Assert.Equal(83, summary["downloadPercent"]); // 1000 of 1200 declared + Assert.Equal(true, summary["streamingFinished"]); } [Fact] @@ -544,6 +546,8 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Office Assert.Equal(1000L, summary["totalBytesDownloaded"]); Assert.Equal(800L, summary["bytesFromCacheServer"]); Assert.Equal(200L, summary["bytesFromCdn"]); + Assert.Equal(83, summary["downloadPercent"]); + Assert.Equal(true, summary["streamingFinished"]); Assert.Equal(OfficeInstallStateData.StateCompleted, rig.Persistence!.Load()!.State); } diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Telemetry/Office/OfficeInstallDetector.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Telemetry/Office/OfficeInstallDetector.cs index f9147253..e9a5c52f 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Telemetry/Office/OfficeInstallDetector.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Telemetry/Office/OfficeInstallDetector.cs @@ -411,7 +411,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Office data["coreBinariesPresent"] = coreBinariesPresent; // One-time Delivery-Optimization summary (how Office was delivered) — far more useful than // a streaming download-% (which is noise under Connected-Cache + multi-job churn). - if (_peakDo != null) data["doSummary"] = BuildDoSummary(_peakDo); + if (_peakDo != null) data["doSummary"] = BuildDoSummary(_peakDo, snap.StreamingFinished); } var message = BuildMessage(eventType, snap); @@ -467,8 +467,14 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Office /// highest-bytes sample seen. Splits the bytes into Connected-Cache-Server / peer / pure-CDN: on /// MCC-enabled networks BytesFromHttp includes the cache-server bytes, so pure CDN is /// BytesFromHttp - BytesFromCacheServer (see project DO-telemetry notes). + /// + /// aggregateFileSize is the sum of the DO jobs' declared target sizes at the peak + /// instant — NOT a stable "total Office payload". It commonly exceeds totalBytesDownloaded + /// because the completed event fires at core-binary completion while background streaming of the + /// remaining declared content is still in flight (streamingFinished == false). The mirrored + /// downloadPercent + streamingFinished make that gap self-explanatory. /// - private static Dictionary BuildDoSummary(OfficeDoSample d) + private static Dictionary BuildDoSummary(OfficeDoSample d, bool streamingFinished) { long total = d.TotalBytesDownloaded; long cacheServer = d.BytesFromCacheServer; @@ -480,6 +486,8 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Telemetry.Office { { "totalBytesDownloaded", total }, { "aggregateFileSize", d.FileSize }, + { "downloadPercent", (object?)d.DownloadPercent ?? 0 }, + { "streamingFinished", streamingFinished }, { "jobCount", d.JobCount }, { "bytesFromCacheServer", cacheServer }, { "bytesFromPeers", peers },