From 2300045f63251fa9b7d1a7cebd6c86ba6a50be5e Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 04:49:26 +0200 Subject: [PATCH] Split template analytics by decision-bias kind --- .../HyperTwistCoachDashboardWidget.cpp | 30 ++++++++++++ .../HyperTwistTrainingRepositoryLibrary.cpp | 48 +++++++++++++++++++ .../HyperTwistTrainingTypes.h | 8 ++++ ...CISION_BIAS_ANALYTICS_PACKET_2026-05-07.md | 46 ++++++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_DECISION_BIAS_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 a9db9fc..7572fdb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -867,6 +867,36 @@ namespace HyperTwistCoachDashboardWidgetInternal SnapshotGuardOutcomeRuns, NoSnapshotGuardOutcomeRuns ); + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats& PlainSnapshotGuardStats = + TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasStats; + const FHyperTwistTrainingTemplateLaunchSelectionScopedStats& DecisionSplitGuardStats = + TemplateStats + ->DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardStats; + if (PlainSnapshotGuardStats.IsStructurallyValid() + || DecisionSplitGuardStats.IsStructurallyValid()) + { + Fragment += FString::Printf( + TEXT(" | %s | %s"), + *BuildTemplateLaunchSelectionLaneAnalyticsSummary( + PlainSnapshotGuardStats, + TEXT("plain-snapshot-guard")), + *BuildTemplateLaunchSelectionLaneAnalyticsSummary( + DecisionSplitGuardStats, + TEXT("decision-split-guard")) + ); + if (PlainSnapshotGuardStats.GetOutcomeRunCount() > 0 + && DecisionSplitGuardStats.GetOutcomeRunCount() > 0) + { + Fragment += FString::Printf( + TEXT(" | decision-split-minus-plain delta success %+.2f | delta mistakes %+.2f"), + DecisionSplitGuardStats.OverallSuccessRate + - PlainSnapshotGuardStats.OverallSuccessRate, + DecisionSplitGuardStats.AverageMistakesPerOutcomeRun + - PlainSnapshotGuardStats.AverageMistakesPerOutcomeRun + ); + } + } return Fragment; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp index d89dfbe..d667e64 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp @@ -9170,6 +9170,10 @@ UHyperTwistTrainingRepositoryLibrary::ListTemplateLaunchScopedStatsForUser( DashboardAnalyticsTieBreakTemplateScopedSignalNoSupportBiasSnapshotGuardAccumulator; FSelectionScopedAccumulator DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardAccumulator; + FSelectionScopedAccumulator + DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasAccumulator; + FSelectionScopedAccumulator + DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardAccumulator; FSelectionScopedAccumulator DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotEarnedAccumulator; FSelectionScopedAccumulator @@ -9372,6 +9376,24 @@ UHyperTwistTrainingRepositoryLibrary::ListTemplateLaunchScopedStatsForUser( .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardAccumulator, Provenance.SelectionProvenance ); + if (Provenance.SelectionProvenance.TemplateSignalSupportDecisionBiasKind == + EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasKind:: + DecisionSplitGuard) + { + AccumulateSelectionScopedStats( + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardAccumulator, + Provenance.SelectionProvenance + ); + } + else + { + AccumulateSelectionScopedStats( + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasAccumulator, + Provenance.SelectionProvenance + ); + } } else { @@ -9594,6 +9616,14 @@ UHyperTwistTrainingRepositoryLibrary::ListTemplateLaunchScopedStatsForUser( Accumulator .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardAccumulator ); + FinalizeSelectionScopedAccumulator( + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasAccumulator + ); + FinalizeSelectionScopedAccumulator( + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardAccumulator + ); FinalizeSelectionScopedAccumulator( Accumulator .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotEarnedAccumulator @@ -9658,6 +9688,14 @@ UHyperTwistTrainingRepositoryLibrary::ListTemplateLaunchScopedStatsForUser( .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardAccumulator .Stats.PeerSupportKindDisposition = EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition::None; + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasAccumulator + .Stats.PeerSupportKindDisposition = + EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition::None; + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardAccumulator + .Stats.PeerSupportKindDisposition = + EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition::None; Accumulator .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotEarnedAccumulator .Stats.PeerSupportKindDisposition = @@ -9708,6 +9746,16 @@ UHyperTwistTrainingRepositoryLibrary::ListTemplateLaunchScopedStatsForUser( Accumulator .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardAccumulator .Stats; + Accumulator.Stats + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasStats = + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasAccumulator + .Stats; + Accumulator.Stats + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardStats = + Accumulator + .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardAccumulator + .Stats; Accumulator.Stats .DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotEarnedStats = Accumulator diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 7b8d43f..4625add 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -2332,6 +2332,14 @@ struct FHyperTwistTrainingTemplateLaunchScopedStats FHyperTwistTrainingTemplateLaunchSelectionScopedStats DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardStats; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingTemplateLaunchSelectionScopedStats + DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasStats; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingTemplateLaunchSelectionScopedStats + DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardStats; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistTrainingTemplateLaunchSelectionScopedStats DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotEarnedStats; diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_DECISION_BIAS_ANALYTICS_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_DECISION_BIAS_ANALYTICS_PACKET_2026-05-07.md new file mode 100644 index 0000000..f6e5322 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_DECISION_BIAS_ANALYTICS_PACKET_2026-05-07.md @@ -0,0 +1,46 @@ +# HyperTwist Phase 4 template-signal decision-bias analytics packet + +## Purpose + +This packet aggregates the newly persisted template-signal decision-bias provenance back into repo-backed template analytics. The dashboard can now compare plain `SupportBiasSnapshotGuard` launches against the stricter `DecisionSplitGuard`-reinforced subset instead of treating every snapshot-guard win as the same lane. + +## Scope + +- add repo-backed template-scoped stats buckets for `SupportBiasSnapshotGuard` runs with and without the persisted `DecisionSplitGuard` bias marker +- route stored `TemplateSignalSupportDecisionBiasKind` through template launch scoped analytics derivation +- extend the shared `template-signal decision` analytics fragment so live dashboard detail, template history, and recap expose the new nested snapshot-guard split +- report outcome deltas when both plain snapshot-guard and decision-split-guard lanes have outcome history + +## What landed + +- `HyperTwistTrainingTypes.h` + - added `DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardNoDecisionBiasStats` + - added `DashboardAnalyticsTieBreakTemplateScopedSignalSupportBiasSnapshotGuardDecisionSplitGuardStats` +- `HyperTwistTrainingRepositoryLibrary.cpp` + - added matching scoped accumulators + - `SupportBiasSnapshotGuard` runs with stored `TemplateSignalSupportDecisionBiasKind = DecisionSplitGuard` now flow into the reinforced bucket + - `SupportBiasSnapshotGuard` runs without that retained bias kind, including older records, flow into the plain snapshot-guard bucket +- `HyperTwistCoachDashboardWidget.cpp` + - extended `BuildTemplateLaunchAnalyticsTieBreakTemplateSignalDecisionSplitFragment(...)` + - shared analytics detail now appends plain snapshot-guard vs decision-split-guard lane summaries under the existing decision split + - nested split shows `decision-split-minus-plain` success and mistake deltas when both lanes have outcome history + +## Product effect + +- operators can now see whether the stricter `DecisionSplitGuard`-reinforced subset of snapshot-guard launches is actually outperforming plain snapshot-guard launches +- template launch history and recap inherit the same nested split automatically because they already reuse the shared analytics detail formatter +- older snapshot-guard runs remain analyzable without migration because missing decision-bias provenance is treated as plain snapshot-guard history + +## Acceptance criteria + +- repo-backed template launch scoped stats retain separate snapshot-guard buckets for plain wins and `DecisionSplitGuard`-reinforced wins +- shared dashboard analytics detail renders the nested snapshot-guard split anywhere template launch analytics are shown +- nested decision-bias deltas appear only when both snapshot-guard lanes have outcome history +- older snapshot-guard runs without retained `TemplateSignalSupportDecisionBiasKind` continue to participate in analytics through the plain snapshot-guard bucket + +## Validation checklist + +1. full `Development Editor|Win64` build succeeds +2. confirm scoped analytics route stored `TemplateSignalSupportDecisionBiasKind = DecisionSplitGuard` into the reinforced bucket +3. confirm shared analytics detail shows `plain-snapshot-guard ... | decision-split-guard ...` +4. confirm older snapshot-guard runs without retained decision-bias provenance still surface under `plain-snapshot-guard`