diff --git a/HellionChat/SelfTests/HoverSheenAllocStep.cs b/HellionChat/SelfTests/HoverSheenAllocStep.cs index 12edffb..b7a557e 100644 --- a/HellionChat/SelfTests/HoverSheenAllocStep.cs +++ b/HellionChat/SelfTests/HoverSheenAllocStep.cs @@ -39,10 +39,21 @@ internal sealed class HoverSheenAllocStep : ISelfTestStep foreach (var key in keys) dl.DrawHoverSheen(min, max, accent, key, hovered: true); - // Un-hover sweep to verify the cleanup path drops the entries. + // Hovered loop must have registered exactly the three constant keys + // (no per-frame growth/duplication) — assert via observable state, + // not an unconditional pass (K7 false-green fix). + foreach (var key in keys) + if (!DrawListExtensions.IsSheenTracked(key)) + return SelfTestStepResult.Fail; + + // Un-hover sweep must drop every entry through the cleanup branch. foreach (var key in keys) dl.DrawHoverSheen(min, max, accent, key, hovered: false); + foreach (var key in keys) + if (DrawListExtensions.IsSheenTracked(key)) + return SelfTestStepResult.Fail; + return SelfTestStepResult.Pass; } diff --git a/HellionChat/Ui/StyleEngine/DrawListExtensions.cs b/HellionChat/Ui/StyleEngine/DrawListExtensions.cs index 1c0700c..7fd1590 100644 --- a/HellionChat/Ui/StyleEngine/DrawListExtensions.cs +++ b/HellionChat/Ui/StyleEngine/DrawListExtensions.cs @@ -24,6 +24,11 @@ internal static class DrawListExtensions private static readonly Dictionary SheenStarts = new(); + // Test-observability hook for HoverSheenAllocStep: lets the self-test + // assert the hover/un-hover dictionary contract via real state instead + // of an unconditional pass (K7 false-green fix). Not a runtime path. + internal static bool IsSheenTracked(string elementId) => SheenStarts.ContainsKey(elementId); + public static void DrawHoverSheen( this ImDrawListPtr dl, Vector2 min,