Persist template decision-bias snapshot guard provenance

This commit is contained in:
axiomlogicnexus 2026-05-07 16:01:25 +02:00
parent 8bf4e0975c
commit 167b551a69
3 changed files with 134 additions and 3 deletions

View file

@ -182,6 +182,10 @@ namespace HyperTwistCoachDashboardWidgetInternal
const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance
);
FString BuildTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionDetailFragment(
const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance
);
FString BuildTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDetailFragment(
const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance
);
@ -392,6 +396,23 @@ namespace HyperTwistCoachDashboardWidgetInternal
}
}
FString ResolveTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKindLabel(
const EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind
TemplateSignalSupportDecisionBiasSnapshotDecisionKind
)
{
switch (TemplateSignalSupportDecisionBiasSnapshotDecisionKind)
{
case EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind::
DecisionBiasSnapshotGuard:
return TEXT("decision-bias-snapshot-guard");
case EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind::
None:
default:
return TEXT("none");
}
}
FString ResolveTemplateLaunchSelectionPeerSupportKindDispositionLabel(
const EHyperTwistTrainingTemplateLaunchSelectionPeerSupportKindDisposition
PeerSupportKindDisposition
@ -456,7 +477,8 @@ namespace HyperTwistCoachDashboardWidgetInternal
const FHyperTwistTrainingTemplateLaunchScopedStats* PeerStats,
const EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind AnalyticsSupportKind,
const bool bUsedTemplateSignalSupportBiasSnapshotGuard = false,
const bool bUsedTemplateSignalSupportDecisionBiasGuard = false
const bool bUsedTemplateSignalSupportDecisionBiasGuard = false,
const bool bUsedTemplateSignalSupportDecisionBiasSnapshotGuard = false
)
{
FHyperTwistTrainingTemplateLaunchSelectionProvenance Provenance;
@ -521,6 +543,12 @@ namespace HyperTwistCoachDashboardWidgetInternal
DecisionSplitGuard
: EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasKind::
None;
Provenance.TemplateSignalSupportDecisionBiasSnapshotDecisionKind =
bUsedTemplateSignalSupportDecisionBiasSnapshotGuard
? EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind::
DecisionBiasSnapshotGuard
: EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind::
None;
Provenance.TemplateSignalSupportBiasSnapshot =
BuildTemplateLaunchTemplateSignalSupportBiasSnapshot(SelectedStats);
Provenance.TemplateSignalSupportDecisionBiasSnapshot =
@ -531,7 +559,18 @@ namespace HyperTwistCoachDashboardWidgetInternal
BuildTemplateLaunchComparablePeerConfidenceAuditClause(
SelectedStats,
TEXT("chosen template"));
if (bUsedTemplateSignalSupportDecisionBiasGuard)
if (bUsedTemplateSignalSupportDecisionBiasSnapshotGuard)
{
Provenance.DecisionSummaryLine = FString::Printf(
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, then the decision-split guard tied again, and earned-vs-non-earned decision-bias snapshot history specifically broke the remaining tie: %s | chosen %s | peer %s."),
*ResolveDashboardTemplateLaunchDisplayTitle(PeerTemplate),
*ResolveDashboardTemplateLaunchDisplayTitle(SelectedTemplate),
*SelectedComparablePeerConfidenceAuditClause,
*BuildTemplateLaunchAnalyticsTieBreakSignalFragment(SelectedStats),
*BuildTemplateLaunchAnalyticsTieBreakSignalFragment(PeerStats)
);
}
else if (bUsedTemplateSignalSupportDecisionBiasGuard)
{
Provenance.DecisionSummaryLine = FString::Printf(
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, with the decision-split guard specifically breaking the remaining tie: %s | chosen %s | peer %s."),
@ -1492,6 +1531,30 @@ namespace HyperTwistCoachDashboardWidgetInternal
);
}
FString BuildTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionDetailFragment(
const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance
)
{
if (SelectionProvenance == nullptr
|| SelectionProvenance->ReasonClass
!= EHyperTwistTrainingTemplateLaunchSelectionReasonClass::AnalyticsTieBreak
|| SelectionProvenance->AnalyticsSupportKind
!= EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind::
TemplateScopedOutcomeSignal
|| SelectionProvenance->TemplateSignalSupportDecisionBiasSnapshotDecisionKind
== EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind::
None)
{
return FString();
}
return FString::Printf(
TEXT(" | template-signal decision-bias-snapshot %s"),
*ResolveTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKindLabel(
SelectionProvenance->TemplateSignalSupportDecisionBiasSnapshotDecisionKind)
);
}
FString BuildTemplateLaunchSelectionTemplateSignalSupportBiasDetailFragment(
const FHyperTwistTrainingTemplateLaunchSelectionProvenance* SelectionProvenance
)
@ -1647,6 +1710,9 @@ namespace HyperTwistCoachDashboardWidgetInternal
const FString TemplateSignalSupportDecisionBiasDetailFragment =
BuildTemplateLaunchSelectionTemplateSignalSupportDecisionBiasDetailFragment(
SelectionProvenance);
const FString TemplateSignalSupportDecisionBiasSnapshotDecisionDetailFragment =
BuildTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionDetailFragment(
SelectionProvenance);
const FString TemplateSignalSupportDecisionBiasSnapshotDetailFragment =
BuildTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDetailFragment(
SelectionProvenance);
@ -1662,6 +1728,7 @@ namespace HyperTwistCoachDashboardWidgetInternal
AnalyticsSupportDetailFragment + TemplateSignalSupportDetailFragment
+ TemplateSignalSupportDecisionDetailFragment
+ TemplateSignalSupportDecisionBiasDetailFragment
+ TemplateSignalSupportDecisionBiasSnapshotDecisionDetailFragment
+ TemplateSignalSupportDecisionBiasSnapshotDetailFragment
+ TemplateSignalSupportBiasDetailFragment
+ PeerSupportDispositionDetailFragment
@ -2850,6 +2917,7 @@ namespace HyperTwistCoachDashboardWidgetInternal
EHyperTwistTrainingTemplateLaunchSelectionAnalyticsSupportKind::None;
bool bCandidateUsedTemplateSignalSupportBiasSnapshotGuard = false;
bool bCandidateUsedTemplateSignalSupportDecisionBiasGuard = false;
bool bCandidateUsedTemplateSignalSupportDecisionBiasSnapshotGuard = false;
};
FTemplateLaunchAnalyticsPreferenceDecision ResolveTemplateAnalyticsPreferenceDecision(
@ -2963,6 +3031,7 @@ namespace HyperTwistCoachDashboardWidgetInternal
TemplateScopedOutcomeSignal;
Decision.bCandidateUsedTemplateSignalSupportBiasSnapshotGuard = true;
Decision.bCandidateUsedTemplateSignalSupportDecisionBiasGuard = true;
Decision.bCandidateUsedTemplateSignalSupportDecisionBiasSnapshotGuard = true;
}
return Decision;
}
@ -9718,7 +9787,9 @@ const FHyperTwistTrainingSessionTemplate* UHyperTwistCoachDashboardWidget::FindP
AnalyticsDecision
.bCandidateUsedTemplateSignalSupportBiasSnapshotGuard,
AnalyticsDecision
.bCandidateUsedTemplateSignalSupportDecisionBiasGuard
.bCandidateUsedTemplateSignalSupportDecisionBiasGuard,
AnalyticsDecision
.bCandidateUsedTemplateSignalSupportDecisionBiasSnapshotGuard
);
}
}

View file

@ -1105,6 +1105,15 @@ enum class EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisi
DecisionSplitGuard UMETA(DisplayName = "Decision Split Guard")
};
UENUM(BlueprintType)
enum class
EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind :
uint8
{
None UMETA(DisplayName = "None"),
DecisionBiasSnapshotGuard UMETA(DisplayName = "Decision Bias Snapshot Guard")
};
UENUM(BlueprintType)
enum class EHyperTwistTrainingTemplateLaunchTemplateSignalSupportDecisionBiasSnapshotState :
uint8
@ -1231,6 +1240,12 @@ struct FHyperTwistTrainingTemplateLaunchSelectionProvenance
TemplateSignalSupportDecisionBiasKind =
EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasKind::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind
TemplateSignalSupportDecisionBiasSnapshotDecisionKind =
EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind::
None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistTrainingTemplateLaunchTemplateSignalSupportBiasSnapshot
TemplateSignalSupportBiasSnapshot;

View file

@ -0,0 +1,45 @@
# HyperTwist Phase 4 template-signal decision-bias snapshot guard provenance packet
## Purpose
This packet persists when the new historical decision-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 `DecisionSplitGuard` wins from wins that were specifically reinforced by earned-vs-non-earned decision-bias snapshot history.
## Scope
- add a distinct structured provenance marker for the historical decision-bias snapshot guard
- stamp that marker only when the earned-vs-non-earned decision-bias snapshot guard decides a broader template-signal tie
- surface that marker through the shared selector-detail path already used by live template launch detail, template history, and template-backed recap
## What landed
- `HyperTwistTrainingTypes.h`
- added `EHyperTwistTrainingTemplateLaunchSelectionTemplateSignalSupportDecisionBiasSnapshotDecisionKind`
- added `TemplateSignalSupportDecisionBiasSnapshotDecisionKind` to `FHyperTwistTrainingTemplateLaunchSelectionProvenance`
- `HyperTwistCoachDashboardWidget.cpp`
- broader analytics provenance now stamps:
- `TemplateSignalSupportDecisionBiasKind = DecisionSplitGuard` when the current reinforced-vs-plain snapshot-guard lane breaks the tie
- `TemplateSignalSupportDecisionBiasSnapshotDecisionKind = DecisionBiasSnapshotGuard` only when the new historical earned-vs-non-earned snapshot history then breaks the remaining tie
- shared selector detail now appends:
- `template-signal decision-bias decision-split-guard`
- and, when applicable, `template-signal decision-bias-snapshot decision-bias-snapshot-guard`
- decision summary wording now distinguishes:
- plain decision-split wins
- from wins where the decision-split lane tied again and historical decision-bias snapshot history became the final decider
## Product effect
- live `[Template Launch]` detail now explicitly shows when a broader template-signal winner came from the historical decision-bias snapshot guard
- template launch history and recap preserve that distinction instead of collapsing all reinforced broader wins into the same `DecisionSplitGuard` reason
## Acceptance criteria
- broader template-signal selection provenance persists a distinct decision kind when the decision-bias snapshot guard changes the winner
- live detail, template history, and recap all show that retained decision kind through the shared selector-detail path
- plain `DecisionSplitGuard` wins remain distinguishable from history-reinforced wins
## Validation checklist
1. full `Development Editor|Win64` build succeeds
2. confirm `FHyperTwistTrainingTemplateLaunchSelectionProvenance` persists `TemplateSignalSupportDecisionBiasSnapshotDecisionKind`
3. confirm broader analytics provenance stamps that field only when the historical decision-bias snapshot guard decides the tie
4. confirm shared selector detail shows `template-signal decision-bias-snapshot decision-bias-snapshot-guard` only for those history-reinforced wins