Persist template confidence snapshot provenance

This commit is contained in:
axiomlogicnexus 2026-05-07 02:46:20 +02:00
parent a2806d24da
commit 7508e6b7ad
3 changed files with 221 additions and 31 deletions

View file

@ -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())
{

View file

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

View file

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