Add primary coach orchestration entry lane

This commit is contained in:
axiomlogicnexus 2026-05-06 03:31:10 +02:00
parent 6d37f63c6f
commit d4f4ad3401
10 changed files with 198 additions and 13 deletions

View file

@ -2768,19 +2768,7 @@ void UHyperTwistCoachDashboardWidget::RefreshCoachDashboardView()
FHyperTwistTrainingRunState UHyperTwistCoachDashboardWidget::ExecutePrimaryCoachAction()
{
FHyperTwistTrainingRunState RunState;
if (CachedCoachPanelState.bCanStartQueuedRun)
{
RunState = StartNextQueuedCoachRun(DefaultCoachMaxCases, FString());
}
else if (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
{
RunState = StartCoachFollowUpRun(DefaultCoachMaxCases, FString());
}
else if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
RunState = StartCoachRecommendedRun(DefaultCoachMaxCases, FString());
}
const FHyperTwistTrainingRunState RunState = StartPrimaryCoachAction(DefaultCoachMaxCases, FString());
SyncRetainedRunRecap();
SyncSelectedQueueEntry();

View file

@ -252,6 +252,22 @@ FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachFollowUpRu
return CachedRunState;
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartPrimaryCoachAction(
const int32 MaxCases,
const FString& StartActionSourceLabel
)
{
LastStepResult = FHyperTwistTrainingRunStepResult();
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartPrimaryCoachAction(
this,
DefaultSessionId + TEXT("_primary"),
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases,
StartActionSourceLabel
);
RefreshTrainingState();
return CachedRunState;
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartNextQueuedCoachRun(
const int32 MaxCases,
const FString& StartActionSourceLabel

View file

@ -1328,6 +1328,21 @@ FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartCoachFollowU
return FHyperTwistTrainingRunState();
}
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartPrimaryCoachAction(
UObject* WorldContextObject,
const FString& SessionId,
int32 MaxCases,
const FString& StartActionSourceLabel
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->StartPrimaryCoachAction(SessionId, MaxCases, StartActionSourceLabel);
}
return FHyperTwistTrainingRunState();
}
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartNextQueuedCoachRun(
UObject* WorldContextObject,
const FString& SessionId,

View file

@ -230,6 +230,20 @@ FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachFollowUpT
return CachedRunState;
}
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartPrimaryCoachTrainingRun(
const int32 MaxCases
)
{
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartPrimaryCoachAction(
this,
AutoStartSessionId + TEXT("_primary"),
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases,
FString()
);
RefreshCachedTrainingState();
return CachedRunState;
}
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartNextQueuedCoachTrainingRun(
const int32 MaxCases
)

View file

@ -3193,6 +3193,29 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachFollowUpRun(
return RunState;
}
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartPrimaryCoachAction(
const FString& SessionId,
int32 MaxCases,
const FString& StartActionSourceLabel
)
{
const FHyperTwistTrainingCoachPanelState PanelState = GetActiveCoachPanelState();
if (PanelState.bCanStartQueuedRun)
{
return StartNextQueuedCoachRun(SessionId, MaxCases, StartActionSourceLabel);
}
if (PanelState.bCanStartCoachFollowUpRun && PanelState.bHasFollowUpGuidance)
{
return StartCoachFollowUpRun(SessionId, MaxCases, StartActionSourceLabel);
}
if (PanelState.bCanStartCoachRecommendedRun)
{
return StartCoachRecommendedRun(SessionId, MaxCases, StartActionSourceLabel);
}
return FHyperTwistTrainingRunState();
}
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartNextQueuedCoachRun(
const FString& SessionId,
int32 MaxCases,

View file

@ -181,6 +181,12 @@ public:
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartPrimaryCoachAction(
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartNextQueuedCoachRun(
int32 MaxCases,

View file

@ -548,6 +548,14 @@ public:
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
static FHyperTwistTrainingRunState StartPrimaryCoachAction(
UObject* WorldContextObject,
const FString& SessionId,
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
static FHyperTwistTrainingRunState StartNextQueuedCoachRun(
UObject* WorldContextObject,

View file

@ -156,6 +156,9 @@ public:
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachFollowUpTrainingRun(int32 MaxCases);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartPrimaryCoachTrainingRun(int32 MaxCases);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartNextQueuedCoachTrainingRun(int32 MaxCases);

View file

@ -444,6 +444,13 @@ public:
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartPrimaryCoachAction(
const FString& SessionId,
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartNextQueuedCoachRun(
const FString& SessionId,

View file

@ -0,0 +1,105 @@
# HyperTwist Phase 4 orchestration entry packet
Created on `2026-05-06`
Status:
- first-party HyperTwist packet
- bounded post-Phase-`3` implementation slice
## Purpose
This packet starts Phase `4` with an orchestration-first slice over the already-closed training/coaching backend.
The open task is:
- expose one production-facing primary coach orchestration action above the existing low-level coach-run launch methods
It is not:
- a recognition-provider packet
- a generated-mode backend packet
- a dashboard redesign
- a broad runtime refactor
## Scope
Bounded lane:
- create one first-party primary coach orchestration entry action in the subsystem and runtime library
- surface that action through the generic panel widget and session actor consumers
- make the dashboard use that orchestration action instead of owning the launch-choice branching itself
Required result:
- the repo has a stable first-class action for:
- launch next queued coach work when runnable
- otherwise launch follow-up work when available
- otherwise launch recommended work when available
- dashboard, panel widget, and session actor can all consume that same orchestration action
Out of scope:
- timer-phase orchestration
- provider-backed recognition integration
- new queue heuristics
- changes to generated-mode request/execution behavior
## Current owned product truth
These pieces are already live:
- low-level coach launch primitives:
- recommended
- follow-up
- next queued
- selected queue entry
- consumer surfaces over those primitives in:
- runtime library
- panel widget
- session actor
- dashboard widget
What was still missing:
- one explicit orchestration action above those low-level primitives
- removal of dashboard-local branching as the only place where the primary coach launch order existed
## Implementation target
The slice should do the following:
1. add `StartPrimaryCoachAction(...)` to the training subsystem
2. add the matching runtime-library wrapper
3. add matching thin wrappers to the panel widget and session actor
4. update the dashboard widget to call that action instead of hand-owning the queued / follow-up / recommended branching
## Suggested file ownership
Primary edit surfaces:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSubsystem.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRuntimeLibrary.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp`
- `docs/arch/HYPERTWIST_PHASE4_ORCHESTRATION_ENTRY_PACKET_2026-05-06.md`
## Acceptance criteria
- one first-party primary coach orchestration action exists above the low-level launch primitives
- dashboard launch branching uses that action
- panel widget and session actor surface that action too
- full UE build still succeeds
## Validation checklist
1. build `UnrealHyperTwist.sln`
2. confirm dashboard launch branching now routes through the orchestration action
3. confirm panel widget and session actor expose the same action
That is the packet.