From 7ffe2cd4438e0e2fc9f7c919ce5ca5e67d9dd1f0 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 01:54:14 +0200 Subject: [PATCH] Explain template support-kind selector rationale --- .../HyperTwistCoachDashboardWidget.cpp | 62 +++++++++++-- ...UPPORT_KIND_RATIONALE_PACKET_2026-05-07.md | 90 +++++++++++++++++++ 2 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_SUPPORT_KIND_RATIONALE_PACKET_2026-05-07.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 9188f10..a6955dd 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -207,6 +207,38 @@ namespace HyperTwistCoachDashboardWidgetInternal } } + FString BuildTemplateLaunchAnalyticsSupportKindGuardSignalFragment( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats + ) + { + if (TemplateStats == nullptr || !TemplateStats->IsStructurallyValid()) + { + return FString(); + } + + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats& TemplateSignalStats = + TemplateStats->DashboardAnalyticsTieBreakTemplateScopedSignalStats; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats& SplitConfidenceStats = + TemplateStats->DashboardAnalyticsTieBreakSelectorSplitConfidenceStats; + if (!TemplateSignalStats.IsStructurallyValid() + || !SplitConfidenceStats.IsStructurallyValid() + || TemplateSignalStats.GetOutcomeRunCount() <= 0 + || SplitConfidenceStats.GetOutcomeRunCount() <= 0) + { + return FString(); + } + + return FString::Printf( + TEXT("split-confidence outcomes %d | success %.2f | avg mistakes %.2f vs template-signal outcomes %d | success %.2f | avg mistakes %.2f"), + SplitConfidenceStats.GetOutcomeRunCount(), + SplitConfidenceStats.OverallSuccessRate, + SplitConfidenceStats.AverageMistakesPerOutcomeRun, + TemplateSignalStats.GetOutcomeRunCount(), + TemplateSignalStats.OverallSuccessRate, + TemplateSignalStats.AverageMistakesPerOutcomeRun + ); + } + FHyperTwistTrainingTemplateLaunchSelectionProvenance BuildTemplateLaunchAnalyticsTieBreakSelectionProvenance( const FHyperTwistTrainingSessionTemplate& SelectedTemplate, const FHyperTwistTrainingTemplateLaunchScopedStats* SelectedStats, @@ -222,16 +254,32 @@ namespace HyperTwistCoachDashboardWidgetInternal Provenance.AnalyticsSupportKind = AnalyticsSupportKind; Provenance.PeerTemplateId = PeerTemplate.TemplateId; Provenance.PeerTemplateTitle = ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate); + const FString SupportKindGuardSignalFragment = + BuildTemplateLaunchAnalyticsSupportKindGuardSignalFragment(SelectedStats); if (AnalyticsSupportKind == EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind::SelectorSplitConfidence) { - Provenance.DecisionSummaryLine = FString::Printf( - TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s with selector-split confidence support: chosen %s | peer %s."), - *ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate), - *ResolveDashboardTemplateLaunchDisplayTitle(SelectedTemplate), - *BuildTemplateLaunchAnalyticsTieBreakSignalFragment(SelectedStats), - *BuildTemplateLaunchAnalyticsTieBreakSignalFragment(PeerStats) - ); + if (!SupportKindGuardSignalFragment.IsEmpty()) + { + Provenance.DecisionSummaryLine = FString::Printf( + TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s with selector-split confidence support after split-confidence beat template-signal on stored support-kind outcomes: %s | chosen %s | peer %s."), + *ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate), + *ResolveDashboardTemplateLaunchDisplayTitle(SelectedTemplate), + *SupportKindGuardSignalFragment, + *BuildTemplateLaunchAnalyticsTieBreakSignalFragment(SelectedStats), + *BuildTemplateLaunchAnalyticsTieBreakSignalFragment(PeerStats) + ); + } + else + { + Provenance.DecisionSummaryLine = FString::Printf( + TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s with selector-split confidence support: chosen %s | peer %s."), + *ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate), + *ResolveDashboardTemplateLaunchDisplayTitle(SelectedTemplate), + *BuildTemplateLaunchAnalyticsTieBreakSignalFragment(SelectedStats), + *BuildTemplateLaunchAnalyticsTieBreakSignalFragment(PeerStats) + ); + } } else { diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_SUPPORT_KIND_RATIONALE_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_SUPPORT_KIND_RATIONALE_PACKET_2026-05-07.md new file mode 100644 index 0000000..61ef5cd --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_SUPPORT_KIND_RATIONALE_PACKET_2026-05-07.md @@ -0,0 +1,90 @@ +# HyperTwist Phase 4 template launch support-kind rationale packet + +Created on `2026-05-07` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` dashboard selector explanation slice + +## Purpose + +This packet surfaces the support-kind confidence guard result directly in the analytics tie-break rationale, so the dashboard can say when a winner was reinforced because its `SelectorSplitConfidence` bucket beat its `TemplateScopedOutcomeSignal` bucket on stored support-kind outcomes. + +The open tasks were: + +- stop leaving the new support-kind confidence guard implicit in selector internals +- explain selector-split-confidence-backed tie-break wins using the same stored support-kind evidence that justified the extra confidence point +- make the live dashboard detail, template launch history, and recap rationale more specific without widening the persisted selection schema again + +It is not: + +- another selector score change packet +- a new repository aggregation packet +- a new provenance schema packet +- a new dashboard analytics screen + +## Scope + +Bounded lane: + +- add a concise formatter for the winning template's support-kind guard signal +- use that formatter only when `SelectorSplitConfidence` actually backed an analytics tie-break winner +- fold that guard result into the persisted analytics tie-break decision summary line +- let the existing selector detail fragment continue surfacing that richer rationale across live launch, history, and recap + +Out of scope: + +- changing support-kind bucket derivation +- changing the actual selector confidence thresholds +- adding another selection provenance field +- changing non-dashboard selection rationale + +## Why this was the right next packet + +The previous packet made the selector confidence bonus answer to the right repo-backed support-kind evidence. + +That still left one operator-facing gap: + +- the dashboard could now use the right support-kind split +- but the visible selector rationale still mostly described only the broader analytics tie-break decision +- and it did not explicitly say when the support-kind confidence guard itself had reinforced the winner + +So the next honest move was: + +- keep the evidence source exactly aligned with the selector logic +- format that support-kind guard result concisely +- and inject it into the same persisted rationale already shown by live launch detail, template history, and recap + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp` + - added a concise support-kind guard signal formatter for the winning template + - when `SelectorSplitConfidence` backs the analytics tie-break winner, the persisted decision summary now says that split-confidence beat template-signal on stored support-kind outcomes and includes the support-kind outcome comparison + - falls back to the prior concise wording if that support-kind fragment is unexpectedly unavailable + +## Product effect + +Analytics tie-break rationale is now closer to the real selector logic: + +- broader template-scoped analytics tie-break wins still read the same way +- selector-split-confidence-backed wins now say that the winner beat the template-signal bucket on stored support-kind outcomes +- that richer rationale is persisted and automatically shows up on the live `[Template Launch]` detail, template launch history, and template-backed recap + +## Acceptance criteria + +- `SelectorSplitConfidence`-backed analytics tie-break rationale includes the stored support-kind guard result +- rationale continues to fall back safely if the support-kind fragment is unavailable +- live launch detail, template history, and recap all inherit the richer rationale through existing selection detail plumbing +- full product build succeeds + +## Validation checklist + +1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor` +2. confirm selector-split-confidence-backed analytics tie-break rationale mentions support-kind guard outperformance +3. confirm broader template-signal-only analytics tie-break rationale remains unchanged +4. confirm the richer rationale appears on live launch detail, template history, and recap through persisted selection provenance + +That is the packet.