From 78d5057205503d1ace6cfbf6f4674d6008d717a7 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 03:28:17 +0200 Subject: [PATCH] Guard template-signal bias by support split --- .../HyperTwistCoachDashboardWidget.cpp | 111 ++++++++++++++++-- ...AL_SUPPORT_BIAS_GUARD_PACKET_2026-05-07.md | 52 ++++++++ 2 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_BIAS_GUARD_PACKET_2026-05-07.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index d13f0d3..6246d5b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -172,6 +172,12 @@ namespace HyperTwistCoachDashboardWidgetInternal int32* OutSuppressedOutcomeRuns = nullptr ); + bool HasTemplateLaunchTemplateSignalSupportBias( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, + int32* OutConfidenceLaneBiasOutcomeRuns = nullptr, + int32* OutBroaderOutcomeOnlyOutcomeRuns = nullptr + ); + int32 BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); @@ -634,7 +640,7 @@ namespace HyperTwistCoachDashboardWidgetInternal &SuppressedOutcomeRuns ); Fragment += FString::Printf( - TEXT(" | bias %s eligible %d/%d outcomes share %.2f | suppressed outcomes %d"), + TEXT(" | snapshot-bias %s eligible %d/%d outcomes share %.2f | suppressed outcomes %d"), bBiasEarned ? TEXT("earned") : TEXT("suppressed"), ConfidenceEligibleOutcomeRuns, TemplateSignalOutcomeRuns, @@ -698,6 +704,19 @@ namespace HyperTwistCoachDashboardWidgetInternal - BroaderOutcomeOnlyStats.AverageMistakesPerOutcomeRun ); } + int32 ConfidenceLaneBiasOutcomeRuns = 0; + int32 BroaderOutcomeOnlyOutcomeRuns = 0; + const bool bSupportBiasEarned = HasTemplateLaunchTemplateSignalSupportBias( + TemplateStats, + &ConfidenceLaneBiasOutcomeRuns, + &BroaderOutcomeOnlyOutcomeRuns + ); + Fragment += FString::Printf( + TEXT(" | support-bias %s bias outcomes %d | broader outcomes %d"), + bSupportBiasEarned ? TEXT("earned") : TEXT("suppressed"), + ConfidenceLaneBiasOutcomeRuns, + BroaderOutcomeOnlyOutcomeRuns + ); return Fragment; } @@ -1483,6 +1502,13 @@ namespace HyperTwistCoachDashboardWidgetInternal constexpr int32 TemplateLaunchTemplateSignalConfidenceMinimumScoreDelta = 1; constexpr float TemplateLaunchTemplateSignalConfidenceMinimumSuccessDelta = 0.05f; constexpr float TemplateLaunchTemplateSignalConfidenceMinimumMistakeImprovement = 0.5f; + constexpr int32 TemplateLaunchTemplateSignalSupportBiasMinimumConfidenceLaneBiasOutcomeRuns = + 2; + constexpr int32 TemplateLaunchTemplateSignalSupportBiasMinimumBroaderOutcomeOnlyOutcomeRuns = + 2; + constexpr int32 TemplateLaunchTemplateSignalSupportBiasMinimumScoreDelta = 1; + constexpr float TemplateLaunchTemplateSignalSupportBiasMinimumSuccessDelta = 0.05f; + constexpr float TemplateLaunchTemplateSignalSupportBiasMinimumMistakeImprovement = 0.5f; int32 CountTemplateLaunchOutcomeRuns(const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats) { @@ -1716,6 +1742,73 @@ namespace HyperTwistCoachDashboardWidgetInternal >= TemplateLaunchTemplateSignalConfidenceMinimumMistakeImprovement; } + bool HasTemplateLaunchTemplateSignalSupportBias( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, + int32* OutConfidenceLaneBiasOutcomeRuns, + int32* OutBroaderOutcomeOnlyOutcomeRuns + ) + { + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* ConfidenceLaneBiasStats = + nullptr; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* BroaderOutcomeOnlyStats = + nullptr; + if (TemplateStats != nullptr && TemplateStats->IsStructurallyValid()) + { + ConfidenceLaneBiasStats = + &TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalConfidenceLaneBiasStats; + BroaderOutcomeOnlyStats = + &TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalBroaderOutcomeOnlyStats; + } + + const int32 ConfidenceLaneBiasOutcomeRuns = + CountTemplateLaunchSelectionOutcomeRuns(ConfidenceLaneBiasStats); + const int32 BroaderOutcomeOnlyOutcomeRuns = + CountTemplateLaunchSelectionOutcomeRuns(BroaderOutcomeOnlyStats); + if (OutConfidenceLaneBiasOutcomeRuns != nullptr) + { + *OutConfidenceLaneBiasOutcomeRuns = ConfidenceLaneBiasOutcomeRuns; + } + if (OutBroaderOutcomeOnlyOutcomeRuns != nullptr) + { + *OutBroaderOutcomeOnlyOutcomeRuns = BroaderOutcomeOnlyOutcomeRuns; + } + + if (!HasTemplateLaunchTemplateSignalConfidenceSnapshotBias(TemplateStats) + || ConfidenceLaneBiasOutcomeRuns + < TemplateLaunchTemplateSignalSupportBiasMinimumConfidenceLaneBiasOutcomeRuns + || BroaderOutcomeOnlyOutcomeRuns + < TemplateLaunchTemplateSignalSupportBiasMinimumBroaderOutcomeOnlyOutcomeRuns) + { + return false; + } + + const int32 ConfidenceLaneBiasScore = + BuildTemplateLaunchSelectionLaneAnalyticsScore(ConfidenceLaneBiasStats); + const int32 BroaderOutcomeOnlyScore = + BuildTemplateLaunchSelectionLaneAnalyticsScore(BroaderOutcomeOnlyStats); + const int32 ScoreDelta = ConfidenceLaneBiasScore - BroaderOutcomeOnlyScore; + const float SuccessDelta = + (ConfidenceLaneBiasStats != nullptr && ConfidenceLaneBiasStats->IsStructurallyValid() + ? ConfidenceLaneBiasStats->OverallSuccessRate + : 0.0f) + - (BroaderOutcomeOnlyStats != nullptr && BroaderOutcomeOnlyStats->IsStructurallyValid() + ? BroaderOutcomeOnlyStats->OverallSuccessRate + : 0.0f); + const float MistakeImprovement = + (BroaderOutcomeOnlyStats != nullptr && BroaderOutcomeOnlyStats->IsStructurallyValid() + ? BroaderOutcomeOnlyStats->AverageMistakesPerOutcomeRun + : 0.0f) + - (ConfidenceLaneBiasStats != nullptr && ConfidenceLaneBiasStats->IsStructurallyValid() + ? ConfidenceLaneBiasStats->AverageMistakesPerOutcomeRun + : 0.0f); + return ScoreDelta >= TemplateLaunchTemplateSignalSupportBiasMinimumScoreDelta + || SuccessDelta >= TemplateLaunchTemplateSignalSupportBiasMinimumSuccessDelta + || MistakeImprovement + >= TemplateLaunchTemplateSignalSupportBiasMinimumMistakeImprovement; + } + int32 BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ) @@ -1974,16 +2067,16 @@ namespace HyperTwistCoachDashboardWidgetInternal } return Decision; } - const int32 CandidateTemplateSignalConfidenceSnapshotBonus = - HasTemplateLaunchTemplateSignalConfidenceSnapshotBias(CandidateStats) ? 1 : 0; - const int32 IncumbentTemplateSignalConfidenceSnapshotBonus = - HasTemplateLaunchTemplateSignalConfidenceSnapshotBias(IncumbentStats) ? 1 : 0; - if (CandidateTemplateSignalConfidenceSnapshotBonus - != IncumbentTemplateSignalConfidenceSnapshotBonus) + const int32 CandidateTemplateSignalSupportBiasBonus = + HasTemplateLaunchTemplateSignalSupportBias(CandidateStats) ? 1 : 0; + const int32 IncumbentTemplateSignalSupportBiasBonus = + HasTemplateLaunchTemplateSignalSupportBias(IncumbentStats) ? 1 : 0; + if (CandidateTemplateSignalSupportBiasBonus + != IncumbentTemplateSignalSupportBiasBonus) { Decision.bPreferCandidate = - CandidateTemplateSignalConfidenceSnapshotBonus - > IncumbentTemplateSignalConfidenceSnapshotBonus; + CandidateTemplateSignalSupportBiasBonus + > IncumbentTemplateSignalSupportBiasBonus; if (Decision.bPreferCandidate) { Decision.CandidateAnalyticsSupportKind = diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_BIAS_GUARD_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_BIAS_GUARD_PACKET_2026-05-07.md new file mode 100644 index 0000000..79341e4 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_BIAS_GUARD_PACKET_2026-05-07.md @@ -0,0 +1,52 @@ +# HyperTwist Phase 4 template-signal support bias guard packet + +## Purpose + +This packet feeds the new `broader-outcome-only` versus `confidence-lane-bias` support split back into tied dashboard analytics selection. A template no longer gets the extra broader template-signal bias just because its retained confidence-snapshot lane looks eligible; it now also needs enough stored `confidence-lane-bias` outcomes and those outcomes need to beat plain broader-outcome wins. + +## Scope + +- add a support-split guard helper for broader template-signal bias +- require the older confidence-snapshot bias to be earned first +- require minimum outcome history in both: + - `confidence-lane-bias` + - `broader-outcome-only` +- require the confidence-lane-bias bucket to beat the broader-outcome-only bucket on score or meaningful success / mistake improvement +- use that stricter helper in tied analytics selection +- surface the same `support-bias earned/suppressed` state in shared template analytics detail + +## What landed + +- `HyperTwistCoachDashboardWidget.cpp` + - added `HasTemplateLaunchTemplateSignalSupportBias(...)` + - the helper only returns true when: + - `HasTemplateLaunchTemplateSignalConfidenceSnapshotBias(...)` already clears + - the template has at least `2` confidence-lane-bias outcome runs + - the template has at least `2` broader-outcome-only outcome runs + - the confidence-lane-bias bucket beats broader-outcome-only on lane score or shows meaningful success / mistake improvement + - `ResolveTemplateAnalyticsPreferenceDecision(...)` now uses this stricter support bias when breaking otherwise tied broader analytics outcomes + - shared `template-signal support` analytics detail now appends: + - `support-bias earned` + - or `support-bias suppressed` + - plus the stored bias / broader outcome counts + - the older confidence-snapshot fragment now labels its state as `snapshot-bias` so the two guards are not conflated + +## Product effect + +- broader template-signal bias only reinforces future tied selections when stored confidence-lane-bias outcomes have enough depth and are outperforming plain broader-outcome wins +- templates with sparse or weaker confidence-lane-bias history no longer get that extra tie-break preference +- live `[Template Launch]` detail, template history, and recap now show both the snapshot-bias state and the stricter support-bias state + +## Acceptance criteria + +- tied analytics selection uses the stricter support bias instead of the older snapshot bias alone +- support bias requires minimum outcome history in both compared support buckets +- support bias only clears when confidence-lane-bias beats broader-outcome-only on score or meaningful quality deltas +- shared analytics detail exposes `support-bias earned/suppressed` with both bucket counts + +## Validation checklist + +1. full `Development Editor|Win64` build succeeds +2. confirm tied analytics selection now checks `HasTemplateLaunchTemplateSignalSupportBias(...)` +3. confirm the helper requires both bucket minimums and a positive lane comparison +4. confirm shared analytics detail shows both `snapshot-bias` and `support-bias`