Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Lite.Tests/FactScorerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1627,6 +1627,20 @@ public void Score_PerHourGates_ReadAMissingPeriodAsOneHour()
Assert.Equal(0.0, BaseOf(WaitFact("PAGELATCH_UP", 0.01, new() { ["wait_time_ms"] = 10_000 })), precision: 4);
}

// A coverage_fraction of exactly 0 (WindowCoverage.Unobserved's stamp) reads as the NOMINAL window, not
// as a near-zero divisor: no shipped collector emits a wait fact for an unobserved window, so the branch
// only defends 0/0 — and if a fact like that ever arrived, dividing by ~0 would fire on the artifact.
// 12,000 ms over a nominal 4 h is 3 s/hr → 0, the pre-#3538 reading; pinned so the choice cannot flip.
[Fact]
public void Score_PerHourGates_ReadZeroCoverageAsTheNominalWindow_NotAsNearZero()
{
var fact = WaitFact("PAGELATCH_UP", 0.01, new()
{
["wait_time_ms"] = 12_000, ["period_duration_ms"] = FourHoursMs, ["coverage_fraction"] = 0.0
});
Assert.Equal(0.0, BaseOf(fact), precision: 6);
}

// Lineage census (source-text pin): every entry in GetWaitThresholds is preceded by a comment that
// names either a measured figure (a percentile, a max, "measured") or says "unmeasured". A new entry
// added without its lineage — the pre-#3538 shape of the whole table — fails here. Entries that share
Expand Down
8 changes: 8 additions & 0 deletions PerformanceMonitor.Analysis/FactScorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ private static double ScoreWaitFact(Fact fact)
/// stamps the period, so only a hand-built fact lacks it; reading it as one hour makes the per-hour
/// bars read as plain totals (10 s of PAGELATCH_UP, 15 min of THREADPOOL), which is the one reading a
/// fact with no window can honestly have.</para>
///
/// <para>A <c>coverage_fraction</c> of exactly 0 (what <c>WindowCoverage.Unobserved</c> stamps) falls back
/// to the NOMINAL window rather than to a near-zero divisor. That branch defends the arithmetic against
/// 0/0 and should be moot: both collectors emit no wait fact at all when <c>ObservedDurationMs &lt;= 0</c>,
/// so a fact carrying zero coverage and a non-zero wait total is a fact no shipped collector produces. If
/// one ever did, dividing by ~0 would turn any trace into an enormous hourly rate and fire on the
/// artifact; the nominal reading is the pre-#3538 form — the least-sensitive honest one — and is pinned
/// so the choice cannot silently flip.</para>
/// </summary>
private static double ObservedHours(Fact fact)
{
Expand Down
Loading