Expose dashboard template launch history navigation surface

This commit is contained in:
axiomlogicnexus 2026-05-10 03:20:06 +02:00
parent e76ee5814d
commit 807e712b9b
5 changed files with 255 additions and 4 deletions

View file

@ -9247,6 +9247,116 @@ UHyperTwistCoachDashboardWidget::GetSelectedTemplateLaunchHistoryInspectSurface(
return Surface;
}
FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface
UHyperTwistCoachDashboardWidget::GetDisplayedTemplateLaunchHistoryNavigationInspectSurface() const
{
FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface Surface;
Surface.Headline = TEXT("Template launch history navigation");
const FHyperTwistCoachDashboardTemplateLaunchHistoryEntry* SelectedEntry =
SelectedTemplateLaunchHistoryIndex >= 0
&& SelectedTemplateLaunchHistoryIndex < TemplateLaunchHistoryEntries.Num()
? &TemplateLaunchHistoryEntries[SelectedTemplateLaunchHistoryIndex]
: nullptr;
const FHyperTwistTrainingTemplateLaunchScopedStats* SelectedStats =
SelectedEntry != nullptr
? FindCachedTemplateLaunchScopedStats(
SelectedEntry->TemplateId,
SelectedEntry->UserId)
: nullptr;
const bool bNavigationEnabled = TemplateLaunchHistoryEntries.Num() > 1;
Surface.PreviousLabel = !DisplayedPreviousTemplateLaunchNavigationLabel.IsEmpty()
? DisplayedPreviousTemplateLaunchNavigationLabel
: TEXT("Prev Template Launch");
Surface.NextLabel = !DisplayedNextTemplateLaunchNavigationLabel.IsEmpty()
? DisplayedNextTemplateLaunchNavigationLabel
: TEXT("Next Template Launch");
Surface.SelectedEntryIndex = SelectedTemplateLaunchHistoryIndex;
Surface.HistoryEntryCount = TemplateLaunchHistoryEntries.Num();
Surface.bHasSelectedEntry =
SelectedEntry != nullptr && SelectedEntry->IsStructurallyValid();
Surface.bSelectedEntryIsLatest =
Surface.bHasSelectedEntry
&& SelectedTemplateLaunchHistoryIndex == TemplateLaunchHistoryEntries.Num() - 1;
Surface.bPreviousEnabled = bNavigationEnabled;
Surface.bNextEnabled = bNavigationEnabled;
if (!Surface.bHasSelectedEntry)
{
Surface.SelectedEntryOrdinal = TEXT("none");
Surface.RecordedAtUtc = TEXT("n/a");
Surface.CompletedAtUtc = TEXT("n/a");
Surface.TemplateId = TEXT("n/a");
Surface.SessionStateLabel = TEXT("n/a");
Surface.AnalyticsOutcomeLine = TEXT("n/a");
Surface.SelectionDetailLine = TEXT("");
Surface.SuggestedModeLabel = TEXT("n/a");
Surface.SuggestedSelectionLabel = TEXT("n/a");
Surface.StatusLine =
TEXT("Template launch navigation: no completed template-backed runs retained yet.");
Surface.DetailLine = FString::Printf(
TEXT("%s: %s | %s: %s | selected latest: no"),
*Surface.PreviousLabel,
Surface.bPreviousEnabled ? TEXT("enabled") : TEXT("disabled"),
*Surface.NextLabel,
Surface.bNextEnabled ? TEXT("enabled") : TEXT("disabled"));
return Surface;
}
Surface.bHasScopedStats =
SelectedStats != nullptr && SelectedStats->IsStructurallyValid();
Surface.bHasSelectionProvenance =
SelectedEntry->SelectionProvenance.IsStructurallyValid();
Surface.SelectedEntryOrdinal = FString::Printf(
TEXT("%d/%d"),
SelectedTemplateLaunchHistoryIndex + 1,
TemplateLaunchHistoryEntries.Num());
Surface.RecordedAtUtc = SelectedEntry->RecordedAtUtc.IsEmpty()
? TEXT("n/a")
: SelectedEntry->RecordedAtUtc;
Surface.CompletedAtUtc =
SelectedEntry->CompletedAtUtc.IsEmpty() ? TEXT("n/a") : SelectedEntry->CompletedAtUtc;
Surface.TemplateId =
SelectedEntry->TemplateId.IsEmpty() ? TEXT("n/a") : SelectedEntry->TemplateId;
Surface.SessionStateLabel =
HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
SelectedEntry->SessionState);
Surface.AnalyticsOutcomeLine =
HyperTwistCoachDashboardWidgetInternal::BuildTemplateLaunchAnalyticsOutcomeFragment(
SelectedStats);
Surface.SelectionDetailLine =
HyperTwistCoachDashboardWidgetInternal::BuildTemplateLaunchSelectionDetailFragment(
Surface.bHasSelectionProvenance ? &SelectedEntry->SelectionProvenance : nullptr);
Surface.SuggestedModeLabel =
HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
SelectedEntry->SuggestedMode);
Surface.SuggestedSelectionLabel =
HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(
SelectedEntry->SuggestedSelectionPolicy);
Surface.StatusLine = FString::Printf(
TEXT("Template launch navigation: selected %s | Template: %s | State: %s | Completed: %s"),
*Surface.SelectedEntryOrdinal,
*Surface.TemplateId,
*Surface.SessionStateLabel,
*Surface.CompletedAtUtc);
Surface.DetailLine = FString::Printf(
TEXT("%s: %s | %s: %s | aggregate: %s | mode: %s | selection: %s | selected latest: %s"),
*Surface.PreviousLabel,
Surface.bPreviousEnabled ? TEXT("enabled") : TEXT("disabled"),
*Surface.NextLabel,
Surface.bNextEnabled ? TEXT("enabled") : TEXT("disabled"),
*Surface.AnalyticsOutcomeLine,
*Surface.SuggestedModeLabel,
*Surface.SuggestedSelectionLabel,
Surface.bSelectedEntryIsLatest ? TEXT("yes") : TEXT("no"));
if (!Surface.SelectionDetailLine.IsEmpty())
{
Surface.DetailLine += TEXT(" | ");
Surface.DetailLine += Surface.SelectionDetailLine;
}
return Surface;
}
FHyperTwistTrainingCoachDashboardGuidanceComparisonInspectSurface
UHyperTwistCoachDashboardWidget::GetDisplayedGuidanceComparisonInspectSurface() const
{
@ -24623,8 +24733,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
}
if (PreviousTemplateLaunchHistoryButtonLabel != nullptr)
{
DisplayedPreviousTemplateLaunchNavigationLabel = TEXT("Prev Template Launch");
PreviousTemplateLaunchHistoryButtonLabel->SetText(FText::FromString(
TEXT("Prev Template Launch")));
DisplayedPreviousTemplateLaunchNavigationLabel));
}
if (PreviousTemplateLaunchHistoryButton != nullptr)
{
@ -24632,8 +24743,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
}
if (NextTemplateLaunchHistoryButtonLabel != nullptr)
{
DisplayedNextTemplateLaunchNavigationLabel = TEXT("Next Template Launch");
NextTemplateLaunchHistoryButtonLabel->SetText(FText::FromString(
TEXT("Next Template Launch")));
DisplayedNextTemplateLaunchNavigationLabel));
}
if (NextTemplateLaunchHistoryButton != nullptr)
{

View file

@ -791,6 +791,10 @@ public:
FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface
GetSelectedTemplateLaunchHistoryInspectSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface
GetDisplayedTemplateLaunchHistoryNavigationInspectSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard")
FHyperTwistTrainingCoachDashboardGuidanceComparisonInspectSurface
GetDisplayedGuidanceComparisonInspectSurface() const;
@ -2032,6 +2036,8 @@ private:
FString DisplayedNextHandoffHistoryNavigationLabel;
FString DisplayedPreviousGuidanceDecisionNavigationLabel;
FString DisplayedNextGuidanceDecisionNavigationLabel;
FString DisplayedPreviousTemplateLaunchNavigationLabel;
FString DisplayedNextTemplateLaunchNavigationLabel;
FString DisplayedPreviousAttemptNavigationLabel;
FString DisplayedNextAttemptNavigationLabel;
EHyperTwistCoachDashboardGuidancePreviewLane ActiveGuidancePreviewLane =

View file

@ -10113,6 +10113,87 @@ struct FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Headline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PreviousLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NextLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectedEntryOrdinal;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecordedAtUtc;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CompletedAtUtc;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString TemplateId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SessionStateLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString AnalyticsOutcomeLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectionDetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SuggestedModeLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SuggestedSelectionLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 SelectedEntryIndex = INDEX_NONE;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 HistoryEntryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasSelectedEntry = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSelectedEntryIsLatest = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bPreviousEnabled = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bNextEnabled = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasScopedStats = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasSelectionProvenance = false;
bool IsStructurallyValid() const
{
return !StatusLine.IsEmpty()
|| !DetailLine.IsEmpty()
|| bHasSelectedEntry
|| bPreviousEnabled
|| bNextEnabled;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface
{

View file

@ -69,8 +69,8 @@ Current correction call from the source-exposed audit and current execution refr
- the latest landed bounded `Phase 4` packet closes the retained-idle comparison/outcome actionability gap at the end of the recognition-assisted coach-to-review continuity lane, and the final retained-surface confirmation pass now leaves that closure lane functionally complete
- the retained idle widget surface now keeps retained comparison and decision history inspectable without continuing to advertise live launch or guidance-outcome actions once the coach-to-review loop has already collapsed to truthful closed-loop idle
- the now-complete deliberate `UHyperTwistTrainingRuntimeLibrary` world-context consumer lane reaches through active generated-mode selector option owned-execution split-phase current-selection merged ordered roster phase-id lookup, normalized-order lookup, and evaluation-order lookup inspect surfaces, so gameplay/Blueprint callers can inspect one retained merged-roster row directly by phase id, normalized order, or evaluation order without scanning the merged roster array or re-running inspect derivation details directly
- the latest landed bounded `Phase 4` packet stays on that same retained-history navigation/control lane and adds the guidance decision history navigation control inspect surface over already-retained authored decision outcomes, so gameplay/Blueprint callers can inspect the live prev/next decision labels, enablement posture, selected retained decision ordinal, and lane/scope/action/latest-selection context without scraping button text or reopening retained-history derivation
- the next clean move is to continue that same retained-history navigation/control lane and expose the template launch history navigation control surface rather than drifting back into backend continuity or reopening the already-inspectable history detail seams
- the latest landed bounded `Phase 4` packet stays on that same retained-history navigation/control lane and adds the template launch history navigation control inspect surface over already-retained template-backed runs, so gameplay/Blueprint callers can inspect the live prev/next template-launch labels, enablement posture, selected retained launch ordinal, and template/state/analytics/latest-selection context without scraping button text or reopening retained-history derivation
- the retained-history navigation/control lane now appears complete enough to reassess whether to pivot deliberately into the next broader `Phase 4` roadmap lane rather than widening this seam by drift
- the current execution-discipline rule that broad refactor / monolith-splitting work should not interrupt the active bounded roadmap packet unless structure is actually blocking it
## Donor portfolio phase taxonomy

View file

@ -0,0 +1,52 @@
# HyperTwist Phase 4 dashboard template launch history navigation control inspect surface packet
- bounded Phase `4` dashboard-consumer slice
- date: `2026-05-10`
## Intent
Stay on the retained-history navigation/control lane inside `UHyperTwistCoachDashboardWidget` and expose the template launch history navigation controls directly to gameplay and Blueprint callers.
## Why this packet exists
The dashboard already derived and displayed:
- the previous template-launch-history button label
- the next template-launch-history button label
- the enablement posture for both controls
But that control state still only surfaced through button text and button enabled state. Callers still had to inspect widget controls directly to understand whether retained template-launch navigation is available and which retained template-backed run is currently selected.
## What changed
- added `FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface`
- added `GetDisplayedTemplateLaunchHistoryNavigationInspectSurface()`
- retained the exact displayed previous/next template-launch-history labels inside the widget so the new inspect surface reports the same live strings the dashboard is already presenting
- kept the packet bounded to `UHyperTwistCoachDashboardWidget` plus shared surface types, with no dashboard redesign and no backend continuity changes
## Files
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistTrainingTypes.h`
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistCoachDashboardWidget.h`
- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Private\HyperTwistTraining\HyperTwistCoachDashboardWidget.cpp`
- `C:\HyperTwist\docs\HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md`
## Outcome
Gameplay and Blueprint callers can now inspect:
- the live previous/next template-launch-history labels
- whether template-launch-history navigation is currently enabled
- the selected retained launch ordinal, recorded/completed time, template id, and session state
- the retained analytics outcome line, selection-detail posture, suggested mode/selection posture, and latest-selection posture
without scraping button text or reopening retained-history derivation.
## Validation
- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln`
- passed with `0 Warning(s), 0 Error(s)`
## Next clean slice
Reassess whether the retained-history navigation/control lane is now complete enough to pivot deliberately into the next broader `Phase 4` roadmap lane.