diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 9944f69..b9fe5f0 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -9638,6 +9638,96 @@ UHyperTwistCoachDashboardWidget::GetSelectedTemplateLaunchHistoryInspectSurface( return Surface; } +FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryStatusInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedTemplateLaunchHistoryStatusInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryStatusInspectSurface Surface; + Surface.Headline = TEXT("Template launch history status"); + + const FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface HistorySurface = + GetSelectedTemplateLaunchHistoryInspectSurface(); + const FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface NavigationSurface = + GetDisplayedTemplateLaunchHistoryNavigationInspectSurface(); + + Surface.StatusLine = !DisplayedTemplateLaunchHistoryStatusLine.IsEmpty() + ? DisplayedTemplateLaunchHistoryStatusLine + : HistorySurface.StatusLine; + Surface.SelectedEntryOrdinal = NavigationSurface.SelectedEntryOrdinal.IsEmpty() + ? TEXT("none") + : NavigationSurface.SelectedEntryOrdinal; + Surface.RecordedAtUtc = + HistorySurface.RecordedAtUtc.IsEmpty() ? TEXT("n/a") : HistorySurface.RecordedAtUtc; + Surface.CompletedAtUtc = + HistorySurface.CompletedAtUtc.IsEmpty() ? TEXT("n/a") : HistorySurface.CompletedAtUtc; + Surface.TemplateId = HistorySurface.TemplateId.IsEmpty() ? TEXT("n/a") : HistorySurface.TemplateId; + Surface.SessionStateLabel = HistorySurface.SessionStateLabel.IsEmpty() + ? TEXT("n/a") + : HistorySurface.SessionStateLabel; + Surface.AnalyticsOutcomeLine = HistorySurface.AnalyticsOutcomeLine.IsEmpty() + ? TEXT("n/a") + : HistorySurface.AnalyticsOutcomeLine; + Surface.SelectedEntryIndex = HistorySurface.SelectedEntryIndex; + Surface.HistoryEntryCount = HistorySurface.HistoryEntryCount; + Surface.bHasSelectedEntry = HistorySurface.bHasSelectedEntry; + Surface.bSelectedEntryIsLatest = NavigationSurface.bSelectedEntryIsLatest; + Surface.bHasScopedStats = HistorySurface.bHasScopedStats; + return Surface; +} + +FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryDetailInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedTemplateLaunchHistoryDetailInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryDetailInspectSurface Surface; + Surface.Headline = TEXT("Template launch history detail"); + + const FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface HistorySurface = + GetSelectedTemplateLaunchHistoryInspectSurface(); + + Surface.DetailLine = !DisplayedTemplateLaunchHistoryDetailLine.IsEmpty() + ? DisplayedTemplateLaunchHistoryDetailLine + : HistorySurface.DetailLine; + Surface.SelectionDetailLine = HistorySurface.SelectionDetailLine.IsEmpty() + ? TEXT("") + : HistorySurface.SelectionDetailLine; + Surface.AnalyticsDetailLine = HistorySurface.AnalyticsDetailLine.IsEmpty() + ? TEXT("n/a") + : HistorySurface.AnalyticsDetailLine; + Surface.SourceSessionId = + HistorySurface.SourceSessionId.IsEmpty() ? TEXT("n/a") : HistorySurface.SourceSessionId; + Surface.SourceReviewPlanId = HistorySurface.SourceReviewPlanId.IsEmpty() + ? TEXT("none") + : HistorySurface.SourceReviewPlanId; + Surface.TemplateId = HistorySurface.TemplateId.IsEmpty() ? TEXT("n/a") : HistorySurface.TemplateId; + Surface.TemplateTitle = + HistorySurface.TemplateTitle.IsEmpty() ? TEXT("n/a") : HistorySurface.TemplateTitle; + Surface.UserId = HistorySurface.UserId.IsEmpty() ? TEXT("n/a") : HistorySurface.UserId; + Surface.FocusDeckId = + HistorySurface.FocusDeckId.IsEmpty() ? TEXT("n/a") : HistorySurface.FocusDeckId; + Surface.TemplateKindLabel = HistorySurface.TemplateKindLabel.IsEmpty() + ? TEXT("n/a") + : HistorySurface.TemplateKindLabel; + Surface.SuggestedModeLabel = HistorySurface.SuggestedModeLabel.IsEmpty() + ? TEXT("n/a") + : HistorySurface.SuggestedModeLabel; + Surface.SuggestedSelectionLabel = HistorySurface.SuggestedSelectionLabel.IsEmpty() + ? TEXT("n/a") + : HistorySurface.SuggestedSelectionLabel; + Surface.SelectedEntryIndex = HistorySurface.SelectedEntryIndex; + Surface.HistoryEntryCount = HistorySurface.HistoryEntryCount; + Surface.RecommendedCaseCount = HistorySurface.RecommendedCaseCount; + Surface.FocusCaseCount = HistorySurface.FocusCaseCount; + Surface.CompletedAttemptCount = HistorySurface.CompletedAttemptCount; + Surface.TotalMistakes = HistorySurface.TotalMistakes; + Surface.SuccessRate = HistorySurface.SuccessRate; + Surface.bHasSelectedEntry = HistorySurface.bHasSelectedEntry; + Surface.bHasScopedStats = HistorySurface.bHasScopedStats; + Surface.bHasSelectionProvenance = HistorySurface.bHasSelectionProvenance; + Surface.bPreferReviewRun = HistorySurface.bPreferReviewRun; + Surface.bCoachReview = HistorySurface.bCoachReview; + Surface.bPreferShortReviewSet = HistorySurface.bPreferShortReviewSet; + return Surface; +} + FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface UHyperTwistCoachDashboardWidget::GetDisplayedTemplateLaunchHistoryNavigationInspectSurface() const { @@ -26393,11 +26483,13 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() TemplateLaunchHistoryHeaderTextBlock->SetText(FText::FromString( TEXT("[Template Launch History]"))); } + DisplayedTemplateLaunchHistoryStatusLine = TemplateLaunchHistoryStatusLine; if (TemplateLaunchHistoryStatusTextBlock != nullptr) { TemplateLaunchHistoryStatusTextBlock->SetText(FText::FromString( TemplateLaunchHistoryStatusLine)); } + DisplayedTemplateLaunchHistoryDetailLine = TemplateLaunchHistoryDetailLine; if (TemplateLaunchHistoryDetailTextBlock != nullptr) { TemplateLaunchHistoryDetailTextBlock->SetText(FText::FromString( diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 3f3138b..3c976d4 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -807,6 +807,14 @@ public: FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface GetSelectedTemplateLaunchHistoryInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryStatusInspectSurface + GetDisplayedTemplateLaunchHistoryStatusInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryDetailInspectSurface + GetDisplayedTemplateLaunchHistoryDetailInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface GetDisplayedTemplateLaunchHistoryNavigationInspectSurface() const; @@ -2056,6 +2064,8 @@ private: FString DisplayedActiveRunSummaryLine; FString DisplayedTemplateLaunchStatusLine; FString DisplayedTemplateLaunchDetailLine; + FString DisplayedTemplateLaunchHistoryStatusLine; + FString DisplayedTemplateLaunchHistoryDetailLine; FString DisplayedRunRecapStatusLine; FString DisplayedRunRecapDetailLine; FString DisplayedTemplateLaunchActionLabel; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index df471ba..82e58c6 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -10604,6 +10604,149 @@ struct FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryStatusInspectSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + 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") + 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 bHasScopedStats = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || bHasSelectedEntry + || HistoryEntryCount > 0; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryDetailInspectSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectionDetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString AnalyticsDetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceSessionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceReviewPlanId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateTitle; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UserId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FocusDeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateKindLabel; + + 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") + int32 RecommendedCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FocusCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CompletedAttemptCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TotalMistakes = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float SuccessRate = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelectedEntry = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasScopedStats = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelectionProvenance = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreferReviewRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCoachReview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreferShortReviewSet = false; + + bool IsStructurallyValid() const + { + return !DetailLine.IsEmpty() + || bHasSelectedEntry + || bHasSelectionProvenance; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryNavigationInspectSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 207c624..70a61a8 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -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 adjacent in that same live dashboard consumer lane and adds the dedicated template-launch status/detail inspect pair over the exact displayed template-launch lines already cached in the widget, so gameplay/Blueprint callers can inspect the live template-launch text band directly without routing through the broader preferred-template action-control surface - - the primary template-launch band is now functionally complete enough to stop widening it by drift; the next best clean move is to stay adjacent in the same dashboard neighborhood and expose the displayed template-launch history status/detail pair over the current retained-history text band + - the latest landed bounded `Phase 4` packet stays adjacent in that same live dashboard consumer lane and adds the dedicated template-launch history status/detail inspect pair over the exact displayed retained-history lines already shown by the widget, so gameplay/Blueprint callers can inspect the current retained template-launch history text band directly without scraping text blocks or routing only through the broader selected-history and navigation surfaces + - the retained template-launch history band is now functionally complete enough to stop widening it by drift; the next best clean move is to stay adjacent in the same retained-history dashboard neighborhood and expose the displayed handoff history status/detail pair over the current handoff-history text band - 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 diff --git a/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_TEMPLATE_LAUNCH_HISTORY_STATUS_DETAIL_INSPECT_SURFACE_PACKET_2026-05-10.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_TEMPLATE_LAUNCH_HISTORY_STATUS_DETAIL_INSPECT_SURFACE_PACKET_2026-05-10.md new file mode 100644 index 0000000..85780fd --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_TEMPLATE_LAUNCH_HISTORY_STATUS_DETAIL_INSPECT_SURFACE_PACKET_2026-05-10.md @@ -0,0 +1,53 @@ +# HyperTwist Phase 4 dashboard template-launch history status detail inspect surface packet + +- bounded Phase `4` dashboard-consumer slice +- date: `2026-05-10` + +## Intent + +Stay adjacent in the retained-history dashboard lane and expose the dedicated template-launch history text band directly to gameplay and Blueprint callers. + +## Why this packet exists + +`UHyperTwistCoachDashboardWidget` already displayed: + +- the current `Template launch history` status line +- the current `Template launch history` detail line + +But those two lines only surfaced through `TemplateLaunchHistoryStatusTextBlock` and `TemplateLaunchHistoryDetailTextBlock`. Callers could inspect the selected retained entry and the prev/next navigation posture separately, but there was still no dedicated inspect surface for the exact retained-history text band the dashboard was already presenting. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryStatusInspectSurface` +- added `FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryDetailInspectSurface` +- added `GetDisplayedTemplateLaunchHistoryStatusInspectSurface()` +- added `GetDisplayedTemplateLaunchHistoryDetailInspectSurface()` +- retained the exact displayed template-launch history status/detail lines inside the widget so the new surfaces report the same live strings the dashboard is already presenting +- composed both surfaces over the already-landed selected-history and history-navigation surfaces so the packet stayed bounded and reused the same retained-history derivation + +## 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 exact displayed template-launch history status line +- the exact displayed template-launch history detail line +- selected retained entry ordinal, template id, state, and aggregate outcome context +- selected retained entry title, mode, selection, focus-deck, source-plan, and analytics detail context + +without scraping widget text blocks or stitching together the selected-history and navigation wrappers manually. + +## Validation + +- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln` + - passed with `0 Warning(s), 0 Error(s)` + +## Next clean slice + +Treat the retained template-launch history band as functionally complete enough and stay adjacent in the same retained-history dashboard neighborhood to expose the displayed handoff history status/detail pair over the current handoff-history text band.