From 84b426443ae40f7a57cf64641f3e793fd0aba453 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Wed, 6 May 2026 22:50:12 +0200 Subject: [PATCH] Expose first-party template launch consumers --- .../HyperTwistTrainingPanelWidget.cpp | 51 +++++++++ .../HyperTwistTrainingSessionActor.cpp | 50 ++++++++ .../HyperTwistTrainingPanelWidget.h | 19 +++ .../HyperTwistTrainingSessionActor.h | 19 +++ ...PLATE_CONSUMER_PARITY_PACKET_2026-05-06.md | 108 ++++++++++++++++++ 5 files changed, 247 insertions(+) create mode 100644 docs/arch/HYPERTWIST_PHASE4_TEMPLATE_CONSUMER_PARITY_PACKET_2026-05-06.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp index 5216932..d9b85df 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -53,6 +53,21 @@ void UHyperTwistTrainingPanelWidget::RefreshTrainingState() UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingMethodDrillFollowUpTemplate(this); CachedMethodDrillFollowUpDeck = UHyperTwistTrainingRuntimeLibrary::BuildActiveMethodDrillFollowUpDeck(this); + CachedActiveTrainingSessionTemplates = + UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingSessionTemplates(this); + if (DefaultUserId.IsEmpty()) + { + CachedDefaultUserTrainingSessionTemplates.Reset(); + } + else + { + CachedDefaultUserTrainingSessionTemplates = + UHyperTwistTrainingRuntimeLibrary::GetTrainingSessionTemplatesForUser( + this, + DefaultUserId, + FString() + ); + } CachedCoachSignals = UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachSignals(this); CachedCoachBrief = UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachBrief(this); CachedCoachFollowUpBrief = UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachFollowUpBrief(this); @@ -71,6 +86,25 @@ void UHyperTwistTrainingPanelWidget::RefreshTrainingState() UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingRepositoryRoundTripVerification(this); } +TArray UHyperTwistTrainingPanelWidget::GetTrainingSessionTemplatesForUser( + const FString& UserId, + const FString& ReferenceUtc +) +{ + const TArray SessionTemplates = + UHyperTwistTrainingRuntimeLibrary::GetTrainingSessionTemplatesForUser( + this, + UserId, + ReferenceUtc + ); + if (UserId == DefaultUserId) + { + CachedDefaultUserTrainingSessionTemplates = SessionTemplates; + } + + return SessionTemplates; +} + FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartSampleClassicTrainingRun() { LastStepResult = FHyperTwistTrainingRunStepResult(); @@ -103,6 +137,23 @@ FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartTrainingRunFrom return CachedRunState; } +FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartTrainingRunFromTemplate( + const FString& TemplateId, + const FString& UserId, + const FString& SessionId +) +{ + LastStepResult = FHyperTwistTrainingRunStepResult(); + CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartTrainingRunFromTemplate( + this, + TemplateId, + UserId, + SessionId + ); + RefreshTrainingState(); + return CachedRunState; +} + FHyperTwistTrainingRunStepResult UHyperTwistTrainingPanelWidget::SubmitTrainingAttempt( const FHyperTwistTrainingAttempt& Attempt, int32 TimeMs diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp index b0c3d10..7093cf3 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp @@ -145,6 +145,21 @@ void AHyperTwistTrainingSessionActor::RefreshCachedTrainingState() this, CachedLatestGeneratedModeLaunchRequestForActiveDeck ); + CachedActiveTrainingSessionTemplates = + UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingSessionTemplates(this); + if (AutoStartUserId.IsEmpty()) + { + CachedAutoStartUserTrainingSessionTemplates.Reset(); + } + else + { + CachedAutoStartUserTrainingSessionTemplates = + UHyperTwistTrainingRuntimeLibrary::GetTrainingSessionTemplatesForUser( + this, + AutoStartUserId, + FString() + ); + } CachedCoachSignals = UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachSignals(this); CachedCoachBrief = UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachBrief(this); CachedCoachFollowUpBrief = UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingCoachFollowUpBrief(this); @@ -163,6 +178,25 @@ void AHyperTwistTrainingSessionActor::RefreshCachedTrainingState() UHyperTwistTrainingRuntimeLibrary::GetActiveTrainingRepositoryRoundTripVerification(this); } +TArray AHyperTwistTrainingSessionActor::GetTrainingSessionTemplatesForUser( + const FString& UserId, + const FString& ReferenceUtc +) +{ + const TArray SessionTemplates = + UHyperTwistTrainingRuntimeLibrary::GetTrainingSessionTemplatesForUser( + this, + UserId, + ReferenceUtc + ); + if (UserId == AutoStartUserId) + { + CachedAutoStartUserTrainingSessionTemplates = SessionTemplates; + } + + return SessionTemplates; +} + FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::ApplyActiveImportedRuntimeSelectorOption( const FString& SelectorId, const FString& OptionId @@ -246,6 +280,22 @@ FHyperTwistTrainingDeck AHyperTwistTrainingSessionActor::BuildCoachFollowUpDeck( return CachedCoachFollowUpDeck; } +FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartRunFromTemplate( + const FString& TemplateId, + const FString& UserId, + const FString& SessionId +) +{ + CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartTrainingRunFromTemplate( + this, + TemplateId, + UserId, + SessionId + ); + RefreshCachedTrainingState(); + return CachedRunState; +} + FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun( const int32 MaxCases ) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h index d25b9b3..c91c7c2 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h @@ -50,6 +50,12 @@ public: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Drill") FHyperTwistTrainingDeck CachedMethodDrillFollowUpDeck; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Templates") + TArray CachedActiveTrainingSessionTemplates; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Templates") + TArray CachedDefaultUserTrainingSessionTemplates; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training") FHyperTwistTrainingRunStepResult LastStepResult; @@ -116,6 +122,12 @@ public: UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training") void RefreshTrainingState(); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Templates") + TArray GetTrainingSessionTemplatesForUser( + const FString& UserId, + const FString& ReferenceUtc + ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training") FHyperTwistTrainingRunState StartSampleClassicTrainingRun(); @@ -127,6 +139,13 @@ public: EHyperTwistTrainingDeliveryMode Mode ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Templates") + FHyperTwistTrainingRunState StartTrainingRunFromTemplate( + const FString& TemplateId, + const FString& UserId, + const FString& SessionId + ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training") FHyperTwistTrainingRunStepResult SubmitTrainingAttempt( const FHyperTwistTrainingAttempt& Attempt, diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h index 55a3486..59b4ab1 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h @@ -76,6 +76,12 @@ public: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Coach") FHyperTwistTrainingDeck CachedCoachFollowUpDeck; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Templates") + TArray CachedActiveTrainingSessionTemplates; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Templates") + TArray CachedAutoStartUserTrainingSessionTemplates; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Training|Imported") FHyperTwistTrainingImportedRuntimeSelectionState CachedImportedRuntimeSelectionState; @@ -138,6 +144,12 @@ public: UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training") void RefreshCachedTrainingState(); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Templates") + TArray GetTrainingSessionTemplatesForUser( + const FString& UserId, + const FString& ReferenceUtc + ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Imported") FHyperTwistTrainingRunState ApplyActiveImportedRuntimeSelectorOption( const FString& SelectorId, @@ -168,6 +180,13 @@ public: UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach") FHyperTwistTrainingDeck BuildCoachFollowUpDeck(int32 MaxCases); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Templates") + FHyperTwistTrainingRunState StartRunFromTemplate( + const FString& TemplateId, + const FString& UserId, + const FString& SessionId + ); + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach") FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases); diff --git a/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_CONSUMER_PARITY_PACKET_2026-05-06.md b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_CONSUMER_PARITY_PACKET_2026-05-06.md new file mode 100644 index 0000000..37a5b17 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_TEMPLATE_CONSUMER_PARITY_PACKET_2026-05-06.md @@ -0,0 +1,108 @@ +# HyperTwist Phase 4 template consumer parity packet + +Created on `2026-05-06` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` template consumer parity slice + +## Purpose + +This packet exposes session-template launch and query access through first-party panel and actor consumers. + +The open tasks are: + +- surface active session templates in built-in Blueprint-facing consumers +- surface user-scoped session-template queries without dropping to the raw runtime library +- surface template-backed training launches through the same first-party consumer layer + +It is not: + +- a dashboard UI redesign packet +- a new template derivation packet +- a review-plan subsystem packet +- a coach orchestration packet + +## Scope + +Bounded lane: + +- keep runtime-library and subsystem template logic unchanged +- cache active session templates on `UHyperTwistTrainingPanelWidget` +- cache active session templates on `AHyperTwistTrainingSessionActor` +- cache the current default / auto-start user template set on those first-party consumers +- add panel and actor wrappers for `GetTrainingSessionTemplatesForUser(...)` +- add panel and actor wrappers for `StartTrainingRunFromTemplate(...)` + +Out of scope: + +- adding new UMG controls or dashboard buttons +- changing review-template resume semantics +- widening into template editing or repository mutation flows +- changing coach or method-drill recommendation logic + +## Why this was the right next packet + +The last two packets fixed review-template behavior in the subsystem: + +- generic review-preferring templates now launch with review-plan semantics +- method-drill follow-up templates that prefer review execution now persist review-plan state too + +But first-party consumer parity still lagged behind: + +- the runtime library already exposed active template lists, user-scoped template queries, and template-backed launches +- while the built-in panel and session actor still only surfaced deck-backed and coach/drill launch paths + +That left the corrected review-template lane reachable only through the raw runtime library or custom caller code. + +So the next honest move was: + +- lift session-template state into the default panel and actor caches +- add matching query and launch wrappers there +- keep the packet narrow by avoiding UI redesign work + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h` + - added cached active/default-user session-template arrays + - added first-party template query and launch wrappers +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp` + - refresh now caches active session templates and default-user templates + - added `GetTrainingSessionTemplatesForUser(...)` + - added `StartTrainingRunFromTemplate(...)` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h` + - added cached active/auto-start-user session-template arrays + - added first-party template query and launch wrappers +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp` + - refresh now caches active session templates and auto-start-user templates + - added `GetTrainingSessionTemplatesForUser(...)` + - added `StartRunFromTemplate(...)` + +## Product effect + +The corrected template lane is now reachable through the maintained first-party consumer layer: + +- panel and dashboard consumers can inspect active session templates after refresh +- panel and dashboard consumers can inspect the default user’s effective session templates without raw runtime calls +- actor consumers can do the same for their configured auto-start user +- template-backed launches, including review-preferring templates, now have built-in panel/actor entry points + +## Acceptance criteria + +- panel refresh caches active session templates and default-user templates +- session actor refresh caches active session templates and auto-start-user templates +- panel exposes template query and template launch wrappers +- session actor exposes template query and template launch wrappers +- full product build succeeds + +## Validation checklist + +1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor` +2. confirm panel refresh populates active and default-user template caches +3. confirm session actor refresh populates active and auto-start-user template caches +4. confirm template-backed launch calls route through the new first-party panel/actor wrappers + +That is the packet.