From a46404dda63792b54e34bc4be6f7a19a1bd9b67f Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 03:36:18 +0200 Subject: [PATCH] Persist template-signal support-bias snapshot --- .../HyperTwistCoachDashboardWidget.cpp | 88 +++++++++++++++++-- .../HyperTwistTrainingTypes.h | 33 +++++++ ...S_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md | 53 +++++++++++ 3 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_BIAS_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 6246d5b..29b645f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -138,6 +138,11 @@ namespace HyperTwistCoachDashboardWidgetInternal const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); + FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot + BuildTemplateLaunchTemplateSignalSupportBiasSnapshot( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats + ); + FString BuildTemplateLaunchComparablePeerConfidenceAuditClause( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, const TCHAR* SubjectLabel @@ -156,6 +161,10 @@ namespace HyperTwistCoachDashboardWidgetInternal const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance ); + FString BuildTemplateLaunchSelectionTemplateSignalSupportBiasDetailFragment( + const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance + ); + bool HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, int32* OutComparablePeerOutcomeRuns = nullptr, @@ -369,7 +378,7 @@ namespace HyperTwistCoachDashboardWidgetInternal const FHyperTwistTrainingSessionTemplate& PeerTemplate, const FHyperTwistTrainingTemplateLaunchScopedStats* PeerStats, const EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind AnalyticsSupportKind, - const bool bUsedTemplateSignalConfidenceSnapshotBias = false + const bool bUsedTemplateSignalSupportBias = false ) { FHyperTwistTrainingTemplateLaunchSelectionProvenance Provenance; @@ -417,21 +426,23 @@ namespace HyperTwistCoachDashboardWidgetInternal } else { - Provenance.TemplateSignalSupportKind = bUsedTemplateSignalConfidenceSnapshotBias + Provenance.TemplateSignalSupportKind = bUsedTemplateSignalSupportBias ? EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportKind:: ConfidenceLaneBias : EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportKind:: BroaderOutcomeOnly; + Provenance.TemplateSignalSupportBiasSnapshot = + BuildTemplateLaunchTemplateSignalSupportBiasSnapshot(SelectedStats); Provenance.ComparablePeerConfidenceSnapshot = BuildTemplateLaunchComparablePeerConfidenceSnapshot(SelectedStats); const FString SelectedComparablePeerConfidenceAuditClause = BuildTemplateLaunchComparablePeerConfidenceAuditClause( SelectedStats, TEXT("chosen template")); - if (bUsedTemplateSignalConfidenceSnapshotBias) + if (bUsedTemplateSignalSupportBias) { Provenance.DecisionSummaryLine = FString::Printf( - TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s on broader template-scoped outcome signal with retained confidence-lane bias support: %s | chosen %s | peer %s."), + TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s on broader template-scoped outcome signal with retained confidence-lane support-bias reinforcement: %s | chosen %s | peer %s."), *ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate), *ResolveDashboardTemplateLaunchDisplayTitle(SelectedTemplate), *SelectedComparablePeerConfidenceAuditClause, @@ -442,7 +453,7 @@ namespace HyperTwistCoachDashboardWidgetInternal else { Provenance.DecisionSummaryLine = FString::Printf( - TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s on broader template-scoped outcome signal without selector-split confidence support or retained confidence-lane bias support: %s | chosen %s | peer %s."), + TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s on broader template-scoped outcome signal without support-bias reinforcement deciding the winner: %s | chosen %s | peer %s."), *ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate), *ResolveDashboardTemplateLaunchDisplayTitle(SelectedTemplate), *SelectedComparablePeerConfidenceAuditClause, @@ -955,6 +966,23 @@ namespace HyperTwistCoachDashboardWidgetInternal return Snapshot; } + FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot + BuildTemplateLaunchTemplateSignalSupportBiasSnapshot( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats + ) + { + FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot Snapshot; + const bool bBiasEarned = HasTemplateLaunchTemplateSignalSupportBias( + TemplateStats, + &Snapshot.ConfidenceLaneBiasOutcomeRuns, + &Snapshot.BroaderOutcomeOnlyOutcomeRuns + ); + Snapshot.State = bBiasEarned + ? EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState::Earned + : EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState::Suppressed; + return Snapshot; + } + FString BuildTemplateLaunchComparablePeerConfidenceStatusClauseFromSnapshot( const FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot& Snapshot ) @@ -1076,6 +1104,46 @@ namespace HyperTwistCoachDashboardWidgetInternal ); } + FString BuildTemplateLaunchSelectionTemplateSignalSupportBiasDetailFragment( + const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance + ) + { + if (SelectionProvenance == nullptr + || SelectionProvenance->ReasonClass + != EHyperTwistTrainingTemplateLaunchSelectionReasonClass::AnalyticsTieBreak + || SelectionProvenance->AnalyticsSupportKind + != EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind:: + TemplateScopedOutcomeSignal + || !SelectionProvenance->TemplateSignalSupportBiasSnapshot.IsStructurallyValid()) + { + return FString(); + } + + const FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot& Snapshot = + SelectionProvenance->TemplateSignalSupportBiasSnapshot; + switch (Snapshot.State) + { + case EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState::Earned: + return FString::Printf( + TEXT( + " | at-launch support-bias earned on confidence-lane-bias history (%d bias outcomes vs %d broader outcomes)"), + Snapshot.ConfidenceLaneBiasOutcomeRuns, + Snapshot.BroaderOutcomeOnlyOutcomeRuns + ); + case EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState:: + Suppressed: + return FString::Printf( + TEXT( + " | at-launch support-bias suppressed (%d bias outcomes vs %d broader outcomes)"), + Snapshot.ConfidenceLaneBiasOutcomeRuns, + Snapshot.BroaderOutcomeOnlyOutcomeRuns + ); + case EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState::None: + default: + return FString(); + } + } + FString BuildTemplateLaunchSelectionComparablePeerConfidenceDetailFragment( const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance ) @@ -1142,6 +1210,9 @@ namespace HyperTwistCoachDashboardWidgetInternal const FString TemplateSignalSupportDetailFragment = BuildTemplateLaunchSelectionTemplateSignalSupportDetailFragment( SelectionProvenance); + const FString TemplateSignalSupportBiasDetailFragment = + BuildTemplateLaunchSelectionTemplateSignalSupportBiasDetailFragment( + SelectionProvenance); const FString PeerSupportDispositionDetailFragment = BuildTemplateLaunchSelectionPeerSupportDispositionDetailFragment(SelectionProvenance); const FString ComparablePeerConfidenceDetailFragment = @@ -1149,6 +1220,7 @@ namespace HyperTwistCoachDashboardWidgetInternal SelectionProvenance); const FString SelectionReasonDetailFragment = AnalyticsSupportDetailFragment + TemplateSignalSupportDetailFragment + + TemplateSignalSupportBiasDetailFragment + PeerSupportDispositionDetailFragment + ComparablePeerConfidenceDetailFragment; if (!SelectionProvenance->PeerTemplateTitle.IsEmpty() @@ -2029,7 +2101,7 @@ namespace HyperTwistCoachDashboardWidgetInternal bool bPreferCandidate = false; EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind CandidateAnalyticsSupportKind = EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind::None; - bool bCandidateUsedTemplateSignalConfidenceSnapshotBias = false; + bool bCandidateUsedTemplateSignalSupportBias = false; }; FTemplateLaunchAnalyticsPreferenceDecision ResolveTemplateAnalyticsPreferenceDecision( @@ -2082,7 +2154,7 @@ namespace HyperTwistCoachDashboardWidgetInternal Decision.CandidateAnalyticsSupportKind = EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind:: TemplateScopedOutcomeSignal; - Decision.bCandidateUsedTemplateSignalConfidenceSnapshotBias = true; + Decision.bCandidateUsedTemplateSignalSupportBias = true; } return Decision; } @@ -8835,7 +8907,7 @@ const FHyperTwistTrainingSessionTemplate* UHyperTwistCoachDashboardWidget::FindP PreviousPreferredTemplate, PreferredStats, AnalyticsDecision.CandidateAnalyticsSupportKind, - AnalyticsDecision.bCandidateUsedTemplateSignalConfidenceSnapshotBias + AnalyticsDecision.bCandidateUsedTemplateSignalSupportBias ); } } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 1378c9e..f400ace 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -1082,6 +1082,14 @@ enum class EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportKind : ConfidenceLaneBias UMETA(DisplayName = "Confidence Lane Bias") }; +UENUM(BlueprintType) +enum class EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState : uint8 +{ + None UMETA(DisplayName = "None"), + Earned UMETA(DisplayName = "Earned"), + Suppressed UMETA(DisplayName = "Suppressed") +}; + UENUM(BlueprintType) enum class EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState : uint8 { @@ -1092,6 +1100,27 @@ enum class EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotStat SuppressedPeerNoEvidenceHeavy UMETA(DisplayName = "Suppressed Peer No Evidence Heavy") }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState State = + EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ConfidenceLaneBiasOutcomeRuns = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BroaderOutcomeOnlyOutcomeRuns = 0; + + bool IsStructurallyValid() const + { + return State != EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState::None; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot { @@ -1145,6 +1174,10 @@ struct FHyperTwistTrainingTemplateLaunchSelectionProvenance TemplateSignalSupportKind = EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportKind::None; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot + TemplateSignalSupportBiasSnapshot; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot ComparablePeerConfidenceSnapshot; diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_BIAS_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_BIAS_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md new file mode 100644 index 0000000..3d0cfab --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_BIAS_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md @@ -0,0 +1,53 @@ +# HyperTwist Phase 4 template-signal support-bias snapshot provenance packet + +## Purpose + +This packet persists the broader template-signal `support-bias earned/suppressed` state into template selection provenance at launch time. That keeps later audits honest: template history, recap, and other selector-detail surfaces now read the stored at-launch support-bias posture instead of inferring it from later analytics drift. + +## Scope + +- add a compact template-signal support-bias snapshot under template selection provenance +- capture: + - whether support-bias was `earned` or `suppressed` at launch + - the chosen template's retained `confidence-lane-bias` outcome count + - the chosen template's retained `broader-outcome-only` outcome count +- stamp that snapshot whenever the dashboard records a broader `TemplateScopedOutcomeSignal` analytics tie-break launch +- append the stored `at-launch support-bias` fragment through the shared selector-detail formatter used by live detail, template history, and recap +- correct broader-outcome decision-summary wording so it no longer claims the chosen template lacked retained support-bias when that bias simply did not decide the winner + +## What landed + +- `HyperTwistTrainingTypes.h` + - added `EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotState` + - added `FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot` + - nested `TemplateSignalSupportBiasSnapshot` under `FHyperTwistTrainingTemplateLaunchSelectionProvenance` +- `HyperTwistCoachDashboardWidget.cpp` + - added `BuildTemplateLaunchTemplateSignalSupportBiasSnapshot(...)` + - broader analytics provenance now stamps the selected template's support-bias snapshot at launch + - added shared selector-detail rendering for: + - `at-launch support-bias earned` + - `at-launch support-bias suppressed` + - renamed the local broader-analytics decision flag from the stale confidence-snapshot name to the actual support-bias meaning + - broader-outcome decision-summary text now says the winner launched without support-bias reinforcement deciding the outcome, rather than claiming the chosen template lacked retained support-bias entirely + +## Product effect + +- template history and recap now preserve the dashboard's at-launch support-bias posture for broader analytics launches +- selector-detail audits can distinguish: + - what kind of broader template-signal support actually decided the winner + - versus whether support-bias was merely present or suppressed at launch +- stored decision summaries no longer overstate the absence of retained support-bias when it existed but did not change the winner + +## Acceptance criteria + +- broader `TemplateScopedOutcomeSignal` launch provenance persists an `earned/suppressed` support-bias snapshot with both retained outcome counts +- shared selector detail shows the stored at-launch support-bias fragment in live detail, template history, and recap +- older runs without the new snapshot keep rendering without fabricated support-bias detail +- broader-outcome decision summaries avoid claiming the chosen template lacked retained support-bias when that state was simply non-decisive + +## Validation checklist + +1. full `Development Editor|Win64` build succeeds +2. confirm broader analytics provenance stamps `TemplateSignalSupportBiasSnapshot` +3. confirm shared selector detail renders stored `at-launch support-bias earned/suppressed` +4. confirm broader-outcome decision summaries use the corrected non-decisive wording