From 233bc1627f320d04d11c0da5277cc3388619a681 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Wed, 6 May 2026 04:38:25 +0200 Subject: [PATCH] Surface recognition handoff state in coach dashboard --- .../HyperTwistCoachDashboardWidget.cpp | 20 ++++ .../HyperTwistTrainingSubsystem.cpp | 38 ++++++++ .../HyperTwistCoachDashboardWidget.h | 6 ++ .../HyperTwistTrainingTypes.h | 6 ++ ...ON_HANDOFF_INSPECTION_PACKET_2026-05-06.md | 96 +++++++++++++++++++ 5 files changed, 166 insertions(+) create mode 100644 docs/arch/HYPERTWIST_PHASE4_RECOGNITION_HANDOFF_INSPECTION_PACKET_2026-05-06.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 7d78c55..397be16 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -5540,6 +5540,18 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt() TEXT("CoachQueueSuppressionHistoryDetail"), 8 ); + RecognitionStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachRecognitionStatus"), + 2 + ); + RecognitionDetailTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachRecognitionDetail"), + 8 + ); VerificationStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( WidgetTree, RootLayout, @@ -15619,6 +15631,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() { VerificationStatusTextBlock->SetText(FText::FromString(CachedCoachPanelState.VerificationStatusLine)); } + if (RecognitionStatusTextBlock != nullptr) + { + RecognitionStatusTextBlock->SetText(FText::FromString(CachedCoachPanelState.RecognitionStatusLine)); + } + if (RecognitionDetailTextBlock != nullptr) + { + RecognitionDetailTextBlock->SetText(FText::FromString(CachedCoachPanelState.RecognitionDetailLine)); + } if (VerificationHeaderTextBlock != nullptr) { VerificationHeaderTextBlock->SetText(FText::FromString(TEXT("[Repository Verification]"))); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index 005d1c7..3fffe85 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -1260,6 +1260,11 @@ namespace HyperTwistTrainingSubsystemInternal } return Count; } + + FString FormatRecognitionConfidence(const float Value) + { + return Value >= 0.0f ? FString::Printf(TEXT("%.2f"), Value) : TEXT("n/a"); + } } bool UHyperTwistTrainingSubsystem::HasActiveRun() const @@ -2122,6 +2127,39 @@ FHyperTwistTrainingCoachPanelState UHyperTwistTrainingSubsystem::GetActiveCoachP *BaseQueueStatusLine, *PanelState.QueueSuppressionLine ); + const FString RecognitionProviderLabel = ActiveRecognitionSessionState.ServiceHealth.ProviderLabel.IsEmpty() + ? (ActiveRecognitionSessionState.bUsingProviderBackedClient ? TEXT("local-http-sidecar") : TEXT("mock")) + : ActiveRecognitionSessionState.ServiceHealth.ProviderLabel; + const FString RecognitionTransportState = ActiveRecognitionSessionState.ServiceHealth.bReady + ? TEXT("ready") + : (ActiveRecognitionSessionState.ServiceHealth.LastError.IsEmpty() + ? TEXT("not-ready") + : *ActiveRecognitionSessionState.ServiceHealth.LastError); + PanelState.RecognitionStatusLine = FString::Printf( + TEXT("Recognition: %s | Service: %s | Session: %s | Frames %d | Commits %d | Corrections %d | Finalizations %d"), + *RecognitionProviderLabel, + *RecognitionTransportState, + ActiveRecognitionSessionState.bSessionOpen ? TEXT("open") : TEXT("closed"), + ActiveRecognitionSessionState.SubmittedFrameCount, + ActiveRecognitionSessionState.CommittedObservationCount, + ActiveRecognitionSessionState.CorrectionEventCount, + ActiveRecognitionSessionState.FinalizedSessionCount + ); + const bool bCoachPrefersRecognitionReview = + ActiveCoachBrief.bPreferRecognitionReplayReview || ActiveCoachFollowUpBrief.bPreferRecognitionReplayReview; + const int32 ReplayCorrectionCount = HyperTwistTrainingSubsystemInternal::CountRecognitionCorrectionEvents( + ActiveRunState.ReplayPacket.Events + ); + PanelState.RecognitionDetailLine = FString::Printf( + TEXT("Replay episodes %d | Ambiguous %d | Replay corrections %d | Avg commit confidence %s | Coach recognition review %s"), + ActiveRecognitionReplaySummary.EpisodeCount, + ActiveRecognitionReplaySummary.AmbiguousEpisodeCount, + ReplayCorrectionCount, + *HyperTwistTrainingSubsystemInternal::FormatRecognitionConfidence( + ActiveRecognitionReplaySummary.AverageCommitConfidence + ), + bCoachPrefersRecognitionReview ? TEXT("preferred") : TEXT("inactive") + ); PanelState.VerificationStatusLine = ActiveRepositoryRoundTripVerification.ResolvedPath.IsEmpty() ? TEXT("Verification: not run yet") : (ActiveRepositoryRoundTripVerification.bDidRoundTripPass diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 9fb8dd1..e75b4e5 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -1142,6 +1142,12 @@ protected: UPROPERTY(Transient) TObjectPtr NextQueueSuppressionButtonLabel = nullptr; + UPROPERTY(Transient) + TObjectPtr RecognitionStatusTextBlock = nullptr; + + UPROPERTY(Transient) + TObjectPtr RecognitionDetailTextBlock = nullptr; + UPROPERTY(Transient) TObjectPtr VerificationStatusTextBlock = nullptr; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index cc3f377..5914c72 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -6801,6 +6801,12 @@ struct FHyperTwistTrainingCoachPanelState UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString VerificationStatusLine; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecognitionStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecognitionDetailLine; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") bool bHasLatestGeneratedModeLaunchRequest = false; diff --git a/docs/arch/HYPERTWIST_PHASE4_RECOGNITION_HANDOFF_INSPECTION_PACKET_2026-05-06.md b/docs/arch/HYPERTWIST_PHASE4_RECOGNITION_HANDOFF_INSPECTION_PACKET_2026-05-06.md new file mode 100644 index 0000000..523e4f2 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_RECOGNITION_HANDOFF_INSPECTION_PACKET_2026-05-06.md @@ -0,0 +1,96 @@ +# HyperTwist Phase 4 recognition handoff inspection packet + +Created on `2026-05-06` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` recognition inspection slice + +## Purpose + +This packet closes the next honest step after the recognition runtime handoff landed. + +The open task is: + +- surface live recognition provider/session/replay-handoff state through the existing coach-panel and dashboard inspection path + +It is not: + +- a large recognition UI rewrite +- another backend productionization packet +- a speech or simulation packet +- a general dashboard redesign + +## Scope + +Bounded lane: + +- extend `FHyperTwistTrainingCoachPanelState` with recognition inspection lines +- derive those lines from: + - recognition service health + - recognition session runtime state + - recognition replay summary + - coach preference state already derived from replay/coaching logic +- show those lines in the coach dashboard using the existing text-row inspection pattern + +Out of scope: + +- new recognition controls +- transport/protocol changes +- broader coach-surface layout work +- reopening the recognition runtime handoff backend + +## Why this was the right next packet + +Before this slice: + +- the owned recognition runtime handoff was live +- replay/training/coaching state could already rebuild from recognition replay mutation +- but the live product did not expose that state coherently through its main inspection surface + +So the next bounded need was: + +- make the runtime handoff visible where the current product already inspects coaching/runtime state + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h` + - added recognition inspection lines to `FHyperTwistTrainingCoachPanelState` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp` + - coach-panel state now emits: + - recognition provider / service / session / counter summary + - recognition replay / ambiguity / coach-preference handoff summary +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp` + - dashboard now renders the new recognition inspection lines with the existing text-row surface + +## Product effect + +The first-party coach dashboard can now show: + +- which recognition provider lane is active +- whether the provider currently reports ready or blocked transport state +- whether a recognition session is open +- cumulative frame / commit / correction / finalize counts +- recognition replay episode and ambiguity counts +- whether coach guidance is currently recognition-review biased + +That makes the already-landed runtime handoff inspectable without widening the backend again. + +## Acceptance criteria + +- coach-panel state exposes live recognition runtime and replay-handoff inspection lines +- the coach dashboard renders those lines +- no recognition backend contract or runtime ownership packet is reopened +- full product build succeeds + +## Validation checklist + +1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor` +2. confirm the updated panel-state struct, subsystem assembly, and dashboard widget all compile together +3. confirm the packet stays bounded to inspection/surfacing rather than changing recognition transport behavior + +That is the packet.