From 2fdcaf46eef9568fef0bd9e993eb1e5a7b794273 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 15:46:21 +0200 Subject: [PATCH] Split template analytics by decision-bias snapshot --- .../HyperTwistCoachDashboardWidget.cpp | 59 ++++++++++++++++++- .../HyperTwistTrainingRepositoryLibrary.cpp | 53 +++++++++++++++++ .../HyperTwistTrainingTypes.h | 12 ++++ ...AS_SNAPSHOT_ANALYTICS_PACKET_2026-05-07.md | 48 +++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_DECISION_BIAS_SNAPSHOT_ANALYTICS_PACKET_2026-05-07.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index ffc0cb4..d7030cb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -129,6 +129,10 @@ namespace HyperTwistCoachDashboardWidgetInternal const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); + FString BuildTemplateLaunchAnalyticsTieBreakTemplateSignalDecisionBiasSnapshotFragment( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats + ); + FString BuildTemplateLaunchAnalyticsTieBreakTemplateSignalConfidenceSnapshotFragment( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); @@ -240,7 +244,7 @@ namespace HyperTwistCoachDashboardWidgetInternal } return FString::Printf( - TEXT("avg recommended %d | avg focus %d | avg completed attempts %d | total success/failure/timeout/dnf %d/%d/%d/%d | avg mistakes %.2f | review-backed %d | coach-review %d | short set %d | selector split %s | tie-break support %s | template-signal support %s | template-signal decision %s | template-signal support-bias snapshot %s | template-signal confidence snapshot %s | peer support split %s | comparable-peer confidence %s | last launch %s | last completed %s"), + TEXT("avg recommended %d | avg focus %d | avg completed attempts %d | total success/failure/timeout/dnf %d/%d/%d/%d | avg mistakes %.2f | review-backed %d | coach-review %d | short set %d | selector split %s | tie-break support %s | template-signal support %s | template-signal decision %s | template-signal support-bias snapshot %s | template-signal decision-bias snapshot %s | template-signal confidence snapshot %s | peer support split %s | comparable-peer confidence %s | last launch %s | last completed %s"), TemplateStats->AverageRecommendedCaseCount, TemplateStats->AverageFocusCaseCount, TemplateStats->AverageCompletedAttemptCount, @@ -259,6 +263,8 @@ namespace HyperTwistCoachDashboardWidgetInternal TemplateStats), *BuildTemplateLaunchAnalyticsTieBreakTemplateSignalSupportBiasSnapshotFragment( TemplateStats), + *BuildTemplateLaunchAnalyticsTieBreakTemplateSignalDecisionBiasSnapshotFragment( + TemplateStats), *BuildTemplateLaunchAnalyticsTieBreakTemplateSignalConfidenceSnapshotFragment( TemplateStats), *BuildTemplateLaunchAnalyticsTieBreakPeerDispositionSplitFragment(TemplateStats), @@ -977,6 +983,57 @@ namespace HyperTwistCoachDashboardWidgetInternal return Fragment; } + FString BuildTemplateLaunchAnalyticsTieBreakTemplateSignalDecisionBiasSnapshotFragment( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats + ) + { + if (TemplateStats == nullptr || !TemplateStats->IsStructurallyValid()) + { + return TEXT( + "no template-signal decision-bias snapshot split is available for this template id yet"); + } + + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats& EarnedStats = + TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotEarnedStats; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats& SuppressedStats = + TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotSuppressedStats; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats& NoRetainedStats = + TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotNoRetainedStats; + if (!EarnedStats.IsStructurallyValid() + && !SuppressedStats.IsStructurallyValid() + && !NoRetainedStats.IsStructurallyValid()) + { + return TEXT( + "no template-signal decision-bias snapshot split is available for this template id yet"); + } + + FString Fragment = FString::Printf( + TEXT("%s | %s | %s"), + *BuildTemplateLaunchSelectionLaneAnalyticsSummary(EarnedStats, TEXT("earned")), + *BuildTemplateLaunchSelectionLaneAnalyticsSummary(SuppressedStats, TEXT("suppressed")), + *BuildTemplateLaunchSelectionLaneAnalyticsSummary( + NoRetainedStats, + TEXT("no-retained")) + ); + if (EarnedStats.IsStructurallyValid() + && SuppressedStats.IsStructurallyValid() + && EarnedStats.GetOutcomeRunCount() > 0 + && SuppressedStats.GetOutcomeRunCount() > 0) + { + Fragment += FString::Printf( + TEXT(" | earned-minus-suppressed delta success %+.2f | delta mistakes %+.2f"), + EarnedStats.OverallSuccessRate - SuppressedStats.OverallSuccessRate, + EarnedStats.AverageMistakesPerOutcomeRun + - SuppressedStats.AverageMistakesPerOutcomeRun + ); + } + + return Fragment; + } + FString BuildTemplateLaunchAnalyticsTieBreakPeerDispositionSplitFragment( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp index d667e64..eeec8ee 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp @@ -9180,6 +9180,12 @@ UHyperTwistTrainingRepositoryLibrary::ListTemplateLaunchScopedStatsForUser( DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotSuppressedAccumulator; FSelectionScopedAccumulator DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotNoRetainedAccumulator; + FSelectionScopedAccumulator + DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotEarnedAccumulator; + FSelectionScopedAccumulator + DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotSuppressedAccumulator; + FSelectionScopedAccumulator + DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotNoRetainedAccumulator; FSelectionScopedAccumulator DashboardAnalyticsTieBreakTemplateScopedSignalNoRetainedComparablePeerConfidenceLaneAccumulator; FSelectionScopedAccumulator @@ -9433,6 +9439,38 @@ UHyperTwistTrainingRepositoryLibrary::ListTemplateLaunchScopedStatsForUser( Provenance.SelectionProvenance ); } + const EHyperTwistTrainingTemplateLaunchTemplateSignalSupportDecisionBiasSnapshotState + DecisionBiasSnapshotState = + Provenance.SelectionProvenance + .TemplateSignalSupportDecisionBiasSnapshot + .State; + if (DecisionBiasSnapshotState == + EHyperTwistTrainingTemplateLaunchTemplateSignalSupportDecisionBiasSnapshotState::Earned) + { + AccumulateSelectionScopedStats( + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotEarnedAccumulator, + Provenance.SelectionProvenance + ); + } + else if ( + DecisionBiasSnapshotState == + EHyperTwistTrainingTemplateLaunchTemplateSignalSupportDecisionBiasSnapshotState::Suppressed) + { + AccumulateSelectionScopedStats( + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotSuppressedAccumulator, + Provenance.SelectionProvenance + ); + } + else + { + AccumulateSelectionScopedStats( + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotNoRetainedAccumulator, + Provenance.SelectionProvenance + ); + } const EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState SnapshotState = Provenance.SelectionProvenance.ComparablePeerConfidenceSnapshot.State; @@ -9771,6 +9809,21 @@ UHyperTwistTrainingRepositoryLibrary::ListTemplateLaunchScopedStatsForUser( Accumulator .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotNoRetainedAccumulator .Stats; + Accumulator.Stats + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotEarnedStats = + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotEarnedAccumulator + .Stats; + Accumulator.Stats + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotSuppressedStats = + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotSuppressedAccumulator + .Stats; + Accumulator.Stats + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotNoRetainedStats = + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotNoRetainedAccumulator + .Stats; Accumulator.Stats .DashboardAnalyticsTieBreakTemplateScopedSignalNoRetainedComparablePeerConfidenceLaneStats = Accumulator diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 0ed66b0..c26aecb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -2388,6 +2388,18 @@ struct FHyperTwistTrainingTemplateLaunchScopedStats FHyperTwistTrainingTemplateLaunchSelectionScopedStats DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotNoRetainedStats; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingTemplateLaunchSelectionScopedStats + DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotEarnedStats; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingTemplateLaunchSelectionScopedStats + DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotSuppressedStats; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingTemplateLaunchSelectionScopedStats + DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotNoRetainedStats; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistTrainingTemplateLaunchSelectionScopedStats DashboardAnalyticsTieBreakTemplateScopedSignalNoRetainedComparablePeerConfidenceLaneStats; diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_DECISION_BIAS_SNAPSHOT_ANALYTICS_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_DECISION_BIAS_SNAPSHOT_ANALYTICS_PACKET_2026-05-07.md new file mode 100644 index 0000000..7810311 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_DECISION_BIAS_SNAPSHOT_ANALYTICS_PACKET_2026-05-07.md @@ -0,0 +1,48 @@ +# HyperTwist Phase 4 template-signal decision-bias snapshot analytics packet + +## Purpose + +This packet pushes the newly persisted at-launch template-signal decision-bias snapshot into repo-backed template analytics. Broader `TemplateScopedOutcomeSignal` launches can now be compared by whether the reinforced-vs-plain snapshot-guard lane was already `earned`, still `suppressed`, or not retained yet at launch time. + +## Scope + +- widen per-template launch scoped stats with decision-bias snapshot buckets for: + - `earned` + - `suppressed` + - `no-retained` +- route broader template-signal dashboard analytics launches into those buckets from persisted selection provenance +- surface the new split through the shared dashboard analytics detail + +## What landed + +- `HyperTwistTrainingTypes.h` + - added repo-backed scoped stats for: + - `DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotEarnedStats` + - `DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotSuppressedStats` + - `DashboardAnalyticsTieBreakTemplateScopedSignalSupportDecisionBiasSnapshotNoRetainedStats` +- `HyperTwistTrainingRepositoryLibrary.cpp` + - widened the template launch scoped accumulator with matching decision-bias snapshot buckets + - routes broader `TemplateScopedOutcomeSignal` launches by persisted + `TemplateSignalSupportDecisionBiasSnapshot.State` + - treats older runs with no retained snapshot as `no-retained` +- `HyperTwistCoachDashboardWidget.cpp` + - added shared `template-signal decision-bias snapshot` analytics detail + - appends `earned-minus-suppressed` success and mistake deltas when both lanes have outcome history + +## Product effect + +- template analytics now show whether broader analytics picks launched with an already-earned reinforced-vs-plain guard are outperforming picks made while that lane was still suppressed +- history, recap, and live dashboard template analytics all inherit the same repo-backed split through the shared detail fragment + +## Acceptance criteria + +- repo-backed template analytics persist `earned`, `suppressed`, and `no-retained` buckets for at-launch decision-bias snapshot state +- broader `TemplateScopedOutcomeSignal` launches are routed by the stored snapshot state, not recomputed current guard state +- shared dashboard analytics detail shows the new `template-signal decision-bias snapshot` split + +## Validation checklist + +1. full `Development Editor|Win64` build succeeds +2. confirm repository analytics route broader template-signal launches by `TemplateSignalSupportDecisionBiasSnapshot.State` +3. confirm template scoped stats expose the three new decision-bias snapshot buckets +4. confirm shared analytics detail shows `template-signal decision-bias snapshot` with `earned`, `suppressed`, and `no-retained` lanes