From 2f4b0afcc408652af705c1992194be9b4d20b65c Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 04:01:08 +0200 Subject: [PATCH] Persist template support snapshot-guard provenance --- .../HyperTwistCoachDashboardWidget.cpp | 66 ++++++++++++++++--- .../HyperTwistTrainingTypes.h | 12 ++++ ...SHOT_GUARD_PROVENANCE_PACKET_2026-05-07.md | 48 ++++++++++++++ 3 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_SNAPSHOT_GUARD_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 cd5be32..8a3af9b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -327,6 +327,22 @@ namespace HyperTwistCoachDashboardWidgetInternal } } + FString ResolveTemplateLaunchSelectionTemplateSignalSupportDecisionKindLabel( + const EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind + TemplateSignalSupportDecisionKind + ) + { + switch (TemplateSignalSupportDecisionKind) + { + case EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind:: + SupportBiasSnapshotGuard: + return TEXT("support-bias-snapshot-guard"); + case EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind::None: + default: + return TEXT("none"); + } + } + FString ResolveTemplateLaunchSelectionPeerSupportKindDispositionLabel( const EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition PeerSupportKindDisposition @@ -390,7 +406,7 @@ namespace HyperTwistCoachDashboardWidgetInternal const FHyperTwistTrainingSessionTemplate& PeerTemplate, const FHyperTwistTrainingTemplateLaunchScopedStats* PeerStats, const EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind AnalyticsSupportKind, - const bool bUsedTemplateSignalSupportBias = false + const bool bUsedTemplateSignalSupportBiasSnapshotGuard = false ) { FHyperTwistTrainingTemplateLaunchSelectionProvenance Provenance; @@ -438,11 +454,17 @@ namespace HyperTwistCoachDashboardWidgetInternal } else { - Provenance.TemplateSignalSupportKind = bUsedTemplateSignalSupportBias + Provenance.TemplateSignalSupportKind = bUsedTemplateSignalSupportBiasSnapshotGuard ? EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportKind:: ConfidenceLaneBias : EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportKind:: BroaderOutcomeOnly; + Provenance.TemplateSignalSupportDecisionKind = + bUsedTemplateSignalSupportBiasSnapshotGuard + ? EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind:: + SupportBiasSnapshotGuard + : EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind:: + None; Provenance.TemplateSignalSupportBiasSnapshot = BuildTemplateLaunchTemplateSignalSupportBiasSnapshot(SelectedStats); Provenance.ComparablePeerConfidenceSnapshot = @@ -451,10 +473,10 @@ namespace HyperTwistCoachDashboardWidgetInternal BuildTemplateLaunchComparablePeerConfidenceAuditClause( SelectedStats, TEXT("chosen template")); - if (bUsedTemplateSignalSupportBias) + if (bUsedTemplateSignalSupportBiasSnapshotGuard) { 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 support-bias reinforcement: %s | chosen %s | peer %s."), + TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s on broader template-scoped outcome signal with earned-vs-non-earned support-bias snapshot guard reinforcement deciding the winner: %s | chosen %s | peer %s."), *ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate), *ResolveDashboardTemplateLaunchDisplayTitle(SelectedTemplate), *SelectedComparablePeerConfidenceAuditClause, @@ -465,7 +487,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 support-bias reinforcement deciding the winner: %s | chosen %s | peer %s."), + TEXT("Selector: tied with %s on primary dashboard priority; analytics preferred %s on broader template-scoped outcome signal without the earned-vs-non-earned support-bias snapshot guard deciding the winner: %s | chosen %s | peer %s."), *ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate), *ResolveDashboardTemplateLaunchDisplayTitle(SelectedTemplate), *SelectedComparablePeerConfidenceAuditClause, @@ -1181,6 +1203,29 @@ namespace HyperTwistCoachDashboardWidgetInternal ); } + FString BuildTemplateLaunchSelectionTemplateSignalSupportDecisionDetailFragment( + const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance + ) + { + if (SelectionProvenance == nullptr + || SelectionProvenance->ReasonClass + != EHyperTwistTrainingTemplateLaunchSelectionReasonClass::AnalyticsTieBreak + || SelectionProvenance->AnalyticsSupportKind + != EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind:: + TemplateScopedOutcomeSignal + || SelectionProvenance->TemplateSignalSupportDecisionKind + == EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind::None) + { + return FString(); + } + + return FString::Printf( + TEXT(" | template-signal decision %s"), + *ResolveTemplateLaunchSelectionTemplateSignalSupportDecisionKindLabel( + SelectionProvenance->TemplateSignalSupportDecisionKind) + ); + } + FString BuildTemplateLaunchSelectionTemplateSignalSupportBiasDetailFragment( const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance ) @@ -1287,6 +1332,9 @@ namespace HyperTwistCoachDashboardWidgetInternal const FString TemplateSignalSupportDetailFragment = BuildTemplateLaunchSelectionTemplateSignalSupportDetailFragment( SelectionProvenance); + const FString TemplateSignalSupportDecisionDetailFragment = + BuildTemplateLaunchSelectionTemplateSignalSupportDecisionDetailFragment( + SelectionProvenance); const FString TemplateSignalSupportBiasDetailFragment = BuildTemplateLaunchSelectionTemplateSignalSupportBiasDetailFragment( SelectionProvenance); @@ -1297,6 +1345,7 @@ namespace HyperTwistCoachDashboardWidgetInternal SelectionProvenance); const FString SelectionReasonDetailFragment = AnalyticsSupportDetailFragment + TemplateSignalSupportDetailFragment + + TemplateSignalSupportDecisionDetailFragment + TemplateSignalSupportBiasDetailFragment + PeerSupportDispositionDetailFragment + ComparablePeerConfidenceDetailFragment; @@ -2288,7 +2337,7 @@ namespace HyperTwistCoachDashboardWidgetInternal bool bPreferCandidate = false; EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind CandidateAnalyticsSupportKind = EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind::None; - bool bCandidateUsedTemplateSignalSupportBias = false; + bool bCandidateUsedTemplateSignalSupportBiasSnapshotGuard = false; }; FTemplateLaunchAnalyticsPreferenceDecision ResolveTemplateAnalyticsPreferenceDecision( @@ -2347,7 +2396,7 @@ namespace HyperTwistCoachDashboardWidgetInternal Decision.CandidateAnalyticsSupportKind = EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind:: TemplateScopedOutcomeSignal; - Decision.bCandidateUsedTemplateSignalSupportBias = true; + Decision.bCandidateUsedTemplateSignalSupportBiasSnapshotGuard = true; } return Decision; } @@ -9100,7 +9149,8 @@ const FHyperTwistTrainingSessionTemplate* UHyperTwistCoachDashboardWidget::FindP PreviousPreferredTemplate, PreferredStats, AnalyticsDecision.CandidateAnalyticsSupportKind, - AnalyticsDecision.bCandidateUsedTemplateSignalSupportBias + AnalyticsDecision + .bCandidateUsedTemplateSignalSupportBiasSnapshotGuard ); } } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 5781e8b..3016fe7 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -1090,6 +1090,13 @@ enum class EHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshotSta Suppressed UMETA(DisplayName = "Suppressed") }; +UENUM(BlueprintType) +enum class EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind : uint8 +{ + None UMETA(DisplayName = "None"), + SupportBiasSnapshotGuard UMETA(DisplayName = "Support Bias Snapshot Guard") +}; + UENUM(BlueprintType) enum class EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState : uint8 { @@ -1174,6 +1181,11 @@ struct FHyperTwistTrainingTemplateLaunchSelectionProvenance TemplateSignalSupportKind = EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportKind::None; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind + TemplateSignalSupportDecisionKind = + EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind::None; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot TemplateSignalSupportBiasSnapshot; diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_SNAPSHOT_GUARD_PROVENANCE_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_SNAPSHOT_GUARD_PROVENANCE_PACKET_2026-05-07.md new file mode 100644 index 0000000..95421ce --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_SIGNAL_SUPPORT_SNAPSHOT_GUARD_PROVENANCE_PACKET_2026-05-07.md @@ -0,0 +1,48 @@ +# HyperTwist Phase 4 template-signal support snapshot-guard provenance packet + +## Purpose + +This packet persists when the new earned-vs-non-earned support-bias snapshot guard actually changes a tied broader template-signal winner. That keeps selector audits precise: dashboard detail, template history, and recap can now distinguish plain legacy `confidence-lane-bias` wins from newer wins that were specifically broken by the historical support-bias snapshot guard. + +## Scope + +- add a compact template-signal support decision enum under template launch selection provenance +- stamp that field only when the earned-vs-non-earned support-bias snapshot guard decides a broader template-signal tie +- update broader template-signal decision summary wording so it explicitly names the snapshot guard when it decides the winner +- append the stored decision kind through the shared selector-detail formatter used by live detail, template history, and recap +- tighten internal naming so the runtime decision flag matches the actual snapshot-guard semantics + +## What landed + +- `HyperTwistTrainingTypes.h` + - added `EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionKind` + - nested `TemplateSignalSupportDecisionKind` under `FHyperTwistTrainingTemplateLaunchSelectionProvenance` +- `HyperTwistCoachDashboardWidget.cpp` + - added `ResolveTemplateLaunchSelectionTemplateSignalSupportDecisionKindLabel(...)` + - broader analytics provenance now stamps: + - `SupportBiasSnapshotGuard` when the historical earned-vs-non-earned support-bias snapshot guard breaks the tie + - broader analytics decision summaries now distinguish: + - winner decided by the snapshot guard + - winner chosen without that snapshot guard deciding the outcome + - shared selector detail now appends `template-signal decision support-bias-snapshot-guard` for runs that used the new guard + - renamed the local decision flag to `bCandidateUsedTemplateSignalSupportBiasSnapshotGuard` + +## Product effect + +- live `[Template Launch]` detail now explicitly shows when a broader template-signal winner came from the historical support-bias snapshot guard +- template history and recap preserve that same distinction from stored provenance +- older `confidence-lane-bias` runs without the new persisted decision kind remain distinguishable because they do not show the new snapshot-guard detail + +## Acceptance criteria + +- broader template-signal selection provenance persists a distinct decision kind when the support-bias snapshot guard changes the winner +- shared selector detail shows that stored decision kind in live detail, template history, and recap +- broader analytics decision summaries explicitly mention whether the snapshot guard decided the winner +- legacy `confidence-lane-bias` runs without the new field continue to render without fabricated snapshot-guard claims + +## Validation checklist + +1. full `Development Editor|Win64` build succeeds +2. confirm broader analytics provenance stamps `TemplateSignalSupportDecisionKind = SupportBiasSnapshotGuard` when the guard breaks the tie +3. confirm shared selector detail shows `template-signal decision support-bias-snapshot-guard` +4. confirm broader analytics decision summaries explicitly differentiate snapshot-guard wins from non-snapshot-guard wins