diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 058993f..73b0804 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -9797,9 +9797,15 @@ const FHyperTwistTrainingDeck* UHyperTwistCoachDashboardWidget::GetActiveGuidanc switch (ActiveGuidancePreviewLane) { case EHyperTwistCoachDashboardGuidancePreviewLane::Recommended: - return CachedCoachRecommendedDeck.IsStructurallyValid() ? &CachedCoachRecommendedDeck : nullptr; + return CachedCoachPanelState.bCanStartCoachRecommendedRun + && CachedCoachRecommendedDeck.IsStructurallyValid() + ? &CachedCoachRecommendedDeck + : nullptr; case EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp: - return CachedCoachFollowUpDeck.IsStructurallyValid() ? &CachedCoachFollowUpDeck : nullptr; + return CachedCoachPanelState.bCanStartCoachFollowUpRun + && CachedCoachFollowUpDeck.IsStructurallyValid() + ? &CachedCoachFollowUpDeck + : nullptr; default: return nullptr; } @@ -9810,11 +9816,16 @@ const FHyperTwistCoachBrief* UHyperTwistCoachDashboardWidget::GetActiveGuidanceP switch (ActiveGuidancePreviewLane) { case EHyperTwistCoachDashboardGuidancePreviewLane::Recommended: - return CachedCoachBrief.Headline.IsEmpty() && CachedCoachBrief.PrimaryActionLabel.IsEmpty() + return !CachedCoachPanelState.bCanStartCoachRecommendedRun + || (CachedCoachBrief.Headline.IsEmpty() + && CachedCoachBrief.PrimaryActionLabel.IsEmpty()) ? nullptr : &CachedCoachBrief; case EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp: - return CachedCoachFollowUpBrief.Headline.IsEmpty() && CachedCoachFollowUpBrief.PrimaryActionLabel.IsEmpty() + return !CachedCoachPanelState.bCanStartCoachFollowUpRun + || !CachedCoachPanelState.bHasFollowUpGuidance + || (CachedCoachFollowUpBrief.Headline.IsEmpty() + && CachedCoachFollowUpBrief.PrimaryActionLabel.IsEmpty()) ? nullptr : &CachedCoachFollowUpBrief; default: @@ -10671,8 +10682,10 @@ FHyperTwistCoachDashboardDecisionWeighting UHyperTwistCoachDashboardWidget::Buil FHyperTwistCoachDashboardGuidanceLanePreference UHyperTwistCoachDashboardWidget::BuildGuidanceLanePreference() const { FHyperTwistCoachDashboardGuidanceLanePreference Preference; - const bool bHasRecommendedDeck = CachedCoachRecommendedDeck.IsStructurallyValid(); - const bool bHasFollowUpDeck = CachedCoachFollowUpDeck.IsStructurallyValid(); + const bool bHasRecommendedDeck = CachedCoachPanelState.bCanStartCoachRecommendedRun + && CachedCoachRecommendedDeck.IsStructurallyValid(); + const bool bHasFollowUpDeck = CachedCoachPanelState.bCanStartCoachFollowUpRun + && CachedCoachFollowUpDeck.IsStructurallyValid(); if (!bHasRecommendedDeck && !bHasFollowUpDeck) { return Preference; @@ -13054,7 +13067,8 @@ void UHyperTwistCoachDashboardWidget::RecordMethodDrillRecoveryMemoryHistorySnap const FString& QueueRecoveryStatusLine, const FString& HandoffRecommendationKindLabel, const FString& HandoffRecommendationStatusLine, - const bool bUsedMethodDrillRecoveryMemoryForHandoff + const bool bUsedMethodDrillRecoveryMemoryForHandoff, + const bool bSuppressGeneratedGuidancePacketSurface ) { const bool bHasMethodDrillRecoveryHistory = @@ -13323,63 +13337,31 @@ void UHyperTwistCoachDashboardWidget::RecordMethodDrillRecoveryMemoryHistorySnap Entry.bGeneratedPacketUsedStrongRetainedMethodDrillRecoveryPosture = bStrongPacketPosture; Entry.bGeneratedPacketUsedWeakRetainedMethodDrillRecoveryPosture = bWeakPacketPosture; Entry.bGeneratedPacketUsedGenericMethodDrillRecoveryMemory = bGenericRecoveryPacket; - if (Entry.RecommendedHandoffKindLabel.Equals(TEXT("queue"), ESearchCase::IgnoreCase)) + if (!bSuppressGeneratedGuidancePacketSurface) { - if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedQueueEntry = - FindSelectedQueueEntry()) + if (Entry.RecommendedHandoffKindLabel.Equals(TEXT("queue"), ESearchCase::IgnoreCase)) { - Entry.GeneratedGuidancePacketKindLabel = TEXT("queue-entry"); - Entry.GeneratedGuidancePacketSourceLabel = SelectedQueueEntry->SourceLabel; - Entry.GeneratedGuidancePacketHeadline = SelectedQueueEntry->Headline; - Entry.GeneratedGuidancePacketActionLabel = - SelectedQueueEntry->Headline.IsEmpty() - ? SelectedQueueEntry->SourceLabel - : SelectedQueueEntry->Headline; - Entry.GeneratedGuidancePacketFocusCaseCount = SelectedQueueEntry->FocusCaseIds.Num(); - ApplyGeneratedPlanSnapshot( - bReliableAlignedTrustedTightPlanSource, - bReliableAlignedTrustedBroadPlanSource, - bReliableTrustedTightPlanSource, - bReliableTrustedBroadPlanSource, - SelectedQueueEntry->SuggestedMode, - SelectedQueueEntry->SuggestedSelectionPolicy); + if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedQueueEntry = + FindSelectedQueueEntry()) + { + Entry.GeneratedGuidancePacketKindLabel = TEXT("queue-entry"); + Entry.GeneratedGuidancePacketSourceLabel = SelectedQueueEntry->SourceLabel; + Entry.GeneratedGuidancePacketHeadline = SelectedQueueEntry->Headline; + Entry.GeneratedGuidancePacketActionLabel = + SelectedQueueEntry->Headline.IsEmpty() + ? SelectedQueueEntry->SourceLabel + : SelectedQueueEntry->Headline; + Entry.GeneratedGuidancePacketFocusCaseCount = SelectedQueueEntry->FocusCaseIds.Num(); + ApplyGeneratedPlanSnapshot( + bReliableAlignedTrustedTightPlanSource, + bReliableAlignedTrustedBroadPlanSource, + bReliableTrustedTightPlanSource, + bReliableTrustedBroadPlanSource, + SelectedQueueEntry->SuggestedMode, + SelectedQueueEntry->SuggestedSelectionPolicy); + } } - } - else if (Entry.RecommendedHandoffKindLabel.Equals(TEXT("follow-up"), ESearchCase::IgnoreCase)) - { - Entry.GeneratedGuidancePacketKindLabel = TEXT("follow-up-packet"); - Entry.GeneratedGuidancePacketSourceLabel = GuidanceSourceLabel; - Entry.GeneratedGuidancePacketHeadline = CachedCoachFollowUpBrief.Headline; - Entry.GeneratedGuidancePacketActionLabel = CachedCoachFollowUpBrief.PrimaryActionLabel; - Entry.GeneratedGuidancePacketFocusCaseCount = CachedCoachFollowUpBrief.FocusCaseIds.Num(); - ApplyGeneratedPlanSnapshot( - bReliableAlignedTrustedTightPlanSource, - bReliableAlignedTrustedBroadPlanSource, - bReliableTrustedTightPlanSource, - bReliableTrustedBroadPlanSource, - CachedCoachFollowUpBrief.SuggestedMode, - CachedCoachFollowUpBrief.SuggestedSelectionPolicy); - } - else if (Entry.RecommendedHandoffKindLabel.Equals(TEXT("recommended"), ESearchCase::IgnoreCase)) - { - Entry.GeneratedGuidancePacketKindLabel = TEXT("recommended-brief"); - Entry.GeneratedGuidancePacketSourceLabel = GuidanceSourceLabel; - Entry.GeneratedGuidancePacketHeadline = CachedCoachBrief.Headline; - Entry.GeneratedGuidancePacketActionLabel = CachedCoachBrief.PrimaryActionLabel; - Entry.GeneratedGuidancePacketFocusCaseCount = CachedCoachBrief.FocusCaseIds.Num(); - ApplyGeneratedPlanSnapshot( - bReliableAlignedTrustedTightPlanSource, - bReliableAlignedTrustedBroadPlanSource, - bReliableTrustedTightPlanSource, - bReliableTrustedBroadPlanSource, - CachedCoachBrief.SuggestedMode, - CachedCoachBrief.SuggestedSelectionPolicy); - } - if (Entry.GeneratedGuidancePacketKindLabel.IsEmpty()) - { - if (!CachedCoachFollowUpBrief.Headline.IsEmpty() - || !CachedCoachFollowUpBrief.PrimaryActionLabel.IsEmpty() - || CachedCoachFollowUpBrief.FocusCaseIds.Num() > 0) + else if (Entry.RecommendedHandoffKindLabel.Equals(TEXT("follow-up"), ESearchCase::IgnoreCase)) { Entry.GeneratedGuidancePacketKindLabel = TEXT("follow-up-packet"); Entry.GeneratedGuidancePacketSourceLabel = GuidanceSourceLabel; @@ -13394,28 +13376,7 @@ void UHyperTwistCoachDashboardWidget::RecordMethodDrillRecoveryMemoryHistorySnap CachedCoachFollowUpBrief.SuggestedMode, CachedCoachFollowUpBrief.SuggestedSelectionPolicy); } - else if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedQueueEntry = - FindSelectedQueueEntry()) - { - Entry.GeneratedGuidancePacketKindLabel = TEXT("queue-entry"); - Entry.GeneratedGuidancePacketSourceLabel = SelectedQueueEntry->SourceLabel; - Entry.GeneratedGuidancePacketHeadline = SelectedQueueEntry->Headline; - Entry.GeneratedGuidancePacketActionLabel = - SelectedQueueEntry->Headline.IsEmpty() - ? SelectedQueueEntry->SourceLabel - : SelectedQueueEntry->Headline; - Entry.GeneratedGuidancePacketFocusCaseCount = SelectedQueueEntry->FocusCaseIds.Num(); - ApplyGeneratedPlanSnapshot( - bReliableAlignedTrustedTightPlanSource, - bReliableAlignedTrustedBroadPlanSource, - bReliableTrustedTightPlanSource, - bReliableTrustedBroadPlanSource, - SelectedQueueEntry->SuggestedMode, - SelectedQueueEntry->SuggestedSelectionPolicy); - } - else if (!CachedCoachBrief.Headline.IsEmpty() - || !CachedCoachBrief.PrimaryActionLabel.IsEmpty() - || CachedCoachBrief.FocusCaseIds.Num() > 0) + else if (Entry.RecommendedHandoffKindLabel.Equals(TEXT("recommended"), ESearchCase::IgnoreCase)) { Entry.GeneratedGuidancePacketKindLabel = TEXT("recommended-brief"); Entry.GeneratedGuidancePacketSourceLabel = GuidanceSourceLabel; @@ -13430,6 +13391,62 @@ void UHyperTwistCoachDashboardWidget::RecordMethodDrillRecoveryMemoryHistorySnap CachedCoachBrief.SuggestedMode, CachedCoachBrief.SuggestedSelectionPolicy); } + if (Entry.GeneratedGuidancePacketKindLabel.IsEmpty()) + { + if (!CachedCoachFollowUpBrief.Headline.IsEmpty() + || !CachedCoachFollowUpBrief.PrimaryActionLabel.IsEmpty() + || CachedCoachFollowUpBrief.FocusCaseIds.Num() > 0) + { + Entry.GeneratedGuidancePacketKindLabel = TEXT("follow-up-packet"); + Entry.GeneratedGuidancePacketSourceLabel = GuidanceSourceLabel; + Entry.GeneratedGuidancePacketHeadline = CachedCoachFollowUpBrief.Headline; + Entry.GeneratedGuidancePacketActionLabel = CachedCoachFollowUpBrief.PrimaryActionLabel; + Entry.GeneratedGuidancePacketFocusCaseCount = CachedCoachFollowUpBrief.FocusCaseIds.Num(); + ApplyGeneratedPlanSnapshot( + bReliableAlignedTrustedTightPlanSource, + bReliableAlignedTrustedBroadPlanSource, + bReliableTrustedTightPlanSource, + bReliableTrustedBroadPlanSource, + CachedCoachFollowUpBrief.SuggestedMode, + CachedCoachFollowUpBrief.SuggestedSelectionPolicy); + } + else if (const FHyperTwistTrainingCoachSessionQueueStateEntry* SelectedQueueEntry = + FindSelectedQueueEntry()) + { + Entry.GeneratedGuidancePacketKindLabel = TEXT("queue-entry"); + Entry.GeneratedGuidancePacketSourceLabel = SelectedQueueEntry->SourceLabel; + Entry.GeneratedGuidancePacketHeadline = SelectedQueueEntry->Headline; + Entry.GeneratedGuidancePacketActionLabel = + SelectedQueueEntry->Headline.IsEmpty() + ? SelectedQueueEntry->SourceLabel + : SelectedQueueEntry->Headline; + Entry.GeneratedGuidancePacketFocusCaseCount = SelectedQueueEntry->FocusCaseIds.Num(); + ApplyGeneratedPlanSnapshot( + bReliableAlignedTrustedTightPlanSource, + bReliableAlignedTrustedBroadPlanSource, + bReliableTrustedTightPlanSource, + bReliableTrustedBroadPlanSource, + SelectedQueueEntry->SuggestedMode, + SelectedQueueEntry->SuggestedSelectionPolicy); + } + else if (!CachedCoachBrief.Headline.IsEmpty() + || !CachedCoachBrief.PrimaryActionLabel.IsEmpty() + || CachedCoachBrief.FocusCaseIds.Num() > 0) + { + Entry.GeneratedGuidancePacketKindLabel = TEXT("recommended-brief"); + Entry.GeneratedGuidancePacketSourceLabel = GuidanceSourceLabel; + Entry.GeneratedGuidancePacketHeadline = CachedCoachBrief.Headline; + Entry.GeneratedGuidancePacketActionLabel = CachedCoachBrief.PrimaryActionLabel; + Entry.GeneratedGuidancePacketFocusCaseCount = CachedCoachBrief.FocusCaseIds.Num(); + ApplyGeneratedPlanSnapshot( + bReliableAlignedTrustedTightPlanSource, + bReliableAlignedTrustedBroadPlanSource, + bReliableTrustedTightPlanSource, + bReliableTrustedBroadPlanSource, + CachedCoachBrief.SuggestedMode, + CachedCoachBrief.SuggestedSelectionPolicy); + } + } } if (!Entry.IsStructurallyValid()) { @@ -16782,14 +16799,22 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() return FString::Printf(TEXT("%s: no guidance available"), EmptyPrefix); }; - const FString RecommendedDeckPreviewLine = BuildDeckPreviewLine( - CachedCoachRecommendedDeck, - CachedCoachBrief, + FString RecommendedDeckPreviewLine = BuildDeckPreviewLine( + CachedCoachPanelState.bCanStartCoachRecommendedRun + ? CachedCoachRecommendedDeck + : FHyperTwistTrainingDeck(), + CachedCoachPanelState.bCanStartCoachRecommendedRun + ? CachedCoachBrief + : FHyperTwistCoachBrief(), TEXT("Recommended guidance") ); - const FString FollowUpDeckPreviewLine = BuildDeckPreviewLine( - CachedCoachFollowUpDeck, - CachedCoachFollowUpBrief, + FString FollowUpDeckPreviewLine = BuildDeckPreviewLine( + (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance) + ? CachedCoachFollowUpDeck + : FHyperTwistTrainingDeck(), + (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance) + ? CachedCoachFollowUpBrief + : FHyperTwistCoachBrief(), TEXT("Follow-up guidance") ); const FHyperTwistTrainingDeck* ActivePreviewDeck = GetActiveGuidancePreviewDeck(); @@ -16841,7 +16866,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() : HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel( CachedCoachPanelState.PreferredGuidanceSourceLabel); }(); - const FString GuidancePreferenceStatusLine = + FString GuidancePreferenceStatusLine = GuidanceLanePreference.IsStructurallyValid() ? FString::Printf( TEXT("Preference: next surfaced lane %s | Active preview: %s | Source: %s"), @@ -17361,6 +17386,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } if (bShouldPresentClosedLoopIdleDashboard) { + RecommendedDeckPreviewLine = + TEXT("Recommended guidance: no active recommended guidance; the coach-to-review loop is closed and idle."); + FollowUpDeckPreviewLine = + TEXT("Follow-up guidance: no active follow-up guidance; the coach-to-review loop is closed and idle."); + GuidancePreferenceStatusLine = + TEXT("Preference: the coach-to-review loop is closed and idle; no coach preview lane is active."); + GuidancePreferenceDetailLine = + TEXT("Preference detail: retained lane history remains inspectable, but no recommended or follow-up preview is currently active."); TransitionStatusLine = TEXT("Transition: the coach-to-review loop is closed and idle. No queued, follow-up, or recommended continuation remains."); ContinueRunLabel = TEXT("Loop Idle"); @@ -17441,7 +17474,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() HyperTwistCoachDashboardWidgetInternal::DescribeHandoffRecommendationKind( HandoffRecommendationKind), CoachHandoffRecommendationStatusLine, - bHandoffRecommendationUsedMethodDrillRecoveryMemory + bHandoffRecommendationUsedMethodDrillRecoveryMemory, + bShouldPresentClosedLoopIdleDashboard ); const bool bHasPrimaryQueueSuppression = CachedCoachSessionQueueSummary.bSuppressedPrimaryQueueCreation @@ -17929,7 +17963,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() : (CachedCoachPanelState.bCanStartQueuedRun ? TEXT("Launch Queue Recovery") : TEXT("Apply Recovery"))))); - const FString GuidancePreviewSelectionLine = + FString GuidancePreviewSelectionLine = SelectedGuidancePreviewCase != nullptr && ActivePreviewDeck != nullptr ? FString::Printf( TEXT("Active preview: %s | case %d/%d | deck %s"), @@ -17939,7 +17973,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() ActivePreviewDeck->Title.IsEmpty() ? *ActivePreviewDeck->DeckId : *ActivePreviewDeck->Title ) : FString::Printf(TEXT("Active preview: %s | build a coach deck preview to inspect specific cases"), *ActivePreviewLaneLabel); - const FString GuidancePreviewCaseDetailLine = + FString GuidancePreviewCaseDetailLine = SelectedGuidancePreviewCase != nullptr ? FString::Printf( TEXT("Preview case: %s | Prompt: %s | Kind: %s | Subset: %s | Target: %d ms | Scramble: %s | Canonical: %s"), @@ -17997,7 +18031,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() SignalSummaryLine = FString::Join(SignalSummaryParts, TEXT(" | ")); } } - const FString GuidanceRationaleStatusLine = + FString GuidanceRationaleStatusLine = ActivePreviewBrief != nullptr ? FString::Printf( TEXT("Rationale: %s lane | Priority: %s (%.2f) | Focus: %s | Mode: %s | Selection: %s"), @@ -18012,7 +18046,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() TEXT("Rationale: %s lane | build a recommended or follow-up preview to inspect the coach rationale"), *ActivePreviewLaneLabel ); - const FString GuidanceRationaleDetailLine = + FString GuidanceRationaleDetailLine = ActivePreviewBrief != nullptr ? FString::Printf( TEXT("Brief: %s | Action: %s | Deck: %s | Method: %s | Focus cases: %d | Review: %s | Cadence: %s | Variety: %s | Guidance source: %s | Launch bias: %s from %s | broadened/tightened history %d/%d | deferred tightened launches %d | deferred launch cases %d%s | Selected focus: %s | Matching signals: %d | Case signal: %s | Top signals: %s"), @@ -18044,6 +18078,17 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() *SignalSummaryLine ) : TEXT("Rationale detail: unavailable until a guidance preview lane is active."); + if (bShouldPresentClosedLoopIdleDashboard) + { + GuidancePreviewSelectionLine = + TEXT("Active preview: none | the coach-to-review loop is closed and idle."); + GuidancePreviewCaseDetailLine = + TEXT("Preview case: no active guidance preview remains in the closed-loop idle state."); + GuidanceRationaleStatusLine = + TEXT("Rationale: closed-loop idle state | no active guidance preview remains."); + GuidanceRationaleDetailLine = + TEXT("Rationale detail: retained coach memory and history remain available for inspection, but no live preview packet is active."); + } const FString GuidanceComparisonLaneLabel = DisplayedGuidanceComparisonLane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended ? TEXT("recommended") diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 5648d0b..2e38557 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -1663,7 +1663,8 @@ private: const FString& QueueRecoveryStatusLine, const FString& HandoffRecommendationKindLabel, const FString& HandoffRecommendationStatusLine, - bool bUsedMethodDrillRecoveryMemoryForHandoff + bool bUsedMethodDrillRecoveryMemoryForHandoff, + bool bSuppressGeneratedGuidancePacketSurface ); void RecordGuidanceOutcomeDecision( EHyperTwistCoachDashboardOutcomeAction RecommendedAction, diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index cb4bee7..de2fbc4 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -66,9 +66,9 @@ Current correction call from the source-exposed audit and current execution refr - retained benchmark-oracle and mirror-intake reservations - current code reality in which training/coaching/catalog integration is materially ahead of recognition and hypercube runtime implementation - the imported generated-mode gap is closed; request/config/selection plumbing and the bounded clean-room executor are already landed in first-party code - - the latest landed bounded `Phase 4` packet closes the retained-idle dashboard presentation gap at the end of the already-live recognition-assisted coach-to-review continuity lane - - the retained idle widget surface now collapses next-step, handoff, and retained handoff-history presentation to the truthful closed-loop idle state instead of carrying pre-collapse queue/follow-up/recommended narration forward - - the current next bounded packet is to run one final retained-surface sweep across preview/history panes and land only if stale launch labels, generated packet headlines, or handoff narration still survive after the closed-loop idle collapse + - the latest landed bounded `Phase 4` packet closes the retained-idle preview/history gap at the end of the already-live recognition-assisted coach-to-review continuity lane + - the retained idle widget surface now suppresses stale coach preview availability, preview narration, and generated-packet history restamping once the coach-to-review loop has already collapsed to truthful closed-loop idle + - the current next bounded packet is to run one final lane-completion verification across retained comparison/history surfaces and, only if anything still leaks through, land the last narrow continuity fix before marking this closure lane complete - 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 ## Current execution-reality references diff --git a/docs/arch/HYPERTWIST_PHASE4_RETAINED_IDLE_PREVIEW_HISTORY_CLOSURE_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_RETAINED_IDLE_PREVIEW_HISTORY_CLOSURE_PACKET_2026-05-07.md new file mode 100644 index 0000000..e181950 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_RETAINED_IDLE_PREVIEW_HISTORY_CLOSURE_PACKET_2026-05-07.md @@ -0,0 +1,105 @@ +# HyperTwist Phase 4 retained idle preview/history closure packet + +Created on `2026-05-07` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` coach-to-review validation slice + +## Purpose + +This packet closes the last retained preview/history seam after the retained idle next-step and handoff surface already collapsed to truthful closed-loop idle. + +The open tasks were: + +- stop stale cached recommended/follow-up coach previews from still surfacing as an active preview lane after the panel state has already collapsed to idle +- stop retained method-drill recovery memory history from restamping a fresh generated guidance packet headline/action from those stale cached briefs during the same idle refresh + +It is not: + +- a new subsystem closure packet +- a new queue recovery policy packet +- a new coach-memory scoring packet +- a broader dashboard rewrite + +## Scope + +Bounded lane: + +- make active guidance preview deck/brief selection respect current coach actionability instead of only cached deck structure +- make preferred guidance lane selection treat recommended/follow-up availability as actionability-aware +- collapse retained idle preview narration so the current preview and rationale panes explicitly report that no active guidance preview remains +- suppress generated guidance packet surface restamping in retained method-drill recovery history when the dashboard is already presenting the closed-loop idle boundary + +Out of scope: + +- changing review carryover continuity +- changing queue ordering or queue execution policy +- reopening retained idle next-step or handoff closure work +- widening into unrelated analytics or recognition packets + +## Why this was the right next packet + +Before this slice: + +- retained idle subsystem refresh already stopped rebuilding queue and follow-up actionability +- retained idle dashboard presentation already collapsed next-step and handoff narration to truthful closed-loop idle + +But one retained surface seam still remained: + +- cached recommended/follow-up decks and briefs could still keep a preview lane structurally alive even when the panel state no longer allowed those launches +- retained method-drill recovery memory history could still snapshot a fresh generated guidance packet from those cached briefs during that same idle-closed refresh + +That meant: + +- the live dashboard action surface could already be truthful and idle +- while the retained preview/history surfaces could still imply that a coach packet was actively previewable or newly generated + +So the next honest move was: + +- gate preview availability on live actionability instead of cache structure alone +- and suppress generated-packet history restamping once the retained loop is already closed and idle + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h` + - `GetActiveGuidancePreviewDeck()` and `GetActiveGuidancePreviewBrief()` now require the matching recommended/follow-up lane to still be actionable before they surface a live preview + - `BuildGuidanceLanePreference()` now treats recommended/follow-up deck availability as actionability-aware instead of only structurally cached + - `UpdateDashboardPresentation()` now collapses retained idle preview/rationale narration to explicit no-active-preview wording and avoids building preview deck summary lines from inactive cached coach guidance + - `RecordMethodDrillRecoveryMemoryHistorySnapshot(...)` now accepts an idle-suppression flag so retained closed-loop idle refresh does not stamp a fresh generated guidance packet surface from stale cached queue/recommended/follow-up briefs + +## Product effect + +The retained idle preview/history surface is now more truthful: + +- recommended/follow-up preview lanes no longer remain live just because cached decks and briefs still exist +- retained idle preview and rationale panes no longer narrate a live coach packet after the coach-to-review loop is already closed and idle +- retained method-drill recovery memory history no longer restamps a fresh generated guidance packet from stale cached guidance at that same boundary + +## Acceptance criteria + +- when the retained coach-to-review loop is closed and idle, active guidance preview selection returns no live recommended or follow-up preview +- retained idle preview/rationale panes explicitly report that no active guidance preview remains +- retained method-drill recovery memory history does not stamp a fresh generated guidance packet surface from stale cached guidance in that state +- full product build succeeds + +## Validation checklist + +1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor` +2. confirm retained idle preview selection respects current recommended/follow-up actionability instead of stale cached deck structure +3. confirm retained idle preview and rationale panes collapse to no-active-preview narration +4. confirm retained method-drill recovery memory history does not record a fresh generated guidance packet surface at the same idle boundary +5. confirm no broader queue, carryover, or analytics behavior was reopened + +## Validation result + +- full `Development Editor|Win64` build succeeded on `2026-05-07` +- the preexisting `UnrealMCP` / `PythonScriptPlugin` dependency warning remained unchanged + +## Next bounded follow-on slice + +- run one final lane-completion verification across retained comparison/history surfaces and, only if anything still leaks through, land the last narrow continuity fix before marking the recognition-assisted coach-to-review closure lane complete