diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 18a888f..7d4234d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -15948,6 +15948,87 @@ UHyperTwistCoachDashboardWidget::GetDisplayedExecutionActionControlBandInspectSu return Surface; } +FHyperTwistTrainingCoachDashboardCurrentCaseTimerBandInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedCurrentCaseTimerBandInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardCurrentCaseTimerBandInspectSurface Surface; + Surface.Headline = TEXT("Current-case timer band"); + + const FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface TimerSurface = + GetDisplayedActiveAttemptTimerStatusInspectSurface(); + const FHyperTwistTrainingCoachDashboardCurrentCaseDeckStatusInspectSurface CaseSurface = + GetDisplayedCurrentCaseDeckStatusInspectSurface(); + const FHyperTwistTrainingCoachDashboardAttemptTimeInputHintInspectSurface InputHintSurface = + GetDisplayedAttemptTimeInputHintInspectSurface(); + const FHyperTwistTrainingCoachDashboardLiveTimerSplitControlLabelInspectSurface SplitSurface = + GetDisplayedLiveTimerSplitControlLabelInspectSurface(); + const FHyperTwistTrainingCoachDashboardAttemptReviewNavigationInspectSurface ReviewSurface = + GetDisplayedAttemptReviewNavigationInspectSurface(); + + Surface.AttemptInputLine = TimerSurface.AttemptInputLine.IsEmpty() + ? TEXT("Attempt input: unavailable.") + : TimerSurface.AttemptInputLine; + Surface.TimerStatusLine = TimerSurface.TimerStatusLine.IsEmpty() + ? TEXT("Timer status: unavailable.") + : TimerSurface.TimerStatusLine; + Surface.CurrentCaseStatusLine = CaseSurface.StatusLine.IsEmpty() + ? TEXT("Current case status: unavailable.") + : CaseSurface.StatusLine; + Surface.CurrentCasePromptLine = CaseSurface.PromptLine.IsEmpty() + ? TEXT("Current case prompt: unavailable.") + : CaseSurface.PromptLine; + Surface.TimerControlLine = SplitSurface.TimerControlLine.IsEmpty() + ? TEXT("Timer control: unavailable.") + : SplitSurface.TimerControlLine; + Surface.SplitControlLine = SplitSurface.SplitControlLine.IsEmpty() + ? TEXT("Split control: unavailable.") + : SplitSurface.SplitControlLine; + Surface.AttemptReviewStatusLine = ReviewSurface.StatusLine.IsEmpty() + ? TEXT("Attempt review navigation: unavailable.") + : ReviewSurface.StatusLine; + Surface.ActiveDeckLabel = CaseSurface.ActiveDeckLabel.IsEmpty() ? TEXT("n/a") : CaseSurface.ActiveDeckLabel; + Surface.ActiveCaseId = CaseSurface.ActiveCaseId.IsEmpty() ? TEXT("n/a") : CaseSurface.ActiveCaseId; + Surface.TimerPhaseLabel = TimerSurface.TimerPhaseLabel.IsEmpty() ? TEXT("idle") : TimerSurface.TimerPhaseLabel; + Surface.HintLabel = InputHintSurface.HintLabel.IsEmpty() ? TEXT("n/a") : InputHintSurface.HintLabel; + Surface.AttemptOrdinal = ReviewSurface.AttemptOrdinal.IsEmpty() ? TEXT("none") : ReviewSurface.AttemptOrdinal; + Surface.TimeTargetMs = CaseSurface.TimeTargetMs > 0 ? CaseSurface.TimeTargetMs : TimerSurface.TargetTimeMs; + Surface.DisplayedElapsedMs = TimerSurface.DisplayedElapsedMs; + Surface.TotalAttemptCount = ReviewSurface.TotalAttemptCount; + Surface.bHasActiveRun = CaseSurface.bHasActiveRun || InputHintSurface.bHasActiveRun; + Surface.bHasRunnableCurrentCase = CaseSurface.bHasRunnableCurrentCase || InputHintSurface.bHasRunnableCurrentCase || SplitSurface.bHasRunnableCurrentCase; + Surface.bHasLiveAttemptTimer = TimerSurface.bHasLiveAttemptTimer || InputHintSurface.bHasLiveAttemptTimer || SplitSurface.bHasLiveAttemptTimer; + Surface.bLiveInspectionPhase = TimerSurface.bLiveInspectionPhase || SplitSurface.bLiveInspectionPhase; + Surface.bLiveSolvePhase = TimerSurface.bLiveSolvePhase || SplitSurface.bLiveSolvePhase; + Surface.bAttemptInputEnabled = InputHintSurface.bAttemptInputEnabled; + Surface.bCanStartOrAdvanceTimer = SplitSurface.bCanStartOrAdvanceTimer; + Surface.bCanCaptureSplit = SplitSurface.bCanCaptureSplit; + Surface.bCanSelectPreviousAttempt = ReviewSurface.bCanSelectPreviousAttempt; + Surface.bCanSelectNextAttempt = ReviewSurface.bCanSelectNextAttempt; + Surface.bClosedLoopIdleDashboard = CaseSurface.bClosedLoopIdleDashboard; + Surface.StatusLine = FString::Printf( + TEXT("Current-case timer band: deck %s | case %s | phase %s | input %s | timer %s | split %s"), + *Surface.ActiveDeckLabel, + *Surface.ActiveCaseId, + *Surface.TimerPhaseLabel, + Surface.bAttemptInputEnabled ? TEXT("enabled") : TEXT("disabled"), + Surface.bCanStartOrAdvanceTimer ? TEXT("enabled") : TEXT("disabled"), + Surface.bCanCaptureSplit ? TEXT("enabled") : TEXT("disabled")); + Surface.DetailLine = FString::Printf( + TEXT("%s | %s | %s | %s | %s | %s | target %d | elapsed %d | attempts %s | live timer %s | closed-loop idle %s"), + *Surface.AttemptInputLine, + *Surface.TimerStatusLine, + *Surface.CurrentCaseStatusLine, + *Surface.CurrentCasePromptLine, + *Surface.TimerControlLine, + *Surface.AttemptReviewStatusLine, + Surface.TimeTargetMs, + Surface.DisplayedElapsedMs, + *Surface.AttemptOrdinal, + Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"), + Surface.bClosedLoopIdleDashboard ? TEXT("yes") : TEXT("no")); + return Surface; +} + FHyperTwistTrainingCoachDashboardVerificationActionInspectSurface UHyperTwistCoachDashboardWidget::GetDisplayedVerificationActionInspectSurface() const { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index f556f56..d996944 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -1051,6 +1051,10 @@ public: FHyperTwistTrainingCoachDashboardExecutionActionControlBandInspectSurface GetDisplayedExecutionActionControlBandInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardCurrentCaseTimerBandInspectSurface + GetDisplayedCurrentCaseTimerBandInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") FHyperTwistTrainingCoachDashboardVerificationActionInspectSurface GetDisplayedVerificationActionInspectSurface() const; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 030a564..6cc686b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -14938,6 +14938,109 @@ struct FHyperTwistTrainingCoachDashboardExecutionActionControlBandInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardCurrentCaseTimerBandInspectSurface +{ + 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 AttemptInputLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TimerStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentCaseStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentCasePromptLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TimerControlLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SplitControlLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString AttemptReviewStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveDeckLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveCaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TimerPhaseLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString HintLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString AttemptOrdinal; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TimeTargetMs = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DisplayedElapsedMs = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TotalAttemptCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasActiveRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasRunnableCurrentCase = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasLiveAttemptTimer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bLiveInspectionPhase = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bLiveSolvePhase = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bAttemptInputEnabled = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanStartOrAdvanceTimer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanCaptureSplit = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanSelectPreviousAttempt = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanSelectNextAttempt = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleDashboard = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasRunnableCurrentCase + || bHasLiveAttemptTimer + || bAttemptInputEnabled + || bCanStartOrAdvanceTimer; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachDashboardVerificationActionInspectSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 22b4c69..f3805a0 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -77,8 +77,8 @@ Current correction call from the source-exposed audit and current execution refr - the latest landed bounded `Phase 4` packet deliberately chooses another adjacent live dashboard consumer band and adds a single queue scheduling / recovery seam over the already-landed queue counts / status, schedule horizon, schedule policy / friction, and queue recovery live status/detail surfaces, so gameplay/Blueprint callers can inspect that whole scheduling-and-recovery dashboard cluster without stitching five adjacent getters together - the latest landed bounded `Phase 4` packet deliberately chooses another adjacent live dashboard consumer band and adds a single closure-recovery seam over the already-landed closure-recovery live status/detail and closure-recovery history navigation surfaces, so gameplay/Blueprint callers can inspect that whole closure-recovery dashboard cluster without stitching three adjacent getters together - the latest landed bounded `Phase 4` packet deliberately chooses another adjacent live dashboard consumer band and adds a single queue-suppression seam over the already-landed queue-suppression live summary/detail and queue-suppression history navigation surfaces, so gameplay/Blueprint callers can inspect that whole queue-suppression dashboard cluster without stitching three adjacent getters together - - the latest landed bounded `Phase 4` packet deliberately chooses the next adjacent live dashboard consumer band in the compact handoff/transition neighborhood and adds a single handoff-transition seam over the already-landed handoff recommendation, handoff status, transition status, and run-action-label surfaces, so gameplay/Blueprint callers can inspect that whole compact live handoff/transition dashboard cluster without stitching four adjacent getters together - - with the retained-history text-band lane, the template-launch / preview action-control band, the execution-action-control band, the verification / recognition / review-policy status band, the selected-queue band, the queue scheduling / recovery band, the closure-recovery band, the queue-suppression band, the handoff-history band, the guidance-decision-history band, the template-launch-history band, the method-drill recovery-outcome-history band, the method-drill recovery-memory-history band, the method-drill follow-up-history band, the method-drill operational band, the guidance-preview band, the guidance-reasoning band, the run-state recap band, and the handoff-transition band now all closed baseline, the next best clean move is to deliberately choose another adjacent live dashboard consumer band rather than reopen already-landed surfaces by drift + - the latest landed bounded `Phase 4` packet deliberately chooses the next adjacent live dashboard consumer band in the current-case / timer neighborhood and adds a single current-case-timer seam over the already-landed active-attempt timer, current-case/deck status, attempt-time hint, live timer split control, and attempt-review navigation surfaces, so gameplay/Blueprint callers can inspect that whole current-case/timer dashboard cluster without stitching five adjacent getters together + - with the retained-history text-band lane, the template-launch / preview action-control band, the execution-action-control band, the verification / recognition / review-policy status band, the selected-queue band, the queue scheduling / recovery band, the closure-recovery band, the queue-suppression band, the handoff-history band, the guidance-decision-history band, the template-launch-history band, the method-drill recovery-outcome-history band, the method-drill recovery-memory-history band, the method-drill follow-up-history band, the method-drill operational band, the guidance-preview band, the guidance-reasoning band, the run-state recap band, the handoff-transition band, and the current-case timer band now all closed baseline, the next best clean move is to deliberately choose another adjacent live dashboard consumer band rather than reopen already-landed surfaces 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 diff --git a/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_CURRENT_CASE_TIMER_BAND_INSPECT_SURFACE_PACKET_2026-05-11.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_CURRENT_CASE_TIMER_BAND_INSPECT_SURFACE_PACKET_2026-05-11.md new file mode 100644 index 0000000..4bac5f7 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_CURRENT_CASE_TIMER_BAND_INSPECT_SURFACE_PACKET_2026-05-11.md @@ -0,0 +1,53 @@ +# HyperTwist Phase 4 dashboard current-case timer band inspect surface packet + +- bounded Phase `4` dashboard-consumer slice +- date: `2026-05-11` + +## Intent + +Deliberately choose the next adjacent live dashboard consumer band in the current-case / timer neighborhood and expose the active-attempt timer, current-case/deck status, attempt-time hint, live timer split control, and attempt-review navigation cluster as one inspectable gameplay and Blueprint seam. + +## Why this packet exists + +`UHyperTwistCoachDashboardWidget` already exposed five adjacent current-case / timer surfaces: + +- `GetDisplayedActiveAttemptTimerStatusInspectSurface()` +- `GetDisplayedCurrentCaseDeckStatusInspectSurface()` +- `GetDisplayedAttemptTimeInputHintInspectSurface()` +- `GetDisplayedLiveTimerSplitControlLabelInspectSurface()` +- `GetDisplayedAttemptReviewNavigationInspectSurface()` + +But callers that wanted to reason about that whole current-case/timer dashboard cluster still had to stitch five adjacent getters together manually. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardCurrentCaseTimerBandInspectSurface` +- added `GetDisplayedCurrentCaseTimerBandInspectSurface()` +- composed the new surface entirely over the already-landed active-attempt timer, current-case/deck status, attempt-time hint, live timer split control, and attempt-review navigation surfaces +- kept the packet bounded by avoiding any new backend derivation or new widget-state caching + +## 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 whole adjacent current-case/timer dashboard band through one surface +- timer status, current-case status, attempt-time hint, timer/split control, and attempt-review navigation lines together +- active deck/case identity, timer phase, target time, elapsed time, attempt-review posture, and current-case timer readiness posture + +without stitching five separate adjacent getters together manually. + +## Validation + +- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln` + - passed with `0 Warning(s), 0 Error(s)` + +## Next clean slice + +Treat the retained-history text-band lane, the template-launch / preview action-control band, the execution-action-control band, the verification / recognition / review-policy status band, the selected-queue band, the queue scheduling / recovery band, the closure-recovery band, the queue-suppression band, the handoff-history band, the guidance-decision-history band, the template-launch-history band, the method-drill recovery-outcome-history band, the method-drill recovery-memory-history band, the method-drill follow-up-history band, the method-drill operational band, the guidance-preview band, the guidance-reasoning band, the run-state recap band, the handoff-transition band, and the current-case timer band as landed baseline, then deliberately choose another adjacent live dashboard consumer band instead of reopening already-landed surfaces by drift.