From 3325dc40b9eff95149ad6852fe4f5f0eb7f264ae Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 03:08:08 +0200 Subject: [PATCH] Bias tied template analytics by confidence lane --- .../HyperTwistCoachDashboardWidget.cpp | 139 ++++++++++++++++++ ...NFIDENCE_SCORING_BIAS_PACKET_2026-05-07.md | 47 ++++++ 2 files changed, 186 insertions(+) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_CONFIDENCE_SCORING_BIAS_PACKET_2026-05-07.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 3817dbf..70f360d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -160,6 +160,14 @@ namespace HyperTwistCoachDashboardWidgetInternal int32* OutPeerNoEvidenceOutcomeRuns = nullptr ); + bool HasTemplateLaunchTemplateSignalConfidenceSnapshotBias( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, + int32* OutConfidenceEligibleOutcomeRuns = nullptr, + int32* OutTemplateSignalOutcomeRuns = nullptr, + float* OutConfidenceEligibleOutcomeShare = nullptr, + int32* OutSuppressedOutcomeRuns = nullptr + ); + int32 BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); @@ -570,6 +578,25 @@ namespace HyperTwistCoachDashboardWidgetInternal NoRetainedLaneStats, TEXT("no-retained-lane")) ); + int32 ConfidenceEligibleOutcomeRuns = 0; + int32 TemplateSignalOutcomeRuns = 0; + int32 SuppressedOutcomeRuns = 0; + float ConfidenceEligibleOutcomeShare = 0.0f; + const bool bBiasEarned = HasTemplateLaunchTemplateSignalConfidenceSnapshotBias( + TemplateStats, + &ConfidenceEligibleOutcomeRuns, + &TemplateSignalOutcomeRuns, + &ConfidenceEligibleOutcomeShare, + &SuppressedOutcomeRuns + ); + Fragment += FString::Printf( + TEXT(" | bias %s eligible %d/%d outcomes share %.2f | suppressed outcomes %d"), + bBiasEarned ? TEXT("earned") : TEXT("suppressed"), + ConfidenceEligibleOutcomeRuns, + TemplateSignalOutcomeRuns, + ConfidenceEligibleOutcomeShare, + SuppressedOutcomeRuns + ); if (ConfidenceEligibleStats.IsStructurallyValid() && SuppressedStats.IsStructurallyValid() && ConfidenceEligibleStats.GetOutcomeRunCount() > 0 @@ -1335,6 +1362,11 @@ namespace HyperTwistCoachDashboardWidgetInternal constexpr float TemplateLaunchSelectionTieBreakConfidenceMinimumMistakeImprovement = 0.5f; constexpr int32 TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeRuns = 2; constexpr float TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeShare = 0.40f; + constexpr int32 TemplateLaunchTemplateSignalConfidenceMinimumEligibleOutcomeRuns = 2; + constexpr float TemplateLaunchTemplateSignalConfidenceMinimumEligibleOutcomeShare = 0.50f; + constexpr int32 TemplateLaunchTemplateSignalConfidenceMinimumScoreDelta = 1; + constexpr float TemplateLaunchTemplateSignalConfidenceMinimumSuccessDelta = 0.05f; + constexpr float TemplateLaunchTemplateSignalConfidenceMinimumMistakeImprovement = 0.5f; int32 CountTemplateLaunchOutcomeRuns(const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats) { @@ -1479,6 +1511,95 @@ namespace HyperTwistCoachDashboardWidgetInternal >= TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeShare; } + bool HasTemplateLaunchTemplateSignalConfidenceSnapshotBias( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, + int32* OutConfidenceEligibleOutcomeRuns, + int32* OutTemplateSignalOutcomeRuns, + float* OutConfidenceEligibleOutcomeShare, + int32* OutSuppressedOutcomeRuns + ) + { + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* TemplateSignalStats = nullptr; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* ConfidenceEligibleStats = + nullptr; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* SuppressedStats = nullptr; + if (TemplateStats != nullptr && TemplateStats->IsStructurallyValid()) + { + TemplateSignalStats = + &TemplateStats->DashboardAnalyticsTieBreakTemplateScopedSignalStats; + ConfidenceEligibleStats = + &TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalConfidenceEligibleStats; + SuppressedStats = + &TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalSuppressedPeerNoEvidenceHeavyStats; + } + + const int32 ConfidenceEligibleOutcomeRuns = + CountTemplateLaunchSelectionOutcomeRuns(ConfidenceEligibleStats); + const int32 TemplateSignalOutcomeRuns = + CountTemplateLaunchSelectionOutcomeRuns(TemplateSignalStats); + const int32 SuppressedOutcomeRuns = + CountTemplateLaunchSelectionOutcomeRuns(SuppressedStats); + const float ConfidenceEligibleOutcomeShare = TemplateSignalOutcomeRuns > 0 + ? static_cast(ConfidenceEligibleOutcomeRuns) + / static_cast(TemplateSignalOutcomeRuns) + : 0.0f; + + if (OutConfidenceEligibleOutcomeRuns != nullptr) + { + *OutConfidenceEligibleOutcomeRuns = ConfidenceEligibleOutcomeRuns; + } + if (OutTemplateSignalOutcomeRuns != nullptr) + { + *OutTemplateSignalOutcomeRuns = TemplateSignalOutcomeRuns; + } + if (OutConfidenceEligibleOutcomeShare != nullptr) + { + *OutConfidenceEligibleOutcomeShare = ConfidenceEligibleOutcomeShare; + } + if (OutSuppressedOutcomeRuns != nullptr) + { + *OutSuppressedOutcomeRuns = SuppressedOutcomeRuns; + } + + if (ConfidenceEligibleOutcomeRuns + < TemplateLaunchTemplateSignalConfidenceMinimumEligibleOutcomeRuns + || ConfidenceEligibleOutcomeShare + < TemplateLaunchTemplateSignalConfidenceMinimumEligibleOutcomeShare) + { + return false; + } + if (SuppressedOutcomeRuns <= 0) + { + return true; + } + + const int32 ConfidenceEligibleScore = + BuildTemplateLaunchSelectionLaneAnalyticsScore(ConfidenceEligibleStats); + const int32 SuppressedScore = + BuildTemplateLaunchSelectionLaneAnalyticsScore(SuppressedStats); + const int32 ScoreDelta = ConfidenceEligibleScore - SuppressedScore; + const float SuccessDelta = + (ConfidenceEligibleStats != nullptr && ConfidenceEligibleStats->IsStructurallyValid() + ? ConfidenceEligibleStats->OverallSuccessRate + : 0.0f) + - (SuppressedStats != nullptr && SuppressedStats->IsStructurallyValid() + ? SuppressedStats->OverallSuccessRate + : 0.0f); + const float MistakeImprovement = + (SuppressedStats != nullptr && SuppressedStats->IsStructurallyValid() + ? SuppressedStats->AverageMistakesPerOutcomeRun + : 0.0f) + - (ConfidenceEligibleStats != nullptr && ConfidenceEligibleStats->IsStructurallyValid() + ? ConfidenceEligibleStats->AverageMistakesPerOutcomeRun + : 0.0f); + return ScoreDelta >= TemplateLaunchTemplateSignalConfidenceMinimumScoreDelta + || SuccessDelta >= TemplateLaunchTemplateSignalConfidenceMinimumSuccessDelta + || MistakeImprovement + >= TemplateLaunchTemplateSignalConfidenceMinimumMistakeImprovement; + } + int32 BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ) @@ -1736,6 +1857,24 @@ namespace HyperTwistCoachDashboardWidgetInternal } return Decision; } + const int32 CandidateTemplateSignalConfidenceSnapshotBonus = + HasTemplateLaunchTemplateSignalConfidenceSnapshotBias(CandidateStats) ? 1 : 0; + const int32 IncumbentTemplateSignalConfidenceSnapshotBonus = + HasTemplateLaunchTemplateSignalConfidenceSnapshotBias(IncumbentStats) ? 1 : 0; + if (CandidateTemplateSignalConfidenceSnapshotBonus + != IncumbentTemplateSignalConfidenceSnapshotBonus) + { + Decision.bPreferCandidate = + CandidateTemplateSignalConfidenceSnapshotBonus + > IncumbentTemplateSignalConfidenceSnapshotBonus; + if (Decision.bPreferCandidate) + { + Decision.CandidateAnalyticsSupportKind = + EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind:: + TemplateScopedOutcomeSignal; + } + return Decision; + } if (CandidateOutcomeRuns != IncumbentOutcomeRuns) { Decision.bPreferCandidate = CandidateOutcomeRuns > IncumbentOutcomeRuns; diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_CONFIDENCE_SCORING_BIAS_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_CONFIDENCE_SCORING_BIAS_PACKET_2026-05-07.md new file mode 100644 index 0000000..1e5a1ca --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_CONFIDENCE_SCORING_BIAS_PACKET_2026-05-07.md @@ -0,0 +1,47 @@ +# HyperTwist Phase 4 template-signal confidence scoring bias packet + +## Purpose + +This packet feeds the new broader `TemplateScopedOutcomeSignal` confidence-snapshot split back into actual dashboard analytics tie-break selection. When the existing analytics score already ties, the selector can now conservatively prefer a template whose broader analytics wins are already landing in the `confidence-eligible` lane instead of a competing template whose broader wins still cluster in the `suppressed` lane. + +## Scope + +- add a conservative template-signal confidence-snapshot bias helper in dashboard selection logic +- require minimum `confidence-eligible` outcome count and share before the bias can exist +- if the template still has suppressed broader-history outcomes, require the eligible lane to beat or clearly improve on the suppressed lane before the bias is awarded +- apply that bias only after the existing analytics score ties +- surface the same bias-earned / bias-suppressed state in shared template analytics detail + +## What landed + +- `HyperTwistCoachDashboardWidget.cpp` + - added `HasTemplateLaunchTemplateSignalConfidenceSnapshotBias(...)` + - new guard requirements: + - at least `2` `confidence-eligible` outcome runs + - at least `0.50` eligible share within broader template-signal outcomes + - if suppressed broader-history exists, eligible outcomes must beat it on lane score or show meaningful success / mistake improvement + - `ResolveTemplateAnalyticsPreferenceDecision(...)` now consults this bias only after the existing analytics score already ties + - shared template analytics detail now appends: + - `bias earned` or `bias suppressed` + - eligible outcome count/share + - suppressed outcome count + +## Product effect + +- broader template-signal confidence history no longer just sits in analytics detail; it can now break otherwise tied analytics choices +- the bias stays conservative because it does not override the main analytics score and it requires both sample depth and lane-share support +- live `[Template Launch]` detail, template launch history, and run recap now expose whether the broader template-signal bias is currently earned or suppressed for the displayed template + +## Acceptance criteria + +- the new broader template-signal confidence bias only applies after the current analytics score ties +- the bias requires minimum eligible outcome count and share +- suppressed broader-history prevents the bias unless the eligible lane meaningfully beats or improves on it +- shared analytics detail shows whether the broader template-signal bias is earned or suppressed + +## Validation checklist + +1. full `Development Editor|Win64` build succeeds +2. confirm `ResolveTemplateAnalyticsPreferenceDecision(...)` checks the new bias only after score equality +3. confirm the bias requires minimum eligible outcome count/share +4. confirm shared analytics detail reports `bias earned` or `bias suppressed` with eligible count/share and suppressed count