Persist template support snapshot-guard provenance

This commit is contained in:
axiomlogicnexus 2026-05-07 04:01:08 +02:00
parent 306a9f6e82
commit 2f4b0afcc4
3 changed files with 118 additions and 8 deletions

View file

@ -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
);
}
}

View file

@ -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;

View file

@ -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