diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 81b6632..1cfcdef 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -121,6 +121,14 @@ namespace HyperTwistCoachDashboardWidgetInternal const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); + bool HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, + int32* OutComparablePeerOutcomeRuns = nullptr, + int32* OutSplitConfidenceOutcomeRuns = nullptr, + float* OutComparablePeerOutcomeShare = nullptr, + int32* OutPeerNoEvidenceOutcomeRuns = nullptr + ); + int32 BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); @@ -930,6 +938,8 @@ namespace HyperTwistCoachDashboardWidgetInternal constexpr int32 TemplateLaunchSelectionTieBreakConfidenceMinimumScoreDelta = 2; constexpr float TemplateLaunchSelectionTieBreakConfidenceMinimumSuccessDelta = 0.05f; constexpr float TemplateLaunchSelectionTieBreakConfidenceMinimumMistakeImprovement = 0.5f; + constexpr int32 TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeRuns = 2; + constexpr float TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeShare = 0.40f; int32 CountTemplateLaunchOutcomeRuns(const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats) { @@ -1007,6 +1017,73 @@ namespace HyperTwistCoachDashboardWidgetInternal ); } + bool HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, + int32* OutComparablePeerOutcomeRuns, + int32* OutSplitConfidenceOutcomeRuns, + float* OutComparablePeerOutcomeShare, + int32* OutPeerNoEvidenceOutcomeRuns + ) + { + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* SplitConfidenceStats = nullptr; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* PeerNoEvidenceStats = nullptr; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* PeerWeakerStats = nullptr; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* PeerMarginalStats = nullptr; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats* PeerClearedStats = nullptr; + if (TemplateStats != nullptr && TemplateStats->IsStructurallyValid()) + { + SplitConfidenceStats = + &TemplateStats->DashboardAnalyticsTieBreakSelectorSplitConfidenceStats; + PeerNoEvidenceStats = + &TemplateStats + ->DashboardAnalyticsTieBreakSelectorSplitConfidencePeerLackedComparableEvidenceStats; + PeerWeakerStats = + &TemplateStats + ->DashboardAnalyticsTieBreakSelectorSplitConfidencePeerFailedWeakerSupportKindStats; + PeerMarginalStats = + &TemplateStats + ->DashboardAnalyticsTieBreakSelectorSplitConfidencePeerFailedMarginalSupportKindStats; + PeerClearedStats = + &TemplateStats + ->DashboardAnalyticsTieBreakSelectorSplitConfidencePeerClearedComparableGuardStats; + } + + const int32 ComparablePeerOutcomeRuns = + CountTemplateLaunchSelectionOutcomeRuns(PeerWeakerStats) + + CountTemplateLaunchSelectionOutcomeRuns(PeerMarginalStats) + + CountTemplateLaunchSelectionOutcomeRuns(PeerClearedStats); + const int32 SplitConfidenceOutcomeRuns = + CountTemplateLaunchSelectionOutcomeRuns(SplitConfidenceStats); + const int32 PeerNoEvidenceOutcomeRuns = + CountTemplateLaunchSelectionOutcomeRuns(PeerNoEvidenceStats); + const float ComparablePeerOutcomeShare = SplitConfidenceOutcomeRuns > 0 + ? static_cast(ComparablePeerOutcomeRuns) + / static_cast(SplitConfidenceOutcomeRuns) + : 0.0f; + + if (OutComparablePeerOutcomeRuns != nullptr) + { + *OutComparablePeerOutcomeRuns = ComparablePeerOutcomeRuns; + } + if (OutSplitConfidenceOutcomeRuns != nullptr) + { + *OutSplitConfidenceOutcomeRuns = SplitConfidenceOutcomeRuns; + } + if (OutComparablePeerOutcomeShare != nullptr) + { + *OutComparablePeerOutcomeShare = ComparablePeerOutcomeShare; + } + if (OutPeerNoEvidenceOutcomeRuns != nullptr) + { + *OutPeerNoEvidenceOutcomeRuns = PeerNoEvidenceOutcomeRuns; + } + + return ComparablePeerOutcomeRuns + >= TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeRuns + && ComparablePeerOutcomeShare + >= TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeShare; + } + int32 BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ) @@ -1053,6 +1130,10 @@ namespace HyperTwistCoachDashboardWidgetInternal { return 0; } + if (!HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus(TemplateStats)) + { + return 0; + } return 1; } @@ -1094,6 +1175,11 @@ namespace HyperTwistCoachDashboardWidgetInternal return EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition:: PeerFailedGuardOnWeakerSupportKindOutcomes; } + if (!HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus(TemplateStats)) + { + return EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition:: + PeerLackedComparableEvidence; + } if (BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus(TemplateStats) <= 0) { @@ -1166,6 +1252,32 @@ namespace HyperTwistCoachDashboardWidgetInternal const float MistakeImprovement = TemplateSignalStats.AverageMistakesPerOutcomeRun - SplitConfidenceStats.AverageMistakesPerOutcomeRun; + int32 ComparablePeerOutcomeRuns = 0; + int32 SplitConfidenceSelectionOutcomeRuns = 0; + int32 PeerNoEvidenceOutcomeRuns = 0; + float ComparablePeerOutcomeShare = 0.0f; + const bool bHasComparablePeerEvidence = + HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus( + TemplateStats, + &ComparablePeerOutcomeRuns, + &SplitConfidenceSelectionOutcomeRuns, + &ComparablePeerOutcomeShare, + &PeerNoEvidenceOutcomeRuns + ); + if (!bHasComparablePeerEvidence) + { + return FString::Printf( + TEXT("%s lacked enough selector-split wins against peers with comparable support-kind evidence to clear the selector-split confidence guard: comparable-peer outcomes %d of %d split-confidence outcomes (share %.2f, required at least %d at %.2f) | peer-no-evidence %d | %s"), + ResolvedSubjectLabel, + ComparablePeerOutcomeRuns, + SplitConfidenceSelectionOutcomeRuns, + ComparablePeerOutcomeShare, + TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeRuns, + TemplateLaunchSelectionTieBreakConfidenceMinimumComparablePeerOutcomeShare, + PeerNoEvidenceOutcomeRuns, + *SignalFragment + ); + } if (BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus(TemplateStats) <= 0) { return FString::Printf( @@ -1178,8 +1290,11 @@ namespace HyperTwistCoachDashboardWidgetInternal } return FString::Printf( - TEXT("%s split-confidence beat its template-signal bucket on stored support-kind outcomes and cleared the selector-split confidence guard: %s"), + TEXT("%s split-confidence beat its template-signal bucket on stored support-kind outcomes and cleared the selector-split confidence guard with comparable-peer outcomes %d of %d split-confidence outcomes (share %.2f): %s"), ResolvedSubjectLabel, + ComparablePeerOutcomeRuns, + SplitConfidenceSelectionOutcomeRuns, + ComparablePeerOutcomeShare, *SignalFragment ); } diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_COMPARABLE_PEER_CONFIDENCE_GUARD_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_COMPARABLE_PEER_CONFIDENCE_GUARD_PACKET_2026-05-07.md new file mode 100644 index 0000000..a984ded --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_COMPARABLE_PEER_CONFIDENCE_GUARD_PACKET_2026-05-07.md @@ -0,0 +1,64 @@ +# HyperTwist Phase 4 template launch comparable-peer confidence guard packet + +Created on `2026-05-07` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` dashboard selector confidence slice + +## Purpose + +This packet starts consuming the new peer-support disposition analytics in actual selector policy. The selector-split confidence bonus now requires enough wins against peers that had at least some comparable support-kind evidence, so stored confidence does not keep reinforcing itself mainly by beating peer lanes that had no comparable evidence at all. + +## Scope + +Bounded lane: + +- keep the existing support-kind score, success, and mistake delta guard intact +- add a comparable-peer-evidence guard on top of it using the repo-backed peer disposition buckets +- require both: + - a minimum count of selector-split-confidence outcomes against peers with comparable evidence + - a minimum share of the template's selector-split-confidence outcome history coming from those comparable-evidence peers +- align the existing support-kind audit clause with the stricter guard so rationale stays honest + +Out of scope: + +- changing repo aggregation shape +- widening persisted provenance schema again +- adding a new dashboard panel +- feeding peer disposition directly into primary template scoring outside the selector-split confidence bonus + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp` + - added a helper that measures comparable-peer selector-split-confidence evidence from the repo-backed peer disposition buckets + - `BuildTemplateLaunchAnalyticsTieBreakConfidenceBonus(...)` now refuses the bonus unless comparable-peer outcome history is deep enough and large enough as a share of the template's selector-split-confidence history + - peer support-kind disposition resolution now treats insufficient comparable-peer evidence as `PeerLackedComparableEvidence` + - support-kind audit text now explains the comparable-peer evidence requirement and shows counts/share when that guard fails or clears + +## Product effect + +Template selector confidence is more conservative: + +- selector-split confidence reinforcement still requires the template's split-confidence bucket to beat its template-signal bucket +- but that is no longer enough by itself +- the template must also show enough split-confidence wins against peers that brought some comparable evidence, rather than relying mostly on peer-no-evidence lanes + +## Acceptance criteria + +- selector-split confidence bonus requires minimum comparable-peer outcome count and share +- insufficient comparable-peer evidence suppresses the bonus even when the support-kind split beats the template-signal bucket +- peer disposition and support-kind audit text stay aligned with the stricter guard +- full product build succeeds + +## Validation checklist + +1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor` +2. confirm selector-split confidence bonus now consults the repo-backed peer disposition buckets +3. confirm insufficient comparable-peer evidence suppresses the bonus +4. confirm support-kind audit text names the comparable-peer count/share requirement when relevant + +That is the packet.