diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index e4a211c..b445c78 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -10650,6 +10650,106 @@ UHyperTwistCoachDashboardWidget::GetDisplayedGuidancePreviewCaseNavigationInspec return Surface; } +FHyperTwistTrainingCoachDashboardGuidancePreviewLaunchActionControlInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedGuidancePreviewLaunchActionControlInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidancePreviewLaunchActionControlInspectSurface Surface; + Surface.Headline = TEXT("Preview launch action control"); + + const auto BuildPreviewLaneLabel = [](const EHyperTwistCoachDashboardGuidancePreviewLane Lane) -> FString + { + return Lane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended + ? TEXT("recommended") + : (Lane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp + ? TEXT("follow-up") + : TEXT("none")); + }; + + const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid() + && CachedRunState.ActiveDeck.IsStructurallyValid(); + const FHyperTwistTrainingDeck* ActivePreviewDeck = GetActiveGuidancePreviewDeck(); + const FHyperTwistTrainingCase* SelectedGuidancePreviewCase = + ActivePreviewDeck != nullptr + && SelectedGuidancePreviewCaseIndex >= 0 + && SelectedGuidancePreviewCaseIndex < ActivePreviewDeck->Cases.Num() + ? &ActivePreviewDeck->Cases[SelectedGuidancePreviewCaseIndex] + : nullptr; + const bool bCanStartPreviewDeck = + !bHasLiveAttemptTimer + && !bHasActiveRun + && ActivePreviewDeck != nullptr + && ActivePreviewDeck->IsStructurallyValid(); + const bool bCanStartPreviewCase = + bCanStartPreviewDeck && SelectedGuidancePreviewCase != nullptr; + + Surface.StartPreviewDeckLabel = !DisplayedStartPreviewDeckActionLabel.IsEmpty() + ? DisplayedStartPreviewDeckActionLabel + : TEXT("Start Preview Deck"); + Surface.StartSelectedPreviewCaseLabel = !DisplayedStartPreviewCaseActionLabel.IsEmpty() + ? DisplayedStartPreviewCaseActionLabel + : TEXT("Start Selected Preview Case"); + Surface.ActivePreviewLaneLabel = BuildPreviewLaneLabel(ActiveGuidancePreviewLane); + Surface.DeckLabel = + ActivePreviewDeck != nullptr + ? (ActivePreviewDeck->Title.IsEmpty() ? ActivePreviewDeck->DeckId : ActivePreviewDeck->Title) + : TEXT("n/a"); + Surface.SelectedCaseId = + SelectedGuidancePreviewCase != nullptr && !SelectedGuidancePreviewCase->CaseId.IsEmpty() + ? SelectedGuidancePreviewCase->CaseId + : TEXT("n/a"); + Surface.SelectedCasePromptLabel = + SelectedGuidancePreviewCase != nullptr + && !SelectedGuidancePreviewCase->PromptLabel.IsEmpty() + ? SelectedGuidancePreviewCase->PromptLabel + : TEXT("n/a"); + Surface.SelectedCaseOrdinal = + SelectedGuidancePreviewCase != nullptr && ActivePreviewDeck != nullptr + ? FString::Printf(TEXT("%d/%d"), SelectedGuidancePreviewCaseIndex + 1, ActivePreviewDeck->Cases.Num()) + : TEXT("none"); + Surface.SelectedCaseIndex = SelectedGuidancePreviewCaseIndex; + Surface.TotalCaseCount = ActivePreviewDeck != nullptr ? ActivePreviewDeck->Cases.Num() : 0; + Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer; + Surface.bHasActiveRun = bHasActiveRun; + Surface.bHasActivePreviewDeck = ActivePreviewDeck != nullptr; + Surface.bHasSelectedCase = SelectedGuidancePreviewCase != nullptr; + Surface.bStartPreviewDeckEnabled = bCanStartPreviewDeck; + Surface.bStartSelectedPreviewCaseEnabled = bCanStartPreviewCase; + + const bool bQueueRecoveryMemorySource = + HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource( + CachedCoachPanelState.PreferredGuidanceSourceLabel); + Surface.bClosedLoopIdleDashboard = + IsCoachLoopActionabilityClosed() + && ( + bQueueRecoveryMemorySource + || !CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + || DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None); + + Surface.StatusLine = FString::Printf( + TEXT("%s: %s | %s: %s | lane %s | deck %s"), + *Surface.StartPreviewDeckLabel, + Surface.bStartPreviewDeckEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.StartSelectedPreviewCaseLabel, + Surface.bStartSelectedPreviewCaseEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.ActivePreviewLaneLabel, + *Surface.DeckLabel); + Surface.DetailLine = FString::Printf( + TEXT("Preview launch detail: selected %s | case %s | prompt %s | live timer %s | active run %s"), + *Surface.SelectedCaseOrdinal, + *Surface.SelectedCaseId, + *Surface.SelectedCasePromptLabel, + Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"), + Surface.bHasActiveRun ? TEXT("yes") : TEXT("no")); + if (Surface.bClosedLoopIdleDashboard) + { + Surface.StatusLine = + TEXT("Preview launch control: no active guidance preview remains in the closed-loop idle state."); + Surface.DetailLine = + TEXT("Preview launch detail: retained coach memory remains inspectable, but live preview-deck and preview-case launch actions are inactive."); + } + return Surface; +} + FHyperTwistTrainingCoachDashboardMethodDrillDiagnosticsInspectSurface UHyperTwistCoachDashboardWidget::GetDisplayedMethodDrillDiagnosticsInspectSurface() const { @@ -26477,7 +26577,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (StartPreviewDeckButtonLabel != nullptr) { - StartPreviewDeckButtonLabel->SetText(FText::FromString(TEXT("Start Preview Deck"))); + DisplayedStartPreviewDeckActionLabel = TEXT("Start Preview Deck"); + StartPreviewDeckButtonLabel->SetText(FText::FromString( + DisplayedStartPreviewDeckActionLabel)); } if (StartPreviewDeckButton != nullptr) { @@ -26485,7 +26587,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (StartPreviewCaseButtonLabel != nullptr) { - StartPreviewCaseButtonLabel->SetText(FText::FromString(TEXT("Start Selected Preview Case"))); + DisplayedStartPreviewCaseActionLabel = TEXT("Start Selected Preview Case"); + StartPreviewCaseButtonLabel->SetText(FText::FromString( + DisplayedStartPreviewCaseActionLabel)); } if (StartPreviewCaseButton != nullptr) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 6aabbb2..2ba0581 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -835,6 +835,10 @@ public: FHyperTwistTrainingCoachDashboardGuidancePreviewCaseNavigationInspectSurface GetDisplayedGuidancePreviewCaseNavigationInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidancePreviewLaunchActionControlInspectSurface + GetDisplayedGuidancePreviewLaunchActionControlInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") FHyperTwistTrainingCoachDashboardMethodDrillDiagnosticsInspectSurface GetDisplayedMethodDrillDiagnosticsInspectSurface() const; @@ -2070,6 +2074,8 @@ private: FString DisplayedTimerStatusLine; FString DisplayedPreviewRecommendedActionLabel; FString DisplayedPreviewFollowUpActionLabel; + FString DisplayedStartPreviewDeckActionLabel; + FString DisplayedStartPreviewCaseActionLabel; FString DisplayedRecommendedDeckPreviewLine; FString DisplayedFollowUpDeckPreviewLine; FString DisplayedMethodDrillDiagnosticsStatusLine; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 2c03bbc..2009538 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -11370,6 +11370,79 @@ struct FHyperTwistTrainingCoachDashboardGuidancePreviewCaseNavigationInspectSurf } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardGuidancePreviewLaunchActionControlInspectSurface +{ + 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 StartPreviewDeckLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StartSelectedPreviewCaseLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActivePreviewLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedCaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedCasePromptLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedCaseOrdinal; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 SelectedCaseIndex = INDEX_NONE; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TotalCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasLiveAttemptTimer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasActiveRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasActivePreviewDeck = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelectedCase = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleDashboard = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStartPreviewDeckEnabled = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStartSelectedPreviewCaseEnabled = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasActivePreviewDeck + || bHasSelectedCase + || bStartPreviewDeckEnabled + || bStartSelectedPreviewCaseEnabled; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachDashboardGuidanceRationaleInspectSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 1737174..f829b11 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 guidance preview case navigation control surface over the already-derived `Prev Guidance Case` / `Next Guidance Case` labels and enablement posture, so gameplay/Blueprint callers can inspect the exact surfaced preview-case navigation 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 preview-deck plus start-selected-preview-case action control pair over the already-derived launch button labels and enablement posture + - the latest landed bounded `Phase 4` packet stays adjacent in that same live dashboard consumer lane and adds the guidance preview launch action control surface over the already-derived `Start Preview Deck` / `Start Selected Preview Case` labels and enablement posture, so gameplay/Blueprint callers can inspect the exact surfaced preview-launch labels, selected-case launch context, and gating state without scraping widget button text + - the preview-control seam is now functionally complete enough to stop widening it by drift; the next best clean move is to pivot slightly earlier in that same live dashboard consumer lane and expose the dedicated template-launch status/detail inspect pair over the exact displayed template-launch lines already cached in the widget - 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_GUIDANCE_PREVIEW_LAUNCH_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_GUIDANCE_PREVIEW_LAUNCH_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md new file mode 100644 index 0000000..5aa5ad6 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_GUIDANCE_PREVIEW_LAUNCH_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md @@ -0,0 +1,53 @@ +# HyperTwist Phase 4 dashboard live guidance preview launch 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 guidance preview launch controls directly to gameplay and Blueprint callers. + +## Why this packet exists + +`UHyperTwistCoachDashboardWidget` already displayed: + +- the `Start Preview Deck` action label +- the `Start Selected Preview Case` action label +- the enablement posture behind those two preview-launch controls + +But that control band still only surfaced through button labels and `SetIsEnabled` state. Callers still had to scrape widget controls to understand whether the dashboard currently has a launchable preview deck, whether a selected preview case can be launched directly, and what live run/timer gating is blocking those actions. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardGuidancePreviewLaunchActionControlInspectSurface` +- added `GetDisplayedGuidancePreviewLaunchActionControlInspectSurface()` +- retained the exact displayed `Start Preview Deck` and `Start Selected Preview Case` 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 preview-deck derivation 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 `Start Preview Deck` label +- the exact displayed `Start Selected Preview Case` label +- whether preview-deck launch is currently enabled +- whether selected-preview-case launch is currently enabled +- the active preview lane, selected case ordinal, selected case id, surfaced prompt label, and launch gating posture behind those two controls + +without scraping widget button text or reopening preview-launch gating logic. + +## Validation + +- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln` + - passed with `0 Warning(s), 0 Error(s)` + +## Next clean slice + +Treat the preview-control seam as functionally complete enough and pivot slightly earlier in the same live dashboard consumer lane to expose the dedicated template-launch status/detail inspect pair over the exact displayed template-launch lines already cached in the widget.