diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 042d0b1..c626571 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -8325,6 +8325,122 @@ UHyperTwistCoachDashboardWidget::GetSelectedMethodDrillFollowUpHistoryInspectSur return Surface; } +FHyperTwistTrainingCoachDashboardMethodDrillFollowUpHistoryNavigationInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedMethodDrillFollowUpHistoryNavigationInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardMethodDrillFollowUpHistoryNavigationInspectSurface Surface; + Surface.Headline = TEXT("Method-drill follow-up history navigation"); + + const FHyperTwistCoachDashboardMethodDrillFollowUpWeighting Weighting = + BuildMethodDrillFollowUpWeighting(); + const FHyperTwistCoachDashboardMethodDrillFollowUpHistoryEntry* SelectedEntry = + SelectedMethodDrillFollowUpHistoryIndex >= 0 + && SelectedMethodDrillFollowUpHistoryIndex < MethodDrillFollowUpHistoryEntries.Num() + ? &MethodDrillFollowUpHistoryEntries[SelectedMethodDrillFollowUpHistoryIndex] + : nullptr; + const bool bNavigationEnabled = MethodDrillFollowUpHistoryEntries.Num() > 1; + + Surface.PreviousLabel = !DisplayedPreviousMethodDrillFollowUpNavigationLabel.IsEmpty() + ? DisplayedPreviousMethodDrillFollowUpNavigationLabel + : TEXT("Prev Drill Follow-Up"); + Surface.NextLabel = !DisplayedNextMethodDrillFollowUpNavigationLabel.IsEmpty() + ? DisplayedNextMethodDrillFollowUpNavigationLabel + : TEXT("Next Drill Follow-Up"); + Surface.SelectedEntryIndex = SelectedMethodDrillFollowUpHistoryIndex; + Surface.HistoryEntryCount = MethodDrillFollowUpHistoryEntries.Num(); + Surface.bHasSelectedEntry = + SelectedEntry != nullptr && SelectedEntry->IsStructurallyValid(); + Surface.bSelectedEntryIsLatest = + Surface.bHasSelectedEntry + && SelectedMethodDrillFollowUpHistoryIndex + == MethodDrillFollowUpHistoryEntries.Num() - 1; + Surface.bPreviousEnabled = bNavigationEnabled; + Surface.bNextEnabled = bNavigationEnabled; + Surface.bHasHistoryWeighting = Weighting.IsStructurallyValid(); + Surface.bPreferDrillFollowUp = Weighting.bPreferDrillFollowUp; + Surface.bPreferCoachHandoff = Weighting.bPreferCoachHandoff; + Surface.bHasUpstreamStructureSignal = Weighting.bHasUpstreamStructureSignal; + Surface.OutcomePreferenceLine = !Weighting.IsStructurallyValid() + ? TEXT("no retained drill follow-up history yet") + : (Weighting.bPreferDrillFollowUp + ? (Weighting.bUsedUpstreamNarrowStructureSignal + ? TEXT("retained drill-follow-up packet structure history favors staying narrow with drill follow-up") + : TEXT("retained drill history favors staying narrow with drill follow-up")) + : (Weighting.bPreferCoachHandoff + ? (Weighting.bUsedUpstreamBroadStructureSignal + ? TEXT("retained drill-follow-up packet structure history favors handing control back to broader coach guidance") + : TEXT("retained drill history favors handing control back to broader coach guidance")) + : TEXT("retained drill history is balanced"))); + + if (!Surface.bHasSelectedEntry) + { + Surface.SelectedEntryOrdinal = TEXT("none"); + Surface.RecordedAtUtc = TEXT("n/a"); + Surface.StageLabel = TEXT("n/a"); + Surface.DiagnosticStateLabel = TEXT("n/a"); + Surface.RecommendedReviewGradeLabel = TEXT("n/a"); + Surface.SuggestedModeLabel = TEXT("n/a"); + Surface.PacketStructureLabel = TEXT("n/a"); + Surface.PacketStanceLabel = TEXT("n/a"); + Surface.StatusLine = + TEXT("Drill follow-up navigation: no retained drill follow-up outcomes recorded yet."); + Surface.DetailLine = FString::Printf( + TEXT("%s: %s | %s: %s | preference: %s | selected latest: no"), + *Surface.PreviousLabel, + Surface.bPreviousEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.NextLabel, + Surface.bNextEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.OutcomePreferenceLine); + return Surface; + } + + Surface.SelectedEntryOrdinal = FString::Printf( + TEXT("%d/%d"), + SelectedMethodDrillFollowUpHistoryIndex + 1, + MethodDrillFollowUpHistoryEntries.Num()); + Surface.RecordedAtUtc = SelectedEntry->RecordedAtUtc.IsEmpty() + ? TEXT("n/a") + : SelectedEntry->RecordedAtUtc; + Surface.StageLabel = + SelectedEntry->StageLabel.IsEmpty() ? TEXT("n/a") : SelectedEntry->StageLabel; + Surface.DiagnosticStateLabel = + HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + SelectedEntry->DiagnosticState); + Surface.RecommendedReviewGradeLabel = + HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + SelectedEntry->RecommendedReviewGrade); + Surface.SuggestedModeLabel = + HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + SelectedEntry->SuggestedMode); + Surface.PacketStructureLabel = + SelectedEntry->PacketStructureLabel.IsEmpty() + ? TEXT("default") + : SelectedEntry->PacketStructureLabel; + Surface.PacketStanceLabel = + SelectedEntry->PacketStanceLabel.IsEmpty() + ? TEXT("none") + : SelectedEntry->PacketStanceLabel; + Surface.StatusLine = FString::Printf( + TEXT("Drill follow-up navigation: selected %s | Stage: %s | review %s | packet: %s | Recorded: %s"), + *Surface.SelectedEntryOrdinal, + *Surface.StageLabel, + *Surface.RecommendedReviewGradeLabel, + *Surface.PacketStructureLabel, + *Surface.RecordedAtUtc); + Surface.DetailLine = FString::Printf( + TEXT("%s: %s | %s: %s | state: %s | mode: %s | preference: %s | stance: %s | selected latest: %s"), + *Surface.PreviousLabel, + Surface.bPreviousEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.NextLabel, + Surface.bNextEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.DiagnosticStateLabel, + *Surface.SuggestedModeLabel, + *Surface.OutcomePreferenceLine, + *Surface.PacketStanceLabel, + Surface.bSelectedEntryIsLatest ? TEXT("yes") : TEXT("no")); + return Surface; +} + FHyperTwistTrainingCoachDashboardHandoffHistoryInspectSurface UHyperTwistCoachDashboardWidget::GetSelectedHandoffHistoryInspectSurface() const { @@ -24348,8 +24464,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (PreviousMethodDrillFollowUpHistoryButtonLabel != nullptr) { + DisplayedPreviousMethodDrillFollowUpNavigationLabel = TEXT("Prev Drill Follow-Up"); PreviousMethodDrillFollowUpHistoryButtonLabel->SetText(FText::FromString( - TEXT("Prev Drill Follow-Up"))); + DisplayedPreviousMethodDrillFollowUpNavigationLabel)); } if (PreviousMethodDrillFollowUpHistoryButton != nullptr) { @@ -24358,8 +24475,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (NextMethodDrillFollowUpHistoryButtonLabel != nullptr) { + DisplayedNextMethodDrillFollowUpNavigationLabel = TEXT("Next Drill Follow-Up"); NextMethodDrillFollowUpHistoryButtonLabel->SetText(FText::FromString( - TEXT("Next Drill Follow-Up"))); + DisplayedNextMethodDrillFollowUpNavigationLabel)); } if (NextMethodDrillFollowUpHistoryButton != nullptr) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 83dec1c..5f45d3b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -767,6 +767,10 @@ public: FHyperTwistTrainingCoachDashboardMethodDrillFollowUpHistoryInspectSurface GetSelectedMethodDrillFollowUpHistoryInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardMethodDrillFollowUpHistoryNavigationInspectSurface + GetDisplayedMethodDrillFollowUpHistoryNavigationInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") FHyperTwistTrainingCoachDashboardHandoffHistoryInspectSurface GetSelectedHandoffHistoryInspectSurface() const; @@ -2014,6 +2018,8 @@ private: FString DisplayedNextMethodDrillRecoveryNavigationLabel; FString DisplayedPreviousMethodDrillRecoveryMemoryNavigationLabel; FString DisplayedNextMethodDrillRecoveryMemoryNavigationLabel; + FString DisplayedPreviousMethodDrillFollowUpNavigationLabel; + FString DisplayedNextMethodDrillFollowUpNavigationLabel; FString DisplayedPreviousAttemptNavigationLabel; FString DisplayedNextAttemptNavigationLabel; EHyperTwistCoachDashboardGuidancePreviewLane ActiveGuidancePreviewLane = diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index fdddcd4..81488cb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -9298,6 +9298,93 @@ struct FHyperTwistTrainingCoachDashboardMethodDrillRecoveryMemoryHistoryNavigati } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardMethodDrillFollowUpHistoryNavigationInspectSurface +{ + 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 StageLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DiagnosticStateLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedReviewGradeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SuggestedModeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PacketStructureLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PacketStanceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString OutcomePreferenceLine; + + 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 bHasHistoryWeighting = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreferDrillFollowUp = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreferCoachHandoff = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasUpstreamStructureSignal = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasSelectedEntry + || bPreviousEnabled + || bNextEnabled; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachDashboardMethodDrillFollowUpHistoryInspectSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index be64b76..a5a18d6 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 retained-history navigation/control lane and adds the method-drill recovery memory history navigation control inspect surface over already-retained upstream drill-recovery memory snapshots, so gameplay/Blueprint callers can inspect the live prev/next drill-memory labels, enablement posture, selected retained memory ordinal, and action/handoff/bias/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 method-drill follow-up 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 method-drill follow-up history navigation control inspect surface over already-retained drill follow-up outcomes, so gameplay/Blueprint callers can inspect the live prev/next drill-follow-up labels, enablement posture, selected retained follow-up ordinal, and review/preference/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 handoff history navigation control surface rather than drifting back into backend continuity or reopening the already-inspectable history detail seams - 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_METHOD_DRILL_FOLLOW_UP_HISTORY_NAVIGATION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_METHOD_DRILL_FOLLOW_UP_HISTORY_NAVIGATION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md new file mode 100644 index 0000000..1b7b883 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_METHOD_DRILL_FOLLOW_UP_HISTORY_NAVIGATION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md @@ -0,0 +1,52 @@ +# HyperTwist Phase 4 dashboard method-drill follow-up 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 method-drill follow-up history navigation controls directly to gameplay and Blueprint callers. + +## Why this packet exists + +The dashboard already derived and displayed: + +- the previous drill-follow-up-history button label +- the next drill-follow-up-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 drill-follow-up navigation is available and which retained follow-up outcome is currently selected. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardMethodDrillFollowUpHistoryNavigationInspectSurface` +- added `GetDisplayedMethodDrillFollowUpHistoryNavigationInspectSurface()` +- retained the exact displayed previous/next drill-follow-up-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 drill-follow-up-history labels +- whether drill-follow-up-history navigation is currently enabled +- the selected retained follow-up ordinal, recorded time, stage label, diagnostic state, and review grade +- the retained outcome-preference line, packet structure / stance 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 + +Stay on the retained-history navigation/control lane and expose the handoff history navigation control surface.