From 7508e6b7ad2f261646f05f2c101d7b80da549393 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 02:46:20 +0200 Subject: [PATCH] Persist template confidence snapshot provenance --- .../HyperTwistCoachDashboardWidget.cpp | 167 ++++++++++++++---- .../HyperTwistTrainingTypes.h | 41 +++++ ...E_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md | 44 +++++ 3 files changed, 221 insertions(+), 31 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_CONFIDENCE_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 9a68b28..d4cb6d7 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -125,16 +125,29 @@ namespace HyperTwistCoachDashboardWidgetInternal const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); + FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot + BuildTemplateLaunchComparablePeerConfidenceSnapshot( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats + ); + FString BuildTemplateLaunchComparablePeerConfidenceAuditClause( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, const TCHAR* SubjectLabel ); + FString BuildTemplateLaunchComparablePeerConfidenceStatusClauseFromSnapshot( + const FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot& Snapshot + ); + FString BuildTemplateLaunchComparablePeerConfidenceStatusClause( const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance, const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats ); + FString BuildTemplateLaunchSelectionComparablePeerConfidenceDetailFragment( + const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance + ); + bool HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus( const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats, int32* OutComparablePeerOutcomeRuns = nullptr, @@ -359,6 +372,8 @@ namespace HyperTwistCoachDashboardWidgetInternal } else { + Provenance.ComparablePeerConfidenceSnapshot = + BuildTemplateLaunchComparablePeerConfidenceSnapshot(SelectedStats); const FString SelectedComparablePeerConfidenceAuditClause = BuildTemplateLaunchComparablePeerConfidenceAuditClause( SelectedStats, @@ -715,6 +730,70 @@ namespace HyperTwistCoachDashboardWidgetInternal ); } + FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot + BuildTemplateLaunchComparablePeerConfidenceSnapshot( + const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats + ) + { + FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot Snapshot; + if (TemplateStats == nullptr || !TemplateStats->IsStructurallyValid()) + { + Snapshot.State = + EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + NoRetainedComparablePeerConfidenceLane; + return Snapshot; + } + + const bool bGuardEarned = HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus( + TemplateStats, + &Snapshot.ComparablePeerOutcomeRuns, + &Snapshot.SplitConfidenceOutcomeRuns, + &Snapshot.ComparablePeerOutcomeShare, + &Snapshot.PeerNoEvidenceOutcomeRuns + ); + Snapshot.State = bGuardEarned + ? EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + ConfidenceEligible + : EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + SuppressedPeerNoEvidenceHeavy; + return Snapshot; + } + + FString BuildTemplateLaunchComparablePeerConfidenceStatusClauseFromSnapshot( + const FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot& Snapshot + ) + { + switch (Snapshot.State) + { + case EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + NoRetainedComparablePeerConfidenceLane: + return TEXT( + "Broader analytics pick; split-confidence lane has no retained comparable-peer confidence lane yet."); + case EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + ConfidenceEligible: + return FString::Printf( + TEXT( + "Broader analytics pick; split-confidence lane is already confidence-eligible for future reinforcement on comparable-peer-backed history (%d/%d outcomes, share %.2f)."), + Snapshot.ComparablePeerOutcomeRuns, + Snapshot.SplitConfidenceOutcomeRuns, + Snapshot.ComparablePeerOutcomeShare + ); + case EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + SuppressedPeerNoEvidenceHeavy: + return FString::Printf( + TEXT( + "Broader analytics pick; split-confidence lane is still suppressed by peer-no-evidence-heavy history (%d/%d comparable-peer outcomes, share %.2f; peer-no-evidence %d)."), + Snapshot.ComparablePeerOutcomeRuns, + Snapshot.SplitConfidenceOutcomeRuns, + Snapshot.ComparablePeerOutcomeShare, + Snapshot.PeerNoEvidenceOutcomeRuns + ); + case EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState::None: + default: + return FString(); + } + } + FString BuildTemplateLaunchComparablePeerConfidenceStatusClause( const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance, const FHyperTwistTrainingTemplateLaunchScopedStats* TemplateStats @@ -730,39 +809,14 @@ namespace HyperTwistCoachDashboardWidgetInternal return FString(); } - if (TemplateStats == nullptr || !TemplateStats->IsStructurallyValid()) + if (SelectionProvenance->ComparablePeerConfidenceSnapshot.IsStructurallyValid()) { - return TEXT("Broader analytics pick; split-confidence lane has no retained comparable-peer confidence lane yet."); + return BuildTemplateLaunchComparablePeerConfidenceStatusClauseFromSnapshot( + SelectionProvenance->ComparablePeerConfidenceSnapshot); } - int32 ComparablePeerOutcomeRuns = 0; - int32 SplitConfidenceOutcomeRuns = 0; - int32 PeerNoEvidenceOutcomeRuns = 0; - float ComparablePeerOutcomeShare = 0.0f; - const bool bGuardEarned = HasTemplateLaunchComparablePeerEvidenceForConfidenceBonus( - TemplateStats, - &ComparablePeerOutcomeRuns, - &SplitConfidenceOutcomeRuns, - &ComparablePeerOutcomeShare, - &PeerNoEvidenceOutcomeRuns - ); - if (bGuardEarned) - { - return FString::Printf( - TEXT("Broader analytics pick; split-confidence lane is already confidence-eligible for future reinforcement on comparable-peer-backed history (%d/%d outcomes, share %.2f)."), - ComparablePeerOutcomeRuns, - SplitConfidenceOutcomeRuns, - ComparablePeerOutcomeShare - ); - } - - return FString::Printf( - TEXT("Broader analytics pick; split-confidence lane is still suppressed by peer-no-evidence-heavy history (%d/%d comparable-peer outcomes, share %.2f; peer-no-evidence %d)."), - ComparablePeerOutcomeRuns, - SplitConfidenceOutcomeRuns, - ComparablePeerOutcomeShare, - PeerNoEvidenceOutcomeRuns - ); + return BuildTemplateLaunchComparablePeerConfidenceStatusClauseFromSnapshot( + BuildTemplateLaunchComparablePeerConfidenceSnapshot(TemplateStats)); } FString BuildTemplateLaunchSelectionAnalyticsSupportDetailFragment( @@ -803,6 +857,53 @@ namespace HyperTwistCoachDashboardWidgetInternal ); } + FString BuildTemplateLaunchSelectionComparablePeerConfidenceDetailFragment( + const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance + ) + { + if (SelectionProvenance == nullptr + || SelectionProvenance->ReasonClass + != EHyperTwistTrainingTemplateLaunchSelectionReasonClass::AnalyticsTieBreak + || SelectionProvenance->AnalyticsSupportKind + != EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind:: + TemplateScopedOutcomeSignal + || !SelectionProvenance->ComparablePeerConfidenceSnapshot.IsStructurallyValid()) + { + return FString(); + } + + const FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot& Snapshot = + SelectionProvenance->ComparablePeerConfidenceSnapshot; + switch (Snapshot.State) + { + case EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + NoRetainedComparablePeerConfidenceLane: + return TEXT(" | at-launch confidence lane none yet"); + case EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + ConfidenceEligible: + return FString::Printf( + TEXT( + " | at-launch confidence lane confidence-eligible on comparable-peer-backed history (%d/%d outcomes, share %.2f)"), + Snapshot.ComparablePeerOutcomeRuns, + Snapshot.SplitConfidenceOutcomeRuns, + Snapshot.ComparablePeerOutcomeShare + ); + case EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState:: + SuppressedPeerNoEvidenceHeavy: + return FString::Printf( + TEXT( + " | at-launch confidence lane suppressed by peer-no-evidence-heavy history (%d/%d comparable-peer outcomes, share %.2f; peer-no-evidence %d)"), + Snapshot.ComparablePeerOutcomeRuns, + Snapshot.SplitConfidenceOutcomeRuns, + Snapshot.ComparablePeerOutcomeShare, + Snapshot.PeerNoEvidenceOutcomeRuns + ); + case EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState::None: + default: + return FString(); + } + } + FString BuildTemplateLaunchSelectionDetailFragment( const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance ) @@ -821,8 +922,12 @@ namespace HyperTwistCoachDashboardWidgetInternal BuildTemplateLaunchSelectionAnalyticsSupportDetailFragment(SelectionProvenance); const FString PeerSupportDispositionDetailFragment = BuildTemplateLaunchSelectionPeerSupportDispositionDetailFragment(SelectionProvenance); + const FString ComparablePeerConfidenceDetailFragment = + BuildTemplateLaunchSelectionComparablePeerConfidenceDetailFragment( + SelectionProvenance); const FString SelectionReasonDetailFragment = - AnalyticsSupportDetailFragment + PeerSupportDispositionDetailFragment; + AnalyticsSupportDetailFragment + PeerSupportDispositionDetailFragment + + ComparablePeerConfidenceDetailFragment; if (!SelectionProvenance->PeerTemplateTitle.IsEmpty() || !SelectionProvenance->PeerTemplateId.IsEmpty()) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 6abb6f6..e9fb2ba 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -1074,6 +1074,43 @@ enum class EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition UMETA(DisplayName = "Peer Cleared Comparable Support Kind Guard") }; +UENUM(BlueprintType) +enum class EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState : uint8 +{ + None UMETA(DisplayName = "None"), + NoRetainedComparablePeerConfidenceLane + UMETA(DisplayName = "No Retained Comparable Peer Confidence Lane"), + ConfidenceEligible UMETA(DisplayName = "Confidence Eligible"), + SuppressedPeerNoEvidenceHeavy UMETA(DisplayName = "Suppressed Peer No Evidence Heavy") +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState State = + EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ComparablePeerOutcomeRuns = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 SplitConfidenceOutcomeRuns = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 PeerNoEvidenceOutcomeRuns = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float ComparablePeerOutcomeShare = 0.0f; + + bool IsStructurallyValid() const + { + return State != EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState::None; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingTemplateLaunchSelectionProvenance { @@ -1095,6 +1132,10 @@ struct FHyperTwistTrainingTemplateLaunchSelectionProvenance PeerSupportKindDisposition = EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition::None; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot + ComparablePeerConfidenceSnapshot; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString PeerTemplateId; diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_CONFIDENCE_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_CONFIDENCE_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md new file mode 100644 index 0000000..614540f --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_LAUNCH_CONFIDENCE_SNAPSHOT_PROVENANCE_PACKET_2026-05-07.md @@ -0,0 +1,44 @@ +# HyperTwist Phase 4 template launch confidence snapshot provenance packet + +## Purpose + +This packet persists the comparable-peer confidence-lane state that the dashboard already shows before launch for broader `TemplateScopedOutcomeSignal` analytics winners. That state is now stamped into template selection provenance at selection time and read back through template history and run recap, so later audits preserve the dashboard's at-launch view instead of recomputing it from drifted current template stats. + +## Scope + +- add a compact comparable-peer confidence snapshot payload under template launch selection provenance +- stamp that snapshot when the dashboard builds a broader analytics tie-break selection provenance +- make the live template-launch status clause prefer the stored snapshot and only fall back to current stats for compatibility +- append stored at-launch confidence snapshot detail to the shared template selection detail path used by live detail, template launch history, and run recap + +## What landed + +- `HyperTwistTrainingTypes.h` + - added `EHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshotState` + - added `FHyperTwistTrainingTemplateLaunchComparablePeerConfidenceSnapshot` + - nested the new snapshot under `FHyperTwistTrainingTemplateLaunchSelectionProvenance` +- `HyperTwistCoachDashboardWidget.cpp` + - added a helper that captures comparable-peer outcome count/share and peer-no-evidence count into a selection snapshot + - broader `TemplateScopedOutcomeSignal` tie-break provenance now stores that snapshot at selection time + - the live `[Template Launch]` status clause now prefers the stored snapshot from selection provenance + - shared template selection detail now appends a concise `at-launch confidence lane` fragment sourced from persisted selection provenance + +## Product effect + +- the live dashboard still shows the same confidence-lane posture for broader analytics winners +- template launch history now reflects the at-launch confidence-lane snapshot even if the template's current scoped analytics later drift +- run recap inherits the same persisted at-launch confidence fragment through the existing template selection detail path + +## Acceptance criteria + +- broader analytics tie-break selection provenance persists comparable-peer confidence snapshot state +- live status clause uses stored snapshot when it exists +- template launch history selection detail shows stored at-launch confidence snapshot without recomputing from current stats +- run recap selection detail shows the same stored at-launch confidence snapshot + +## Validation checklist + +1. full `Development Editor|Win64` build succeeds +2. confirm broader analytics tie-break provenance stamps the new snapshot +3. confirm live status clause still distinguishes `confidence-eligible`, `suppressed`, and `no retained lane yet` +4. confirm template history and run recap selection detail append the stored `at-launch confidence lane` fragment