From e76ee5814d565bd3dfb9eb68b1c36656b08eb273 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Sun, 10 May 2026 02:54:38 +0200 Subject: [PATCH] Expose dashboard decision history navigation surface --- .../HyperTwistCoachDashboardWidget.cpp | 139 +++++++++++++++++- .../HyperTwistCoachDashboardWidget.h | 6 + .../HyperTwistTrainingTypes.h | 78 ++++++++++ ...RTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md | 4 +- ...NTROL_INSPECT_SURFACE_PACKET_2026-05-10.md | 52 +++++++ 5 files changed, 275 insertions(+), 4 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_DASHBOARD_GUIDANCE_DECISION_HISTORY_NAVIGATION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 70e8b39..bf01416 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -8998,6 +8998,137 @@ UHyperTwistCoachDashboardWidget::GetSelectedGuidanceDecisionHistoryInspectSurfac return Surface; } +FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryNavigationInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedGuidanceDecisionHistoryNavigationInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryNavigationInspectSurface Surface; + Surface.Headline = TEXT("Decision history navigation"); + Surface.bClosedLoopIdleActionability = IsCoachLoopActionabilityClosed(); + + const auto BuildDecisionActionLabel = + [this](const EHyperTwistCoachDashboardOutcomeAction Action) -> FString + { + return Action == EHyperTwistCoachDashboardOutcomeAction::None + ? TEXT("none") + : BuildGuidanceOutcomeActionLabel(Action); + }; + + const EHyperTwistCoachDashboardOutcomeAction BaseAction = DeriveGuidanceOutcomeAction(); + const FHyperTwistCoachDashboardDecisionWeighting Weighting = + BuildGuidanceDecisionWeighting(BaseAction); + const FHyperTwistCoachDashboardDecisionHistoryEntry* SelectedEntry = + SelectedGuidanceDecisionIndex >= 0 + && SelectedGuidanceDecisionIndex < GuidanceDecisionHistoryEntries.Num() + ? &GuidanceDecisionHistoryEntries[SelectedGuidanceDecisionIndex] + : nullptr; + const bool bNavigationEnabled = GuidanceDecisionHistoryEntries.Num() > 1; + + Surface.PreviousLabel = !DisplayedPreviousGuidanceDecisionNavigationLabel.IsEmpty() + ? DisplayedPreviousGuidanceDecisionNavigationLabel + : TEXT("Prev Decision"); + Surface.NextLabel = !DisplayedNextGuidanceDecisionNavigationLabel.IsEmpty() + ? DisplayedNextGuidanceDecisionNavigationLabel + : TEXT("Next Decision"); + Surface.SelectedEntryIndex = SelectedGuidanceDecisionIndex; + Surface.HistoryEntryCount = GuidanceDecisionHistoryEntries.Num(); + Surface.bHasSelectedEntry = + SelectedEntry != nullptr && SelectedEntry->IsStructurallyValid(); + Surface.bSelectedEntryIsLatest = + Surface.bHasSelectedEntry + && SelectedGuidanceDecisionIndex == GuidanceDecisionHistoryEntries.Num() - 1; + Surface.bPreviousEnabled = bNavigationEnabled; + Surface.bNextEnabled = bNavigationEnabled; + Surface.bHasWeighting = Weighting.IsStructurallyValid(); + Surface.WeightingStatusLine = Weighting.IsStructurallyValid() + ? FString::Printf( + TEXT("Weighting: %d matching decisions | Base: %s | Weighted: %s | Last applied: %s"), + Weighting.MatchingDecisionCount, + *BuildGuidanceOutcomeActionLabel(Weighting.BaseAction), + *BuildGuidanceOutcomeActionLabel(Weighting.WeightedAction), + *BuildGuidanceOutcomeActionLabel(Weighting.LastAppliedAction)) + : TEXT("Weighting: no matching authored decision history yet; the current outcome action is recap-derived only."); + if (Surface.bClosedLoopIdleActionability) + { + Surface.WeightingStatusLine = + TEXT("Weighting: retained outcome history remains inspectable, but no live guidance-outcome action is active."); + } + Surface.LastAppliedActionLabel = BuildDecisionActionLabel(Weighting.LastAppliedAction); + + if (!Surface.bHasSelectedEntry) + { + Surface.SelectedEntryOrdinal = TEXT("none"); + Surface.RecordedAtUtc = TEXT("n/a"); + Surface.GuidanceLaneLabel = TEXT("none"); + Surface.ScopeLabel = TEXT("n/a"); + Surface.RecommendedActionLabel = TEXT("none"); + Surface.AppliedActionLabel = TEXT("none"); + Surface.StatusLine = + TEXT("Decision navigation: no applied guidance-outcome decisions recorded yet."); + Surface.DetailLine = FString::Printf( + TEXT("%s: %s | %s: %s | last applied: %s | selected latest: no"), + *Surface.PreviousLabel, + Surface.bPreviousEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.NextLabel, + Surface.bNextEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.LastAppliedActionLabel); + if (Surface.bClosedLoopIdleActionability) + { + Surface.StatusLine = + TEXT("Decision navigation: no applied guidance-outcome decisions are selected; retained decision history remains inspectable in the closed-loop idle state."); + Surface.DetailLine = FString::Printf( + TEXT("%s: %s | %s: %s | last applied: %s | selected latest: no"), + *Surface.PreviousLabel, + Surface.bPreviousEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.NextLabel, + Surface.bNextEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.LastAppliedActionLabel); + } + return Surface; + } + + Surface.SelectedEntryOrdinal = FString::Printf( + TEXT("%d/%d"), + SelectedGuidanceDecisionIndex + 1, + GuidanceDecisionHistoryEntries.Num()); + Surface.RecordedAtUtc = SelectedEntry->RecordedAtUtc.IsEmpty() + ? TEXT("n/a") + : SelectedEntry->RecordedAtUtc; + Surface.GuidanceLaneLabel = + SelectedEntry->GuidanceLane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended + ? TEXT("recommended") + : (SelectedEntry->GuidanceLane + == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp + ? TEXT("follow-up") + : TEXT("none")); + Surface.ScopeLabel = + SelectedEntry->bSingleCaseScope ? TEXT("selected case") : TEXT("preview deck"); + Surface.RecommendedActionLabel = + SelectedEntry->RecommendedActionLabel.IsEmpty() + ? TEXT("none") + : SelectedEntry->RecommendedActionLabel; + Surface.AppliedActionLabel = + SelectedEntry->AppliedActionLabel.IsEmpty() + ? TEXT("none") + : SelectedEntry->AppliedActionLabel; + Surface.StatusLine = FString::Printf( + TEXT("Decision navigation: selected %s | Lane: %s | Scope: %s | Recorded: %s"), + *Surface.SelectedEntryOrdinal, + *Surface.GuidanceLaneLabel, + *Surface.ScopeLabel, + *Surface.RecordedAtUtc); + Surface.DetailLine = FString::Printf( + TEXT("%s: %s | %s: %s | recommended: %s | applied: %s | last applied: %s | selected latest: %s"), + *Surface.PreviousLabel, + Surface.bPreviousEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.NextLabel, + Surface.bNextEnabled ? TEXT("enabled") : TEXT("disabled"), + *Surface.RecommendedActionLabel, + *Surface.AppliedActionLabel, + *Surface.LastAppliedActionLabel, + Surface.bSelectedEntryIsLatest ? TEXT("yes") : TEXT("no")); + return Surface; +} + FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface UHyperTwistCoachDashboardWidget::GetSelectedTemplateLaunchHistoryInspectSurface() const { @@ -24409,7 +24540,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (PreviousGuidanceDecisionButtonLabel != nullptr) { - PreviousGuidanceDecisionButtonLabel->SetText(FText::FromString(TEXT("Prev Decision"))); + DisplayedPreviousGuidanceDecisionNavigationLabel = TEXT("Prev Decision"); + PreviousGuidanceDecisionButtonLabel->SetText(FText::FromString( + DisplayedPreviousGuidanceDecisionNavigationLabel)); } if (PreviousGuidanceDecisionButton != nullptr) { @@ -24417,7 +24550,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (NextGuidanceDecisionButtonLabel != nullptr) { - NextGuidanceDecisionButtonLabel->SetText(FText::FromString(TEXT("Next Decision"))); + DisplayedNextGuidanceDecisionNavigationLabel = TEXT("Next Decision"); + NextGuidanceDecisionButtonLabel->SetText(FText::FromString( + DisplayedNextGuidanceDecisionNavigationLabel)); } if (NextGuidanceDecisionButton != nullptr) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 8feedac..d060361 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -783,6 +783,10 @@ public: FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface GetSelectedGuidanceDecisionHistoryInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryNavigationInspectSurface + GetDisplayedGuidanceDecisionHistoryNavigationInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface GetSelectedTemplateLaunchHistoryInspectSurface() const; @@ -2026,6 +2030,8 @@ private: FString DisplayedNextMethodDrillFollowUpNavigationLabel; FString DisplayedPreviousHandoffHistoryNavigationLabel; FString DisplayedNextHandoffHistoryNavigationLabel; + FString DisplayedPreviousGuidanceDecisionNavigationLabel; + FString DisplayedNextGuidanceDecisionNavigationLabel; 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 4682212..5d8d964 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -9847,6 +9847,84 @@ struct FHyperTwistTrainingCoachDashboardHandoffHistoryInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryNavigationInspectSurface +{ + 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 GuidanceLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ScopeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString AppliedActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString WeightingStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LastAppliedActionLabel; + + 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 bHasWeighting = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleActionability = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasSelectedEntry + || bPreviousEnabled + || bNextEnabled; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index e5f3c19..706d3df 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 handoff history navigation control inspect surface over already-retained handoff-pattern shifts, so gameplay/Blueprint callers can inspect the live prev/next handoff labels, enablement posture, selected retained handoff ordinal, and recommendation/posture/readiness/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 guidance decision 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 guidance decision history navigation control inspect surface over already-retained authored decision outcomes, so gameplay/Blueprint callers can inspect the live prev/next decision labels, enablement posture, selected retained decision ordinal, and lane/scope/action/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 template launch 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_GUIDANCE_DECISION_HISTORY_NAVIGATION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_GUIDANCE_DECISION_HISTORY_NAVIGATION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md new file mode 100644 index 0000000..6a8d1bb --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_GUIDANCE_DECISION_HISTORY_NAVIGATION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md @@ -0,0 +1,52 @@ +# HyperTwist Phase 4 dashboard guidance decision 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 guidance decision history navigation controls directly to gameplay and Blueprint callers. + +## Why this packet exists + +The dashboard already derived and displayed: + +- the previous decision-history button label +- the next decision-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 decision-history navigation is available and which retained authored outcome decision is currently selected. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryNavigationInspectSurface` +- added `GetDisplayedGuidanceDecisionHistoryNavigationInspectSurface()` +- retained the exact displayed previous/next decision-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 decision-history labels +- whether decision-history navigation is currently enabled +- the selected retained decision ordinal, recorded time, lane, scope, recommended action, and applied action +- the retained weighting status, last-applied action 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 template launch history navigation control surface.