diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 94c6607..a0bb772 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -12619,6 +12619,70 @@ UHyperTwistCoachDashboardWidget::GetDisplayedRunRecapDetailInspectSurface() cons return Surface; } +FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedRunRecapActionControlInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface Surface; + Surface.Headline = TEXT("Run recap action control"); + + const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid() + && CachedRunState.ActiveDeck.IsStructurallyValid(); + const bool bHasCurrentRunRecap = bHasActiveRun + && (CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed + || CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted); + const bool bHasDisplayedRunRecap = bHasCurrentRunRecap || bHasRetainedRunRecap; + const bool bCanRepeatDisplayedRun = !bHasLiveAttemptTimer + && RetainedRunRecapDeck.IsStructurallyValid() + && (!bHasActiveRun + || CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed + || CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted); + const bool bCanDismissStoredRecap = !bHasCurrentRunRecap && bHasRetainedRunRecap; + const FString DisplayedRecapSourceLabel = bHasDisplayedRunRecap + ? (bHasCurrentRunRecap ? TEXT("current run") : TEXT("last completed run")) + : (bHasActiveRun ? TEXT("active run") : TEXT("none yet")); + const FString RetainedDeckLabel = RetainedRunRecapDeck.IsStructurallyValid() + ? (RetainedRunRecapDeck.Title.IsEmpty() + ? (RetainedRunRecapDeck.DeckId.IsEmpty() ? TEXT("n/a") : RetainedRunRecapDeck.DeckId) + : RetainedRunRecapDeck.Title) + : TEXT("n/a"); + + Surface.RepeatLabel = !DisplayedRepeatRunActionLabel.IsEmpty() + ? DisplayedRepeatRunActionLabel + : TEXT("Repeat Run"); + Surface.DismissLabel = !DisplayedDismissRecapActionLabel.IsEmpty() + ? DisplayedDismissRecapActionLabel + : TEXT("Dismiss Recap"); + Surface.DisplayedRecapSourceLabel = DisplayedRecapSourceLabel; + Surface.RetainedDeckLabel = RetainedDeckLabel; + Surface.RetainedSessionId = RetainedRunRecapSessionId.IsEmpty() + ? TEXT("none") + : RetainedRunRecapSessionId; + Surface.bHasDisplayedRunRecap = bHasDisplayedRunRecap; + Surface.bHasCurrentRunRecap = bHasCurrentRunRecap; + Surface.bHasRetainedRunRecap = bHasRetainedRunRecap; + Surface.bHasRetainedDeck = RetainedRunRecapDeck.IsStructurallyValid(); + Surface.bHasActiveRun = bHasActiveRun; + Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer; + Surface.bRepeatEnabled = bCanRepeatDisplayedRun; + Surface.bDismissEnabled = bCanDismissStoredRecap; + Surface.StatusLine = FString::Printf( + TEXT("%s: %s | %s: %s | displayed recap: %s"), + *Surface.RepeatLabel, + Surface.bRepeatEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.DismissLabel, + Surface.bDismissEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.DisplayedRecapSourceLabel); + Surface.DetailLine = FString::Printf( + TEXT("Run-recap control detail: retained deck %s | retained session %s | active run %s | live timer %s | current recap %s | retained recap %s"), + *Surface.RetainedDeckLabel, + *Surface.RetainedSessionId, + Surface.bHasActiveRun ? TEXT("yes") : TEXT("no"), + Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"), + Surface.bHasCurrentRunRecap ? TEXT("yes") : TEXT("no"), + Surface.bHasRetainedRunRecap ? TEXT("yes") : TEXT("no")); + return Surface; +} + FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface UHyperTwistCoachDashboardWidget::GetDisplayedActiveAttemptTimerStatusInspectSurface() const { @@ -25743,7 +25807,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (RepeatRunButtonLabel != nullptr) { - RepeatRunButtonLabel->SetText(FText::FromString(TEXT("Repeat Run"))); + DisplayedRepeatRunActionLabel = TEXT("Repeat Run"); + RepeatRunButtonLabel->SetText(FText::FromString(DisplayedRepeatRunActionLabel)); + } + else + { + DisplayedRepeatRunActionLabel = TEXT("Repeat Run"); } if (RepeatRunButton != nullptr) { @@ -25751,7 +25820,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (DismissRecapButtonLabel != nullptr) { - DismissRecapButtonLabel->SetText(FText::FromString(TEXT("Dismiss Recap"))); + DisplayedDismissRecapActionLabel = TEXT("Dismiss Recap"); + DismissRecapButtonLabel->SetText(FText::FromString(DisplayedDismissRecapActionLabel)); + } + else + { + DisplayedDismissRecapActionLabel = TEXT("Dismiss Recap"); } if (DismissRecapButton != nullptr) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 1ed03e5..2d28f84 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -883,6 +883,10 @@ public: FHyperTwistTrainingCoachDashboardRunRecapDetailInspectSurface GetDisplayedRunRecapDetailInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface + GetDisplayedRunRecapActionControlInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface GetDisplayedActiveAttemptTimerStatusInspectSurface() const; @@ -2024,6 +2028,8 @@ private: FString DisplayedActiveRunSummaryLine; FString DisplayedRunRecapStatusLine; FString DisplayedRunRecapDetailLine; + FString DisplayedRepeatRunActionLabel; + FString DisplayedDismissRecapActionLabel; FString DisplayedCurrentCasePromptLine; FString DisplayedCurrentCaseNotationLine; FString DisplayedAttemptTimeHintLabel; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 89ac55a..5b5acc8 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -13473,6 +13473,69 @@ struct FHyperTwistTrainingCoachDashboardRunRecapDetailInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface +{ + 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 RepeatLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DismissLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DisplayedRecapSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RetainedDeckLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RetainedSessionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedRunRecap = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasCurrentRunRecap = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasRetainedRunRecap = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasRetainedDeck = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasActiveRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasLiveAttemptTimer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRepeatEnabled = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bDismissEnabled = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasDisplayedRunRecap + || bRepeatEnabled + || bDismissEnabled; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 8fa63c0..15de528 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 on that same live dashboard consumer lane and adds dedicated run-recap status and run-recap detail inspect surfaces over the current-or-retained recap presentation band, so gameplay/Blueprint callers can inspect the exact displayed recap lines plus the retained session/template context behind them without scraping widget text or reopening recap derivation - - the next best clean move is to stay adjacent in that same live dashboard consumer lane and expose the run-recap repeat/dismiss action control surface over the already-retained recap action labels and enablement posture + - the latest landed bounded `Phase 4` packet stays adjacent in that same live dashboard consumer lane and adds the run-recap action control inspect surface over the already-retained recap action labels and enablement posture, so gameplay/Blueprint callers can inspect the exact surfaced repeat/dismiss labels and their gating state without scraping widget button text + - the next best clean move is to stay adjacent in that same live dashboard consumer lane and expose the preferred template-launch plus recommended/follow-up preview action control surface trio over the already-derived launch/preview button labels and enablement posture - 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_LIVE_RUN_RECAP_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_RUN_RECAP_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md new file mode 100644 index 0000000..90f77f3 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_RUN_RECAP_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md @@ -0,0 +1,54 @@ +# HyperTwist Phase 4 dashboard live run-recap action control inspect surface packet + +- bounded Phase `4` dashboard-consumer slice +- date: `2026-05-10` + +## Intent + +Stay adjacent in the live dashboard consumer lane and expose the run-recap action controls directly to gameplay and Blueprint callers. + +## Why this packet exists + +`UHyperTwistCoachDashboardWidget` already displayed: + +- the `Repeat Run` action label +- the `Dismiss Recap` action label +- the enablement posture behind those two recap controls + +But that recap-control band still only surfaced through button labels and `SetIsEnabled` state. Callers still had to scrape widget controls to understand whether the dashboard is willing to restart the retained recap deck, whether a stored recap can be dismissed, and which retained recap source is currently gating those actions. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface` +- added `GetDisplayedRunRecapActionControlInspectSurface()` +- retained the exact displayed `Repeat Run` and `Dismiss Recap` labels inside the widget so the new surface reports the same live strings the dashboard is already presenting +- kept the packet bounded to `UHyperTwistCoachDashboardWidget` plus shared surface types, with no recap-persistence refactor 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 exact displayed `Repeat Run` label +- the exact displayed `Dismiss Recap` label +- whether repeat is currently enabled +- whether dismiss is currently enabled +- whether the gating recap is current versus retained +- whether a retained deck and retained session are currently available behind those controls + +without scraping widget button text or reopening recap-control gating logic. + +## Validation + +- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln` + - passed with `0 Warning(s), 0 Error(s)` + +## Next clean slice + +Stay on the same live dashboard consumer lane and expose the preferred template-launch plus recommended/follow-up preview action control surface trio over the already-derived launch/preview button labels and enablement posture.