fix(aggregates): live PercentGood/DurationGood count Good time only (#809)
The frozen 1.5.378.182 StatusAggregateCalculator counts Uncertain regions as Good under TreatUncertainAsBad=false, so live AggregateFilter PercentGood / DurationGood disagreed with HistoryRead (Part 13 §5.4.3.31/§5.4.3.33, Mantis 0006166) whenever Uncertain data was present. Owner-approved 2026-11-11. - New app-owned GoodOnlyStatusAggregateCalculator subclasses the baseline and replaces only the PercentGood/DurationGood computation (Good regions only); CompatibilityAggregateFactory routes exactly those two IDs to it. The copied SDK files are untouched; the other 35 aggregates are byte-identical. - Golden test: the 64 changed vectors (38 PercentGood + 26 DurationGood) are exempted exactly — those whose baseline output differs from the same case's baseline TreatUncertainAsBad=true twin — and pinned to that twin (baseline evidence, not regenerated from the implementation); plus a full Good-only pin over all 352 PercentGood/DurationGood vectors and a hand-computed case. - Live-vs-history parity over Good/Uncertain/Bad mixes (no masks), with a discrimination check against the pre-#809 baseline; the booted-runtime facts test now expects 8000 ms / 40 %. - SdkAggregateOracle: Run/RunLive = the registered factory; RunSdkAsIs = the untouched baseline; its Part13GoodCalculator duplicate is removed. - README (recorded decision), ServerAggregateConfiguration remarks, docs/Historian.md and .claude/rules/historian.md updated. Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
This commit is contained in:
@@ -71,7 +71,7 @@ public abstract class QualityMixConformance : ConformanceBase
|
||||
/// <summary>Hand-computed Part 13 DurationBad of Q's eight intervals, seconds.</summary>
|
||||
private static readonly double[] QDurationBad = [3, 0, 0, 0, 5, 0, 0, 3];
|
||||
|
||||
/// <summary>What the 1.5.378.182 baseline answers for Q's PercentGood (Uncertain counted as Good, #796).</summary>
|
||||
/// <summary>What the untouched 1.5.378.182 baseline answers for Q's PercentGood (Uncertain counted as Good, #796; live before #809).</summary>
|
||||
private static readonly double[] QBaselinePercentGood = [70, 100, 100, 100, 50, 100, 100, 70];
|
||||
|
||||
/// <summary>
|
||||
@@ -182,7 +182,8 @@ public abstract class QualityMixConformance : ConformanceBase
|
||||
var pGood = (await src.ReadProcessedAsync("Q", At(0), At(80), Ten, HistoryAggregateType.PercentGood, Ct)).Samples;
|
||||
ShouldBeClose(Values(good), Values(pGood).Select(p => p * Ten.TotalMilliseconds / 100).ToArray(), "DurationGood vs PercentGood");
|
||||
|
||||
// The baseline (the live AggregateFilter calculator, #809) answers Q's DurationGood with the Uncertain time added.
|
||||
// The untouched baseline (the live AggregateFilter calculator before #809) answers Q's DurationGood with the
|
||||
// Uncertain time added; the live calculator now answers the Part 13 values above (SdkAggregateParityTests).
|
||||
var baseline = SdkAggregateOracle.RunSdkAsIs(HistoryAggregateType.DurationGood, At(0), At(80), Ten,
|
||||
Q.Select(r => (r.T, r.V, r.Status)));
|
||||
ShouldBeClose(baseline.Select(r => Convert.ToDouble(r.Value)).ToArray(),
|
||||
|
||||
Reference in New Issue
Block a user