Expose dashboard decision history navigation surface
This commit is contained in:
parent
0e70c24da1
commit
e76ee5814d
5 changed files with 275 additions and 4 deletions
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
Loading…
Add table
Reference in a new issue