From 7b6031cb5fb25aa1a3e178f200142528ba80b2f8 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 25 Jun 2026 11:59:05 +0000 Subject: [PATCH] Align HyperTwist public release guidance and native control rosters --- .../HyperTwistCoachDashboardWidget.cpp | 27 +++ .../HyperTwistTrainingPanelWidget.cpp | 142 +++++++++++- .../HyperTwistTrainingTypes.h | 12 + .../HyperTwistBrowserBridgeObjectTest.cpp | 85 +++++++ ...PERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md | 20 +- ...UT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md | 43 +++- .../HyperTwist/DEVELOPMENT.md | 74 +++++- .../HyperTwist/FEATURE_REGISTRY.md | 4 +- .../HyperTwist/ROADMAP.md | 27 ++- .../__tests__/public-marketing-pages.test.tsx | 47 ++++ website/src/pages/public-page-helpers.tsx | 215 +++++++++++++++++- website/src/pages/public-pages-commerce.tsx | 11 + website/src/pages/public-pages-features.tsx | 6 + website/src/pages/public-pages-marketing.tsx | 31 +++ 14 files changed, 727 insertions(+), 17 deletions(-) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index c9cb2d8..66ce758 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -19177,6 +19177,22 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt() 4 ) ); + ControlInputReadinessStructuredTextBlocks.Add( + HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachControlInputReadinessClassicPointers"), + 4 + ) + ); + ControlInputReadinessStructuredTextBlocks.Add( + HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachControlInputReadinessActionShortcuts"), + 4 + ) + ); ControlInputReadinessStructuredTextBlocks.Add( HyperTwistCoachDashboardWidgetInternal::AddTextRow( WidgetTree, @@ -19319,6 +19335,14 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt() 4 ) ); + ControlProfileRosterStructuredTextBlocks.Add( + HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachControlProfileRosterMoveRoster"), + 8 + ) + ); ControlProfileRosterStructuredTextBlocks.Add( HyperTwistCoachDashboardWidgetInternal::AddTextRow( WidgetTree, @@ -30447,6 +30471,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() ControlInputReadinessStructuredTextBlocks, { ControlInputSurface.ClassicCubeStatusLine, + ControlInputSurface.ClassicCubePointerLine, + ControlInputSurface.ClassicCubeActionShortcutLine, ControlInputSurface.KeyboardProfileLine, ControlInputSurface.HigherDimensionalStatusLine, ControlInputSurface.XrStatusLine, @@ -30514,6 +30540,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() ControlProfileRosterStructuredTextBlocks, { ControlProfileRosterSurface.ClassicKeyboardProfileLine, + ControlProfileRosterSurface.ClassicKeyboardMoveRosterLine, ControlProfileRosterSurface.ImmersivePresenceProfileLine, ControlProfileRosterSurface.Magic120CellProfileLine, ControlProfileRosterSurface.MagicCube5DProfileLine, diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp index 1ee682d..8124091 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -2,6 +2,7 @@ #include "HyperTwistAlgorithm/HyperTwistAlgorithmKeyboard.h" #include "HyperTwistBrowser/HyperTwistBrowserWidget.h" +#include "HyperTwistSimulation/HyperTwistClassicCubeOrbitPawn.h" #include "HyperTwistSimulation/HyperTwistClassicCubePlayerController.h" #include "HyperTwistSimulation/HyperTwistVirtual3333ProjectionPlayerController.h" #include "HyperTwistTraining/HyperTwistTrainingImmersiveEnvironmentLibrary.h" @@ -49,6 +50,125 @@ namespace HyperTwistTrainingPanelWidgetInternal return bValue ? TEXT("ready") : TEXT("not ready"); } + FString DescribeKeyForRoster(const FKey& Key) + { + return Key.IsValid() ? Key.GetDisplayName().ToString() : TEXT("n/a"); + } + + FString DescribeMoveForRoster(const FHyperTwistAlgorithmBlockMove& Move) + { + if (Move.Family.IsEmpty()) + { + return TEXT("n/a"); + } + + FString Text = Move.Family; + if (Move.Amount < 0) + { + Text += TEXT("'"); + } + else if (FMath::Abs(Move.Amount) > 1) + { + Text += FString::FromInt(FMath::Abs(Move.Amount)); + } + + return Text; + } + + FString BuildClassicKeyboardMoveRosterLine( + const FHyperTwistAlgorithmKeyboardProfile& Profile + ) + { + if (Profile.Bindings.Num() <= 0) + { + return TEXT("Classic move roster: unavailable."); + } + + TArray PairFragments; + PairFragments.Reserve((Profile.Bindings.Num() + 1) / 2); + + for (int32 BindingIndex = 0; BindingIndex < Profile.Bindings.Num(); BindingIndex += 2) + { + const FHyperTwistAlgorithmKeyboardBinding& FirstBinding = + Profile.Bindings[BindingIndex]; + if (BindingIndex + 1 < Profile.Bindings.Num()) + { + const FHyperTwistAlgorithmKeyboardBinding& SecondBinding = + Profile.Bindings[BindingIndex + 1]; + PairFragments.Add( + FString::Printf( + TEXT("%s/%s = %s/%s"), + *DescribeKeyForRoster(FirstBinding.Key), + *DescribeKeyForRoster(SecondBinding.Key), + *DescribeMoveForRoster(FirstBinding.Move), + *DescribeMoveForRoster(SecondBinding.Move) + ) + ); + continue; + } + + PairFragments.Add( + FString::Printf( + TEXT("%s = %s"), + *DescribeKeyForRoster(FirstBinding.Key), + *DescribeMoveForRoster(FirstBinding.Move) + ) + ); + } + + return FString::Printf( + TEXT("Classic move roster: %s."), + *FString::Join(PairFragments, TEXT(", ")) + ); + } + + FString BuildClassicCubePointerLine( + const AHyperTwistClassicCubePlayerController* ClassicCubeDefaults, + const AHyperTwistClassicCubeOrbitPawn* ClassicCubeOrbitDefaults + ) + { + if (ClassicCubeDefaults == nullptr && ClassicCubeOrbitDefaults == nullptr) + { + return TEXT("Classic cube pointers: unavailable."); + } + + const bool bMiddleMouseOrbitReady = + ClassicCubeOrbitDefaults != nullptr + && ClassicCubeOrbitDefaults->bUseMiddleMouseOrbit; + const bool bWheelZoomReady = + ClassicCubeOrbitDefaults != nullptr + && ClassicCubeOrbitDefaults->ZoomStep > 0.0f; + + return FString::Printf( + TEXT("Classic cube pointers: LMB clockwise ready | RMB counter-clockwise %s | touch clockwise %s | MMB drag orbit %s | wheel zoom %s | mouse cursor %s."), + ClassicCubeDefaults != nullptr && ClassicCubeDefaults->bEnableCounterClockwiseRightClick ? TEXT("ready") : TEXT("disabled"), + ClassicCubeDefaults != nullptr && ClassicCubeDefaults->bEnableTouchTurnInput ? TEXT("ready") : TEXT("disabled"), + bMiddleMouseOrbitReady ? TEXT("ready") : TEXT("disabled"), + bWheelZoomReady ? TEXT("ready") : TEXT("disabled"), + ClassicCubeDefaults != nullptr && ClassicCubeDefaults->bShowMouseCursor ? TEXT("visible") : TEXT("hidden") + ); + } + + FString BuildClassicCubeActionShortcutLine( + const AHyperTwistClassicCubePlayerController* ClassicCubeDefaults + ) + { + if (ClassicCubeDefaults == nullptr) + { + return TEXT("Classic cube action shortcuts: unavailable."); + } + + return FString::Printf( + TEXT("Classic cube action shortcuts: R scramble %s | H hint %s | Enter submit %s | F mode %s | V hold-to-talk %s | C cycle voice %s."), + ClassicCubeDefaults->bBindFreshAttemptShortcut ? TEXT("ready") : TEXT("disabled"), + ClassicCubeDefaults->bBindHintShortcut ? TEXT("ready") : TEXT("disabled"), + ClassicCubeDefaults->bBindSubmitSolveShortcut ? TEXT("ready") : TEXT("disabled"), + ClassicCubeDefaults->bBindModeToggleShortcut ? TEXT("ready") : TEXT("disabled"), + ClassicCubeDefaults->bBindVoiceHoldShortcut ? TEXT("ready") : TEXT("disabled"), + ClassicCubeDefaults->bBindVoiceCycleShortcut ? TEXT("ready") : TEXT("disabled") + ); + } + bool TryFindViewerEditorToolById( const FHyperTwistTrainingViewerReferenceBundle& Bundle, const FString& ToolId, @@ -904,6 +1024,8 @@ UHyperTwistTrainingPanelWidget::GetDisplayedControlInputReadinessInspectSurface( const AHyperTwistClassicCubePlayerController* ClassicCubeDefaults = GetDefault(); + const AHyperTwistClassicCubeOrbitPawn* ClassicCubeOrbitDefaults = + GetDefault(); Surface.bClassicCubeMouseInputReady = ClassicCubeDefaults != nullptr && ClassicCubeDefaults->bShowMouseCursor; Surface.bClassicCubeTouchInputReady = @@ -980,6 +1102,15 @@ UHyperTwistTrainingPanelWidget::GetDisplayedControlInputReadinessInspectSurface( *HyperTwistTrainingPanelWidgetInternal::DescribeBool( Surface.bClassicCubeShortcutReady) ); + Surface.ClassicCubePointerLine = + HyperTwistTrainingPanelWidgetInternal::BuildClassicCubePointerLine( + ClassicCubeDefaults, + ClassicCubeOrbitDefaults + ); + Surface.ClassicCubeActionShortcutLine = + HyperTwistTrainingPanelWidgetInternal::BuildClassicCubeActionShortcutLine( + ClassicCubeDefaults + ); Surface.KeyboardProfileLine = FString::Printf( TEXT("Keyboard profile: %s is the shipped classic mapping."), *Surface.KeyboardProfileName @@ -1031,8 +1162,10 @@ UHyperTwistTrainingPanelWidget::GetDisplayedControlInputReadinessInspectSurface( : TEXT("limited") ); Surface.DetailLine = FString::Printf( - TEXT("%s | %s | %s | %s | %s | %s"), + TEXT("%s | %s | %s | %s | %s | %s | %s | %s"), *Surface.ClassicCubeStatusLine, + *Surface.ClassicCubePointerLine, + *Surface.ClassicCubeActionShortcutLine, *Surface.KeyboardProfileLine, *Surface.HigherDimensionalStatusLine, *Surface.XrStatusLine, @@ -1419,6 +1552,10 @@ UHyperTwistTrainingPanelWidget::GetDisplayedControlProfileRosterInspectSurface() *HyperTwistTrainingPanelWidgetInternal::DescribeBool( ClassicKeyboardProfile.Policy.bSuppressRepeat) ); + Surface.ClassicKeyboardMoveRosterLine = + HyperTwistTrainingPanelWidgetInternal::BuildClassicKeyboardMoveRosterLine( + ClassicKeyboardProfile + ); Surface.ImmersivePresenceProfileLine = FString::Printf( TEXT("Immersive presence presets: %s | contract %s | intensity presets %d | reduced-distraction presets %d."), *HyperTwistTrainingPanelWidgetInternal::DescribeReadyState( @@ -1471,8 +1608,9 @@ UHyperTwistTrainingPanelWidget::GetDisplayedControlProfileRosterInspectSurface() Surface.bRepositoryBackedSelectorRecallReady ? TEXT("ready") : TEXT("unavailable") ); Surface.DetailLine = FString::Printf( - TEXT("%s | %s | %s | %s | %s | %s | %s"), + TEXT("%s | %s | %s | %s | %s | %s | %s | %s"), *Surface.ClassicKeyboardProfileLine, + *Surface.ClassicKeyboardMoveRosterLine, *Surface.ImmersivePresenceProfileLine, *Surface.Magic120CellProfileLine, *Surface.MagicCube5DProfileLine, diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 21e6acf..b5d8eb2 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -9997,6 +9997,12 @@ struct FHyperTwistTrainingControlInputReadinessInspectSurface UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString ClassicCubeStatusLine; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ClassicCubePointerLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ClassicCubeActionShortcutLine; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString KeyboardProfileLine; @@ -10074,6 +10080,8 @@ struct FHyperTwistTrainingControlInputReadinessInspectSurface return !SummaryLine.IsEmpty() || !StatusLine.IsEmpty() || !ClassicCubeStatusLine.IsEmpty() + || !ClassicCubePointerLine.IsEmpty() + || !ClassicCubeActionShortcutLine.IsEmpty() || !HigherDimensionalStatusLine.IsEmpty() || !XrStatusLine.IsEmpty() || bClassicCubeMouseInputReady @@ -10229,6 +10237,9 @@ struct FHyperTwistTrainingControlProfileRosterInspectSurface UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString ClassicKeyboardProfileLine; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ClassicKeyboardMoveRosterLine; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString ImmersivePresenceProfileLine; @@ -10339,6 +10350,7 @@ struct FHyperTwistTrainingControlProfileRosterInspectSurface return !SummaryLine.IsEmpty() || !StatusLine.IsEmpty() || !ClassicKeyboardProfileLine.IsEmpty() + || !ClassicKeyboardMoveRosterLine.IsEmpty() || !ImmersivePresenceProfileLine.IsEmpty() || !Magic120CellProfileLine.IsEmpty() || !MagicCube5DProfileLine.IsEmpty() diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistBrowserBridgeObjectTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistBrowserBridgeObjectTest.cpp index 4705425..7cc5a66 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistBrowserBridgeObjectTest.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistBrowserBridgeObjectTest.cpp @@ -1108,6 +1108,21 @@ bool FHyperTwistTrainingPanelControlInputReadinessInspectSurfaceTest::RunTest( && Surface.bClassicCubeTouchInputReady && Surface.bClassicCubeShortcutReady ); + TestTrue( + TEXT("The control/input readiness surface must spell out the shipped classic cube pointer roster."), + Surface.ClassicCubePointerLine.Contains(TEXT("LMB clockwise")) + && Surface.ClassicCubePointerLine.Contains(TEXT("RMB counter-clockwise")) + && Surface.ClassicCubePointerLine.Contains(TEXT("touch clockwise")) + && Surface.ClassicCubePointerLine.Contains(TEXT("MMB drag orbit")) + && Surface.ClassicCubePointerLine.Contains(TEXT("wheel zoom")) + ); + TestTrue( + TEXT("The control/input readiness surface must spell out the shipped classic cube action shortcuts."), + Surface.ClassicCubeActionShortcutLine.Contains(TEXT("R scramble")) + && Surface.ClassicCubeActionShortcutLine.Contains(TEXT("Enter submit")) + && Surface.ClassicCubeActionShortcutLine.Contains(TEXT("V hold-to-talk")) + && Surface.ClassicCubeActionShortcutLine.Contains(TEXT("C cycle voice")) + ); TestTrue( TEXT("The control/input readiness surface must report the higher-dimensional keyboard lane and dedicated-family runtime catalogs."), Surface.bHigherDimensionalKeyboardInputReady @@ -1189,6 +1204,14 @@ bool FHyperTwistCoachDashboardControlInputReadinessInspectSurfaceTest::RunTest( TEXT("The coach dashboard control/input readiness surface must retain the keyboard profile fact in its detail line."), Surface.DetailLine.Contains(TEXT("classic-wca-keyboard/v1")) ); + TestTrue( + TEXT("The coach dashboard control/input readiness surface must retain the exact classic cube pointer and action rosters in its detail line."), + Surface.DetailLine.Contains(TEXT("LMB clockwise")) + && Surface.DetailLine.Contains(TEXT("MMB drag orbit")) + && Surface.DetailLine.Contains(TEXT("wheel zoom")) + && Surface.DetailLine.Contains(TEXT("R scramble")) + && Surface.DetailLine.Contains(TEXT("V hold-to-talk")) + ); TestTrue( TEXT("The coach dashboard control/input readiness surface must retain the unfinished XR/runtime truth in its detail line."), Surface.DetailLine.Contains(TEXT("not yet shipped")) @@ -1464,6 +1487,12 @@ bool FHyperTwistTrainingPanelControlProfileRosterInspectSurfaceTest::RunTest( && Surface.ClassicKeyboardProfileName == TEXT("classic-wca-keyboard/v1") && Surface.ClassicKeyboardBindingCount == 18 ); + TestTrue( + TEXT("The control/profile roster surface must spell out the shipped classic move roster."), + Surface.ClassicKeyboardMoveRosterLine.Contains(TEXT("I/K = R/R'")) + && Surface.ClassicKeyboardMoveRosterLine.Contains(TEXT("J/F = U/U'")) + && Surface.ClassicKeyboardMoveRosterLine.Contains(TEXT("P/Q = z/z'")) + ); TestTrue( TEXT("The control/profile roster surface must report the immersive presence preset roster."), Surface.bImmersivePresenceProfileReady @@ -1603,6 +1632,11 @@ bool FHyperTwistCoachDashboardControlProfileRosterInspectSurfaceTest::RunTest( TEXT("The coach dashboard control/profile roster surface must retain the classic keyboard profile fact in its detail line."), Surface.DetailLine.Contains(TEXT("classic-wca-keyboard/v1")) ); + TestTrue( + TEXT("The coach dashboard control/profile roster surface must retain the exact classic move roster in its detail line."), + Surface.DetailLine.Contains(TEXT("I/K = R/R'")) + && Surface.DetailLine.Contains(TEXT("P/Q = z/z'")) + ); TestEqual( TEXT("The coach dashboard control/profile roster surface must preserve the structured XR host decision id."), Surface.XrHostDecisionId, @@ -1677,6 +1711,16 @@ bool FHyperTwistCoachDashboardControlSurfaceStructuredTextArtifactsTest::RunTest CoachDashboard, TEXT("CoachControlInputReadinessClassicCube") ); + UTextBlock* ControlInputClassicPointersTextBlock = + HyperTwistBrowserBridgeObjectTestInternal::FindDashboardTextBlock( + CoachDashboard, + TEXT("CoachControlInputReadinessClassicPointers") + ); + UTextBlock* ControlInputActionShortcutsTextBlock = + HyperTwistBrowserBridgeObjectTestInternal::FindDashboardTextBlock( + CoachDashboard, + TEXT("CoachControlInputReadinessActionShortcuts") + ); UTextBlock* ControlInputXrTextBlock = HyperTwistBrowserBridgeObjectTestInternal::FindDashboardTextBlock( CoachDashboard, @@ -1690,6 +1734,14 @@ bool FHyperTwistCoachDashboardControlSurfaceStructuredTextArtifactsTest::RunTest TEXT("The coach dashboard must build the structured classic-cube control/input row."), ControlInputClassicCubeTextBlock ); + TestNotNull( + TEXT("The coach dashboard must build the structured classic-pointer control/input row."), + ControlInputClassicPointersTextBlock + ); + TestNotNull( + TEXT("The coach dashboard must build the structured action-shortcut control/input row."), + ControlInputActionShortcutsTextBlock + ); TestNotNull( TEXT("The coach dashboard must build the structured XR control/input row."), ControlInputXrTextBlock @@ -1710,6 +1762,22 @@ bool FHyperTwistCoachDashboardControlSurfaceStructuredTextArtifactsTest::RunTest ControlInputSurface.ClassicCubeStatusLine ); } + if (ControlInputClassicPointersTextBlock != nullptr) + { + TestEqual( + TEXT("The structured classic-pointer control/input row must mirror the inspect surface."), + ControlInputClassicPointersTextBlock->GetText().ToString(), + ControlInputSurface.ClassicCubePointerLine + ); + } + if (ControlInputActionShortcutsTextBlock != nullptr) + { + TestEqual( + TEXT("The structured action-shortcut control/input row must mirror the inspect surface."), + ControlInputActionShortcutsTextBlock->GetText().ToString(), + ControlInputSurface.ClassicCubeActionShortcutLine + ); + } if (ControlInputXrTextBlock != nullptr) { TestEqual( @@ -1781,6 +1849,11 @@ bool FHyperTwistCoachDashboardControlSurfaceStructuredTextArtifactsTest::RunTest CoachDashboard, TEXT("CoachControlProfileRosterClassicKeyboard") ); + UTextBlock* ControlProfileMoveRosterTextBlock = + HyperTwistBrowserBridgeObjectTestInternal::FindDashboardTextBlock( + CoachDashboard, + TEXT("CoachControlProfileRosterMoveRoster") + ); UTextBlock* ControlProfilePreferencesGapTextBlock = HyperTwistBrowserBridgeObjectTestInternal::FindDashboardTextBlock( CoachDashboard, @@ -1794,6 +1867,10 @@ bool FHyperTwistCoachDashboardControlSurfaceStructuredTextArtifactsTest::RunTest TEXT("The coach dashboard must build the structured classic-keyboard roster row."), ControlProfileKeyboardTextBlock ); + TestNotNull( + TEXT("The coach dashboard must build the structured classic move-roster row."), + ControlProfileMoveRosterTextBlock + ); TestNotNull( TEXT("The coach dashboard must build the structured preferences-gap roster row."), ControlProfilePreferencesGapTextBlock @@ -1814,6 +1891,14 @@ bool FHyperTwistCoachDashboardControlSurfaceStructuredTextArtifactsTest::RunTest ControlProfileSurface.ClassicKeyboardProfileLine ); } + if (ControlProfileMoveRosterTextBlock != nullptr) + { + TestEqual( + TEXT("The structured classic move-roster row must mirror the inspect surface."), + ControlProfileMoveRosterTextBlock->GetText().ToString(), + ControlProfileSurface.ClassicKeyboardMoveRosterLine + ); + } if (ControlProfilePreferencesGapTextBlock != nullptr) { TestEqual( diff --git a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md index ededa61..f149965 100644 --- a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md +++ b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md @@ -157,7 +157,8 @@ Use these as the current governing docs: - that same shipping/browser posture now also carries a native control/input readiness inspect surface through `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget`, so the operator can see the shipped - classic keyboard profile, classic-cube input readiness, + classic keyboard profile, exact classic-cube pointer, orbit, zoom, and + action-shortcut truth, higher-dimensional dedicated-family readiness, project-level `EnhancedInput`/motion-control groundwork, and explicit unfinished XR/preferences truth without opening the browser shell @@ -202,8 +203,9 @@ Use these as the current governing docs: - that same shipping/browser posture now also carries a native selectable control/profile roster inspect surface through `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget`, so - the operator can see the shipped classic keyboard profile and binding count, - scenic immersive preset roster, dedicated-family `Magic120Cell` / + the operator can see the shipped classic keyboard profile, binding count, + and exact classic move roster, scenic immersive preset roster, + dedicated-family `Magic120Cell` / `MagicCube5D` runtime/view profile ids plus selector counts, persisted generated-mode selector recall when a structurally valid launch request is present, and explicit unfinished controller rebinding truth without opening @@ -230,6 +232,18 @@ Use these as the current governing docs: covering imported-request fallback plus active-deck precedence for repository-backed selector recall in the training-panel and coach-dashboard control/profile and control/settings inspect surfaces +- Validation evidence on `2026-06-25`: a later same-family parity follow-up + then re-synced the touched type, training-panel, coach-dashboard, and + browser-test files into that same maintained isolated worktree, rebuilt the + broader slice with `Result: Succeeded` and UnrealBuildTool `Total execution + time: 5278.57 seconds`, reran the exact-source state with `Result: + Succeeded` and UnrealBuildTool `Total execution time: 70.76 seconds`, then + `Automation RunTests HyperTwist.Browser` exported + `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` + with `21` `HyperTwist.Browser.*` tests succeeded and `0` failed, explicitly + covering the literal classic-cube pointer/orbit/zoom/shortcut roster, the + exact classic move roster, and the matching coach-dashboard structured text + artifact rows - the same optional full-browser-client branch is now also tightened by a backend-contract matrix packet that fixes transport-neutral operation labels, minimum session or host or bridge identity facts, request or result correlation posture, freshness or reconnect rules, and the owned state/runtime payload anchors a later browser-client implementation must preserve - `MagicTile` `Phase 7C` is now landed separately through the bundled tiling native-behavior proof contract/probe seam, runtime-library proof helpers, and focused Windows validation on diff --git a/docs/ops/HYPERTWIST_UNREAL_INPUT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md b/docs/ops/HYPERTWIST_UNREAL_INPUT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md index 12b73d0..5d08e4c 100644 --- a/docs/ops/HYPERTWIST_UNREAL_INPUT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md +++ b/docs/ops/HYPERTWIST_UNREAL_INPUT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md @@ -69,7 +69,7 @@ truthfully claim: `DefaultInput.ini` text on each surface read - the current surface explicitly reports: - shipped `classic-wca-keyboard/v1` - - classic-cube mouse/touch/shortcut readiness + - exact classic-cube pointer, orbit, zoom, and action-shortcut truth - higher-dimensional dedicated-family runtime-catalog readiness - `EnhancedInput` plus motion-controller groundwork presence - immersive-presence contract presence @@ -104,6 +104,30 @@ truthfully claim: - `TrainingPanel.ControlInputReadinessInspectSurface` - `CoachDashboard.ControlSettingsOwnershipInspectSurface` - `TrainingPanel.ControlSettingsOwnershipInspectSurface` +- a later same-family parity follow-up on `2026-06-25` then widened the same + surface from broad readiness truth into literal shipped roster truth, + including: + - `LMB clockwise` + - `RMB counter-clockwise` + - `touch clockwise` + - `MMB drag orbit` + - `wheel zoom` + - `R scramble` + - `H hint` + - `Enter submit` + - `F mode` + - `V hold-to-talk` + - `C cycle voice` + after re-syncing the touched type, training-panel, coach-dashboard, and + browser-test files into that same maintained validation root, rebuilding + with `Result: Succeeded` and UnrealBuildTool `Total execution time: 5278.57 + seconds`, rerunning the exact-source state with `Result: Succeeded` and + UnrealBuildTool `Total execution time: 70.76 seconds`, and exporting + `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` + with all `21` `HyperTwist.Browser.*` tests green, including: + - `CoachDashboard.ControlInputReadinessInspectSurface` + - `TrainingPanel.ControlInputReadinessInspectSurface` + - `CoachDashboard.ControlSurfaceStructuredTextArtifacts` ### Partial native control/settings ownership is now real @@ -159,6 +183,7 @@ truthfully claim: - the current roster surface explicitly reports: - shipped classic keyboard profile `classic-wca-keyboard/v1` - the current `18` shipped classic keyboard bindings + - the exact shipped classic move roster - immersive-presence contract `immersive-training-presence-control-contract` - the current `3` immersive-intensity presets and `3` @@ -196,6 +221,22 @@ truthfully claim: imported-request fallback and active-deck precedence path for repository- backed selector recall inside both the training-panel and coach-dashboard control/profile and control/settings surfaces +- a later same-family parity follow-up on `2026-06-25` then widened the same + roster surface from profile-summary truth into literal shipped move-roster + truth, including pairs such as: + - `I/K = R/R'` + - `J/F = U/U'` + - `P/Q = z/z'` + after re-syncing the touched type, training-panel, coach-dashboard, and + browser-test files into that same maintained validation root, rebuilding + with `Result: Succeeded` and UnrealBuildTool `Total execution time: 5278.57 + seconds`, rerunning the exact-source state with `Result: Succeeded` and + UnrealBuildTool `Total execution time: 70.76 seconds`, and exporting + `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` + with all `21` `HyperTwist.Browser.*` tests green, including: + - `CoachDashboard.ControlProfileRosterInspectSurface` + - `TrainingPanel.ControlProfileRosterInspectSurface` + - `CoachDashboard.ControlSurfaceStructuredTextArtifacts` ### Structured XR boundary proof now exists diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/DEVELOPMENT.md b/docs/v6_5_deep_manual_pack/HyperTwist/DEVELOPMENT.md index 1b74b91..fec7a37 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/DEVELOPMENT.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/DEVELOPMENT.md @@ -618,6 +618,19 @@ Latest same-day browser/distribution continuity follow-up still on `2026-06-24`: - notices, release notes, pricing, or support when package publication or launch configuration is the limiting factor - a bounded degraded-authority recovery path when live manifest authority is unavailable +- the adjacent `2026-06-25` public/manual continuity packet now mirrors that + release next-step posture back onto the public resources, docs, and support + routes through one shared public decision surface: + - protected desktop-download next move + - pricing or provisioning next move + - protected browser/account continuity next move + - public/protected notices and source-follow-through next move + so the public manual no longer explains the browser-versus-desktop split + abstractly while leaving the concrete next release move implicit +- a same-family continuation later that day then widened the same shared public + decision surface onto the homepage, about, and changelog routes as well, so + the broader operator-facing public website now carries one consistent next + move across posture, narrative, docs, support, and release-note surfaces - the same bounded follow-up then reran the HyperTwist-owned refactor tools on the current public-route continuity state: - `scripts/run-hypertwist-sentrux-source-only.sh` @@ -864,6 +877,20 @@ Current audit note: - that shared bundle keeps public docs, release notes, corresponding source, public repo/notices reference, and support contact visible as one coherent launch surface instead of fragmenting release follow-through by page +- those same operator-facing public routes now also carry the shared + release-decision guide across: + - features + - homepage + - about + - resources + - docs + - support + - pricing + - download + - changelog + so the public lane now says explicitly when the next honest move is + protected desktop access, pricing/provisioning, protected browser/account + continuity, or notices/source follow-through - `PricingPage` and `DownloadPage` now also consume the shared `usePublicReleaseManifestView()` helper rather than duplicating public release-manifest resolution logic locally @@ -873,7 +900,7 @@ Current audit note: - `12` tests passed - the owned umbrella gate stayed green again under: - `scripts/run-hypertwist-web-surface-validation.sh` - - focused website route/auth/release validation: `12` files, `64` tests + - focused website route/auth/release validation: `12` files, `66` tests passed - `npm --prefix website run build` - `npm --prefix website/server run type-check` @@ -888,9 +915,50 @@ Current audit note: - the HyperTwist-owned structural loop also stayed healthy after that same packet: - `scripts/run-hypertwist-sentrux-source-only.sh` - - `Quality: 6210` + - `Quality: 6212` - all `7` rules pass - `scripts/run-hypertwist-gitnexus-analyze.sh` - - `16,253` nodes, `38,116` edges, `665` clusters, `300` flows + - `16,293` nodes, `38,306` edges, `676` clusters, `300` flows - `scripts/run-hypertwist-gitnexus-status.sh` - bounded mirror `Status: up-to-date` + +## Latest native/public control-roster parity follow-up (`2026-06-25`) + +- the same-family native/operator continuity lane then aligned the shipped + native control surfaces to the literal public manual roster instead of + leaving orbit, zoom, shortcut, and move-roster details implied: + - `FHyperTwistTrainingControlInputReadinessInspectSurface` now carries exact + classic-cube pointer and action-shortcut lines + - `FHyperTwistTrainingControlProfileRosterInspectSurface` now carries the + exact shipped classic keyboard move roster line + - `UHyperTwistCoachDashboardWidget` now exposes dedicated structured rows + for classic pointers, action shortcuts, and classic move roster instead of + collapsing all of that truth into broader summary prose +- the current native/public parity packet now renders the literal shipped + roster across training-panel, coach-dashboard, and public manual truth: + - `LMB clockwise` + - `RMB counter-clockwise` + - `touch clockwise` + - `MMB drag orbit` + - `wheel zoom` + - `R scramble` + - `H hint` + - `Enter submit` + - `F mode` + - `V hold-to-talk` + - `C cycle voice` + - classic move pairs including `I/K = R/R'`, `J/F = U/U'`, and `P/Q = z/z'` +- the maintained Windows validation lane then proved the exact-source state in + two steps on `C:\HyperTwist_worktrees\phase10validate`: + - base rebuild: `Result: Succeeded`, UnrealBuildTool `Total execution time: + 5278.57 seconds` + - exact-source rerun after the last synced refinements: `Result: + Succeeded`, UnrealBuildTool `Total execution time: 70.76 seconds` +- focused browser automation then exported + `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` + with all `21` `HyperTwist.Browser.*` tests green, including: + - `CoachDashboard.ControlInputReadinessInspectSurface` + - `TrainingPanel.ControlInputReadinessInspectSurface` + - `CoachDashboard.ControlProfileRosterInspectSurface` + - `TrainingPanel.ControlProfileRosterInspectSurface` + - `CoachDashboard.ControlSurfaceStructuredTextArtifacts` diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md index 78d8452..5181ada 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md @@ -145,9 +145,9 @@ Do not collapse those three tiers into one undifferentiated "features" voice. | Analytics/reporting surfaces | Implemented now | landed analytics/reporting packets | Reporting is real, but bounded to accepted retained slices. | | Rewritten training analytics and report reference grounding | Implemented now | `apache/echarts` retained permissive lane + first-party current code | Current live `Training Analytics` reference side includes four rewritten first-party targets grounded in retained `apache/echarts`: session outcome and progress reporting, analytics data-view and export, timing-trend history and overview interaction, and the optional richer explainer or sidecar boundary. This does not displace the landed `Phase 3R-C` first-party analytics/reporting owner or elevate `ecomfe/echarts-gl` and `ecomfe/zrender` beyond support-only sidecars. | | Browser/spatial/media adjunct surfaces | Implemented now | landed `three.js`, `react-three-fiber`, `xr`, `model-viewer`, `remotion` packets | These are implemented bounded families, and the current Phase 1 browser-runtime landing now includes a first-party authoritative `Content/Browser/index.html` shell, bundled-runtime upgrade path, committed clean-checkout plain-JS fallback runtime, embedded `UHyperTwistBrowserWidget` bridge, the `2026-06-19` runtime-ready queue hardening that retains outbound Unreal shell traffic until the browser runtime is ready, a same-day first-party `Browser Runtime Status` surface with shared boot-state ownership across bundled and fallback shells, a follow-on typed Unreal-side `browser-runtime-status` capture seam that retains the last valid runtime snapshot across unrelated later envelope traffic until explicit reset, a second follow-on typed Unreal-side `hypertwist-runtime-ready` capture seam that raises the original handshake payload out of raw JSON-only handling without changing queue flush behavior, a third same-day native/operator-facing status surface that consumes those typed seams inside Unreal while clearing retained runtime ownership on shell-authority change, a fourth same-day native training/operator diagnostics-panel continuation that wires that typed status ownership into `UHyperTwistTrainingPanelWidget` and the coach dashboard without making the dashboard inspect seam depend on stale rendered strings, a fifth same-day diagnostics-fidelity continuation that now carries bootstrap/runtime-ready timestamps, last command/shell-state receipt timestamps, and fallback reason through the same native operator/training seams, and a later `2026-06-23` control/input readiness continuation that projects shipped keyboard/input truth and unfinished XR/preferences truth through the same native training/operator surfaces. This still is not proof of unlimited browser-shell parity. | -| Native control/input readiness inspect surface | Implemented now | landed first-party `2026-06-23` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlInputReadinessInspectSurface`, including the shipped `classic-wca-keyboard/v1` mapping, classic-cube mouse/touch/shortcut readiness, higher-dimensional dedicated-family runtime-catalog truth, project-level `EnhancedInput` plus motion-controller groundwork facts, immersive-presence contract presence, and explicit unfinished XR/runtime plus preferences truth. The rendered detail line now also carries the fixed desktop-hosted `No-Go` decision on native OpenXR/controller widening instead of leaving that truth stranded in a non-rendered follow-up field. A later same-day structured-boundary hardening follow-up then promoted that same XR/controller boundary into stable inspect-surface fields as well, including decision id `desktop-hosted-openxr-controller-widening-no-go` plus explicit reopen requirements for dedicated runtime owners, user-facing settings or rebinding ownership, and Windows packaged controller validation, so native operator automation no longer depends only on rendered prose to verify the boundary. The recovered primary reverse-SSH `localhost:22022` lane rebuilt maintained validation worktree `C:\HyperTwist_worktrees\phase10validate` on `2026-06-23` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 1563.53 seconds`, and exported `Saved\AutomationReports\Browser-ControlInputReadiness-Verify\index.json` with all `16` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlInputReadinessInspectSurface` and `TrainingPanel.ControlInputReadinessInspectSurface`. A later same-day quality follow-up then moved the project-input-groundwork inspection behind that surface from raw text scanning into structured Unreal config reads, rebuilt the same maintained validation worktree again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 158.13 seconds`, and re-exported focused browser proof to `Saved\AutomationReports\Browser-ControlInputReadiness-StructuredConfig-Verify` with all `16` `HyperTwist.Browser.*` tests green again. The latest same-family truth-render follow-up on `2026-06-24` then rebuilt that same maintained validation root with `Result: Succeeded`, UnrealBuildTool `Total execution time: 172.52 seconds`, and exported `Saved\AutomationReports\Browser-XrNoGoRendered-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, explicitly covering both training-panel and coach-dashboard control-input detail-line visibility for the `No-Go` host decision. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control-input inspect surfaces alongside the sibling control/settings and control/profile seams. | +| Native control/input readiness inspect surface | Implemented now | landed first-party `2026-06-23` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlInputReadinessInspectSurface`, including the shipped `classic-wca-keyboard/v1` mapping, exact classic-cube pointer, orbit, zoom, and action-shortcut truth, higher-dimensional dedicated-family runtime-catalog truth, project-level `EnhancedInput` plus motion-controller groundwork facts, immersive-presence contract presence, and explicit unfinished XR/runtime plus preferences truth. The rendered detail line now also carries the fixed desktop-hosted `No-Go` decision on native OpenXR/controller widening instead of leaving that truth stranded in a non-rendered follow-up field. A later same-day structured-boundary hardening follow-up then promoted that same XR/controller boundary into stable inspect-surface fields as well, including decision id `desktop-hosted-openxr-controller-widening-no-go` plus explicit reopen requirements for dedicated runtime owners, user-facing settings or rebinding ownership, and Windows packaged controller validation, so native operator automation no longer depends only on rendered prose to verify the boundary. The recovered primary reverse-SSH `localhost:22022` lane rebuilt maintained validation worktree `C:\HyperTwist_worktrees\phase10validate` on `2026-06-23` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 1563.53 seconds`, and exported `Saved\AutomationReports\Browser-ControlInputReadiness-Verify\index.json` with all `16` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlInputReadinessInspectSurface` and `TrainingPanel.ControlInputReadinessInspectSurface`. A later same-day quality follow-up then moved the project-input-groundwork inspection behind that surface from raw text scanning into structured Unreal config reads, rebuilt the same maintained validation worktree again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 158.13 seconds`, and re-exported focused browser proof to `Saved\AutomationReports\Browser-ControlInputReadiness-StructuredConfig-Verify` with all `16` `HyperTwist.Browser.*` tests green again. The latest same-family truth-render follow-up on `2026-06-24` then rebuilt that same maintained validation root with `Result: Succeeded`, UnrealBuildTool `Total execution time: 172.52 seconds`, and exported `Saved\AutomationReports\Browser-XrNoGoRendered-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, explicitly covering both training-panel and coach-dashboard control-input detail-line visibility for the `No-Go` host decision. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control-input inspect surfaces alongside the sibling control/settings and control/profile seams. A later same-day `2026-06-25` parity follow-up then widened the same surface from general readiness truth into literal roster truth by rendering dedicated classic-cube pointer and action-shortcut lines, rebuilding the maintained validation root with `Result: Succeeded` at `5278.57 seconds` and exact-source rerun `70.76 seconds`, and exporting `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` with all `21` `HyperTwist.Browser.*` tests green, including both training-panel and coach-dashboard control-input surfaces plus the structured dashboard artifact seam. | | Native control/settings ownership inspect surface | Implemented now | landed first-party `2026-06-24` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlSettingsOwnershipInspectSurface`, including the shipped viewer camera-settings owner `tool/camera-settings`, the immersive-presence control contract, dedicated-family `magic120cell-focus-view-profile` and `magiccube5d-projection-view-profile` view ownership, higher-dimensional selector ownership, persisted generated-mode selector recall when a structurally valid launch request is present, and explicit unfinished XR/controller rebinding truth. A later same-day structured-boundary hardening follow-up then carried the same `desktop-hosted-openxr-controller-widening-no-go` decision id plus the explicit reopen requirements for dedicated runtime owners, polished user-facing settings or rebinding ownership, and Windows packaged controller validation into this surface’s structured state as well, so control-settings diagnostics no longer have to prove the boundary only by substring matching their rendered detail line. The same primary reverse-SSH `localhost:22022` lane rebuilt maintained validation worktree `C:\HyperTwist_worktrees\phase10validate` on `2026-06-24` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 4331.11 seconds`, and then exported `Saved\AutomationReports\Browser-ControlSettingsOwnership-Verify\index.json` with all `18` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlSettingsOwnershipInspectSurface`, `TrainingPanel.ControlSettingsOwnershipInspectSurface`, and the previously landed runtime plus control/input inspect seams. A later same-day selector-recall hardening follow-up then rebuilt that same maintained validation root again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 4084.77 seconds`, and exported `Saved\AutomationReports\Browser-SelectorRecall-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests passing, keeping the current control/settings proof aligned with the latest repository-backed selector-recall behavior. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control/settings inspect surfaces alongside the sibling control/input and control/profile seams. | -| Native control/profile roster inspect surface | Implemented now | landed first-party `2026-06-24` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlProfileRosterInspectSurface`, keeping the actually shipped selectable native roster visible: classic keyboard profile `classic-wca-keyboard/v1` with `18` bindings, immersive-presence contract `immersive-training-presence-control-contract` with `3` intensity plus `3` reduced-distraction presets, dedicated-family `Magic120Cell` runtime/view ids `magic120cell-runtime-profile` and `magic120cell-focus-view-profile`, dedicated-family `MagicCube5D` runtime/view ids `magiccube5d-runtime-profile` and `magiccube5d-projection-view-profile`, the current `4` selectors in each dedicated-family roster, persisted generated-mode selector recall when a structurally valid launch request is present, and explicit unfinished controller rebinding truth. The rendered roster boundary now also states the fixed desktop-hosted `No-Go` decision on native OpenXR/controller widening instead of sounding like a merely pending generic settings packet. A later same-day structured-boundary hardening follow-up then carried the same `desktop-hosted-openxr-controller-widening-no-go` decision id plus its explicit reopen requirements into the roster surface’s structured state too, so native operator automation can prove the shipped roster boundary without depending only on rendered text. After hash-syncing the touched type/training/dashboard/test files into maintained validation worktree `C:\HyperTwist_worktrees\phase10validate`, the same primary reverse-SSH `localhost:22022` lane rebuilt the widened slice on `2026-06-24` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 3606.23 seconds`, then exported `Saved\AutomationReports\Browser-ControlProfileRoster-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlProfileRosterInspectSurface` and `TrainingPanel.ControlProfileRosterInspectSurface`. A later same-day selector-recall hardening follow-up then rebuilt that same maintained validation root again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 4084.77 seconds`, and exported `Saved\AutomationReports\Browser-SelectorRecall-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests passing again, now explicitly covering imported-request fallback plus active-deck precedence for repository-backed selector recall inside the training-panel and coach-dashboard roster surface. The latest same-family truth-render follow-up on `2026-06-24` then rebuilt that same maintained validation root with `Result: Succeeded`, UnrealBuildTool `Total execution time: 172.52 seconds`, and exported `Saved\AutomationReports\Browser-XrNoGoRendered-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, explicitly covering the roster detail-line visibility of the same fixed `No-Go` boundary. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control/profile inspect surfaces alongside the sibling control/input and control/settings seams. | +| Native control/profile roster inspect surface | Implemented now | landed first-party `2026-06-24` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlProfileRosterInspectSurface`, keeping the actually shipped selectable native roster visible: classic keyboard profile `classic-wca-keyboard/v1` with `18` bindings plus the exact classic move roster, immersive-presence contract `immersive-training-presence-control-contract` with `3` intensity plus `3` reduced-distraction presets, dedicated-family `Magic120Cell` runtime/view ids `magic120cell-runtime-profile` and `magic120cell-focus-view-profile`, dedicated-family `MagicCube5D` runtime/view ids `magiccube5d-runtime-profile` and `magiccube5d-projection-view-profile`, the current `4` selectors in each dedicated-family roster, persisted generated-mode selector recall when a structurally valid launch request is present, and explicit unfinished controller rebinding truth. The rendered roster boundary now also states the fixed desktop-hosted `No-Go` decision on native OpenXR/controller widening instead of sounding like a merely pending generic settings packet. A later same-day structured-boundary hardening follow-up then carried the same `desktop-hosted-openxr-controller-widening-no-go` decision id plus its explicit reopen requirements into the roster surface’s structured state too, so native operator automation can prove the shipped roster boundary without depending only on rendered text. After hash-syncing the touched type/training/dashboard/test files into maintained validation worktree `C:\HyperTwist_worktrees\phase10validate`, the same primary reverse-SSH `localhost:22022` lane rebuilt the widened slice on `2026-06-24` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 3606.23 seconds`, then exported `Saved\AutomationReports\Browser-ControlProfileRoster-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlProfileRosterInspectSurface` and `TrainingPanel.ControlProfileRosterInspectSurface`. A later same-day selector-recall hardening follow-up then rebuilt that same maintained validation root again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 4084.77 seconds`, and exported `Saved\AutomationReports\Browser-SelectorRecall-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests passing again, now explicitly covering imported-request fallback plus active-deck precedence for repository-backed selector recall inside the training-panel and coach-dashboard roster surface. The latest same-family truth-render follow-up on `2026-06-24` then rebuilt that same maintained validation root with `Result: Succeeded`, UnrealBuildTool `Total execution time: 172.52 seconds`, and exported `Saved\AutomationReports\Browser-XrNoGoRendered-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, explicitly covering the roster detail-line visibility of the same fixed `No-Go` boundary. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control/profile inspect surfaces alongside the sibling control/input and control/settings seams. A later same-day `2026-06-25` parity follow-up then added the literal shipped classic move-roster line to that surface, mirrored it into dedicated coach-dashboard structured rows, rebuilt the maintained validation root with `Result: Succeeded` at `5278.57 seconds` and exact-source rerun `70.76 seconds`, and exported `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` with all `21` `HyperTwist.Browser.*` tests green, including both training-panel and coach-dashboard control/profile surfaces plus the structured dashboard artifact seam. | | Native XR project-plugin posture inspect truth | Implemented now | landed first-party `2026-06-25` browser/native operator continuation | All three native control/input, control/settings, and control/profile inspect surfaces now expose the current project `OpenXR` plugin enabled/disabled fact in both structured state and rendered operator lines, so project-level motion-controller groundwork no longer masquerades as an enabled `OpenXR` shipping posture. The same primary reverse-SSH `localhost:22022` lane rebuilt the exact-source state on maintained validation root `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 99.38 seconds`, then exported `Saved\AutomationReports\Browser-XrPluginProjectPosture-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green across the training-panel and coach-dashboard input/settings/profile seams. | | Optional full-browser client path above Unreal backend authority | Deep-source grounded retained | landed first-party `Phase 8B` preparation packet + spec/evidence continuation + backend-contract continuation | This branch remains a spec-only first-party option: a browser-owned rendering and input shell may sit above explicit Unreal backend seams while Unreal remains authoritative for puzzle, training, timer, replay, and persistence state. The current continuation now fixes the allowed seam families to catalog, session, action, state, continuity push, replay, persistence status, and runtime health, then further fixes transport-neutral operation labels, minimum identity or freshness requirements, and the owned state/runtime payload anchors that any later browser-client backend implementation must preserve. The landed embedded browser/CEF shell remains the current shipping posture, and this branch stays separate from any native `MagicTile` behavior or renderer widening. | | Rewritten browser spatial scene and renderer reference grounding | Implemented now | `mrdoob/three.js` retained permissive lane + first-party current code | Current live `Browser 3D and XR Support` reference side includes one rewritten first-party target grounded in retained `mrdoob/three.js`: the browser spatial scene and renderer contract. This does not displace the landed `Phase 3R-F` first-party browser spatial owner trio or absorb the adjacent `react-three-fiber` renderer-bridge and `xr` session slices. | diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md index f62d33e..b32052f 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -199,9 +199,10 @@ Current consolidated milestone snapshot: continuation on `2026-06-23`, adding `FHyperTwistTrainingControlInputReadinessInspectSurface` plus native training-panel and coach-dashboard ownership for the shipped classic keyboard - profile, classic-cube mouse/touch/shortcut readiness, higher-dimensional - dedicated-family readiness, project-level `EnhancedInput` and motion-control - groundwork, and explicit unfinished XR/preferences truth; the recovered + profile, exact classic-cube pointer, orbit, zoom, and action-shortcut + truth, higher-dimensional dedicated-family readiness, project-level + `EnhancedInput` and motion-control groundwork, and explicit unfinished + XR/preferences truth; the recovered primary reverse-SSH `localhost:22022` lane rebuilt the maintained validation root `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 1563.53 seconds`, then exported @@ -211,6 +212,15 @@ Current consolidated milestone snapshot: `TrainingPanel.ControlInputReadinessInspectSurface`, `CoachDashboard.RuntimeInspectSurface`, and `TrainingPanel.RuntimeInspectSurface` +- a later same-day `2026-06-25` parity continuation then widened those native + operator surfaces from broad readiness summaries into literal shipped roster + truth by adding dedicated classic-cube pointer and action-shortcut lines, + the exact classic move-roster line, and matching coach-dashboard structured + rows; the maintained `localhost:22022` Windows lane rebuilt + `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded` at + `5278.57 seconds`, reran the exact-source state at `70.76 seconds`, and + exported `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` + with `21` `HyperTwist.Browser.*` tests succeeded and `0` failed - a same-family `2026-06-24` continuation then added `FHyperTwistTrainingControlSettingsOwnershipInspectSurface` plus native training-panel and coach-dashboard ownership for the current viewer @@ -390,7 +400,16 @@ Current consolidated milestone snapshot: into that protected release surface instead of exposing raw download URLs on the marketing page itself; production checkout URLs, broader operator/admin billing workflows, and the exact public corresponding-source URL remain - deployment/application configuration rather than hardcoded product truth + deployment/application configuration rather than hardcoded product truth, + and the `2026-06-25` public/manual continuity continuation now also gives + the public features, resources, docs, support, pricing, and download routes + one shared release-decision guide so the public lane says explicitly when + the next honest move is protected desktop access, pricing/provisioning, + protected browser/account continuity, or notices/source follow-through, + and a same-family continuation later that day widens that same decision + guide onto the homepage, about, and changelog routes so the broader + operator-facing public website now carries one consistent next move across + posture, narrative, docs, support, release, and download surfaces - classic-cube `Phase 9A` replay recording is now closed through first-party runtime capture, `.json` replay persistence, local playback reconstruction, schema-light replay normalization across save/load/viewer import, and live diff --git a/website/src/__tests__/public-marketing-pages.test.tsx b/website/src/__tests__/public-marketing-pages.test.tsx index 4f97e20..557761e 100644 --- a/website/src/__tests__/public-marketing-pages.test.tsx +++ b/website/src/__tests__/public-marketing-pages.test.tsx @@ -246,6 +246,11 @@ describe('public marketing pages', () => { expect(screen.getByText('Why HyperTwist keeps both a website and a desktop runtime')).toBeTruthy() expect(screen.getByText('Why the browser is intentionally narrower')).toBeTruthy() expect(screen.getByText('Choose the right HyperTwist surface')).toBeTruthy() + const downloadDecisionGuideHeading = screen.getByRole('heading', { name: 'Choose the next release move' }) + const downloadDecisionGuideSection = downloadDecisionGuideHeading.closest('section') + expect(downloadDecisionGuideSection).toBeTruthy() + expect(within(downloadDecisionGuideSection as HTMLElement).getByText('Need the protected desktop-download lane?')).toBeTruthy() + expect(within(downloadDecisionGuideSection as HTMLElement).getByText('Need notices, corresponding source, or release follow-through?')).toBeTruthy() expect(screen.getByText('First launch and desktop setup')).toBeTruthy() expect(screen.getByText('Digital delivery workflow')).toBeTruthy() expect(screen.getByText('Protected entitlement handoff')).toBeTruthy() @@ -419,6 +424,11 @@ describe('public marketing pages', () => { expect(screen.getAllByText(/public auth runtime posture/i).length).toBeGreaterThan(0) expect(screen.getByText('Current packaged desktop proof')).toBeTruthy() expect(screen.getByText('Packaged validation passed')).toBeTruthy() + const homeDecisionGuideHeading = screen.getByRole('heading', { name: 'Choose the next release move' }) + const homeDecisionGuideSection = homeDecisionGuideHeading.closest('section') + expect(homeDecisionGuideSection).toBeTruthy() + expect(within(homeDecisionGuideSection as HTMLElement).getByText('Need the protected desktop-download lane?')).toBeTruthy() + expect(within(homeDecisionGuideSection as HTMLElement).getByText('Need plan selection, billing, or operator provisioning?')).toBeTruthy() expect(screen.getByText('Release references and source availability')).toBeTruthy() expect(screen.getByRole('link', { name: 'https://docs.hypertwist.app' })).toBeTruthy() expect(screen.getByText('How a real first session flows')).toBeTruthy() @@ -535,6 +545,11 @@ describe('public marketing pages', () => { expect(screen.getByText('What the desktop runtime is for')).toBeTruthy() expect(screen.getByText('Choose the right HyperTwist surface')).toBeTruthy() expect(screen.getByText('Stay on the public website')).toBeTruthy() + const aboutDecisionGuideHeading = screen.getByRole('heading', { name: 'Choose the next release move' }) + const aboutDecisionGuideSection = aboutDecisionGuideHeading.closest('section') + expect(aboutDecisionGuideSection).toBeTruthy() + expect(within(aboutDecisionGuideSection as HTMLElement).getByText('Need account state, pairing, or protected browser follow-through?')).toBeTruthy() + expect(within(aboutDecisionGuideSection as HTMLElement).getByText('Need plan selection, billing, or operator provisioning?')).toBeTruthy() expect(screen.getByText('Current desktop control and XR truth')).toBeTruthy() expect(screen.getByText('Selectable control and settings roster')).toBeTruthy() expect(screen.getByText('Shipped classic control profile')).toBeTruthy() @@ -658,6 +673,11 @@ describe('public marketing pages', () => { expect(screen.getAllByText('Selectable immersive and family-specific settings').length).toBeGreaterThan(0) expect(screen.getByText('Current public rollout posture')).toBeTruthy() expect(screen.getByText('Public feature and launch posture')).toBeTruthy() + const featuresDecisionGuideHeading = screen.getByRole('heading', { name: 'Choose the next release move' }) + const featuresDecisionGuideSection = featuresDecisionGuideHeading.closest('section') + expect(featuresDecisionGuideSection).toBeTruthy() + expect(within(featuresDecisionGuideSection as HTMLElement).getByText('Need the protected desktop-download lane?')).toBeTruthy() + expect(within(featuresDecisionGuideSection as HTMLElement).getByText('Need plan selection, billing, or operator provisioning?')).toBeTruthy() expect(screen.getByText('Current packaged desktop proof')).toBeTruthy() expect(screen.getByText('Packaged validation passed')).toBeTruthy() expect(screen.getByText('Release references and source availability')).toBeTruthy() @@ -835,6 +855,11 @@ describe('public marketing pages', () => { expect(screen.getByText('Current packaged desktop proof')).toBeTruthy() expect(screen.getAllByText('What happens after access is granted').length).toBeGreaterThan(0) expect(screen.getByText('Choose the right HyperTwist surface')).toBeTruthy() + const pricingDecisionGuideHeading = screen.getByRole('heading', { name: 'Choose the next release move' }) + const pricingDecisionGuideSection = pricingDecisionGuideHeading.closest('section') + expect(pricingDecisionGuideSection).toBeTruthy() + expect(within(pricingDecisionGuideSection as HTMLElement).getByText('Need plan selection, billing, or operator provisioning?')).toBeTruthy() + expect(within(pricingDecisionGuideSection as HTMLElement).getByText('Need account state, pairing, or protected browser follow-through?')).toBeTruthy() expect(screen.getByText('Why HyperTwist keeps both a website and a desktop runtime')).toBeTruthy() expect(screen.getByText('Use the desktop runtime')).toBeTruthy() expect(screen.getByText('Current input, XR, and settings truth')).toBeTruthy() @@ -939,6 +964,15 @@ describe('public marketing pages', () => { expect(screen.getByText('Current support-facing launch posture')).toBeTruthy() expect(screen.getByText('Current packaged desktop proof')).toBeTruthy() expect(screen.getByText('Packaged validation passed')).toBeTruthy() + const decisionGuideHeading = screen.getByRole('heading', { name: 'Choose the next release move' }) + const decisionGuideSection = decisionGuideHeading.closest('section') + expect(decisionGuideSection).toBeTruthy() + expect(within(decisionGuideSection as HTMLElement).getByText('Need the protected desktop-download lane?')).toBeTruthy() + expect(within(decisionGuideSection as HTMLElement).getByText('Need plan selection, billing, or operator provisioning?')).toBeTruthy() + expect(within(decisionGuideSection as HTMLElement).getByText('Need account state, pairing, or protected browser follow-through?')).toBeTruthy() + expect(within(decisionGuideSection as HTMLElement).getByText('Need notices, corresponding source, or release follow-through?')).toBeTruthy() + expect(within(decisionGuideSection as HTMLElement).getByRole('link', { name: 'Sign in for protected browser access' }).getAttribute('href')).toBe('/login?next=%2Fapp%2Fbrowser-access') + expect(within(decisionGuideSection as HTMLElement).getByRole('link', { name: 'Review public notices' }).getAttribute('href')).toBe('/open-source-notices') expect(screen.getByText('Release references and source availability')).toBeTruthy() expect(screen.getByRole('link', { name: 'https://docs.hypertwist.app' })).toBeTruthy() expect(screen.getByText('Digital delivery workflow')).toBeTruthy() @@ -1066,6 +1100,10 @@ describe('public marketing pages', () => { expect(screen.getByText('Deployment readiness snapshot')).toBeTruthy() expect(screen.getAllByText('XR groundwork exists, but the full VR lane is not finished').length).toBeGreaterThan(0) expect(screen.getByText('Higher-dimensional runtime ownership')).toBeTruthy() + expect(screen.getByText('Choose the next release move')).toBeTruthy() + expect(screen.getByText('Need account state, pairing, or protected browser follow-through?')).toBeTruthy() + expect(screen.getByText(/The web lane is not superfluous/i)).toBeTruthy() + expect(screen.getByRole('link', { name: 'Sign in for protected dashboard' }).getAttribute('href')).toBe('/login?next=%2Fapp') expect(screen.getByText('Release references and source availability')).toBeTruthy() expect(screen.getByRole('link', { name: 'https://docs.hypertwist.app' })).toBeTruthy() }) @@ -1157,6 +1195,10 @@ describe('public marketing pages', () => { expect(screen.getByText('Browser auth degraded or mixed')).toBeTruthy() expect(screen.getByText('Release authority temporarily unavailable')).toBeTruthy() expect(screen.getByText('Current packaged desktop proof')).toBeTruthy() + expect(screen.getByText('Choose the next release move')).toBeTruthy() + expect(screen.getByText('Need the protected desktop-download lane?')).toBeTruthy() + expect(screen.getByText('Need notices, corresponding source, or release follow-through?')).toBeTruthy() + expect(screen.getByRole('link', { name: 'Review public notices' }).getAttribute('href')).toBe('/open-source-notices') expect(screen.getByText('Common operator questions')).toBeTruthy() expect(screen.getByText('If the desktop app is primary, why keep the web version?')).toBeTruthy() expect(screen.getByText(/intentionally narrower than the simulator for training and device\/runtime work/i)).toBeTruthy() @@ -1267,6 +1309,11 @@ describe('public marketing pages', () => { expect(screen.getByText('Signed-in operator shell now mirrors the native control roster')).toBeTruthy() expect(screen.getByText('Native selector-recall diagnostics hardened and revalidated')).toBeTruthy() expect(screen.getByText('Choose the right HyperTwist surface')).toBeTruthy() + const changelogDecisionGuideHeading = screen.getByRole('heading', { name: 'Choose the next release move' }) + const changelogDecisionGuideSection = changelogDecisionGuideHeading.closest('section') + expect(changelogDecisionGuideSection).toBeTruthy() + expect(within(changelogDecisionGuideSection as HTMLElement).getByText('Need notices, corresponding source, or release follow-through?')).toBeTruthy() + expect(within(changelogDecisionGuideSection as HTMLElement).getByRole('link', { name: 'Review public notices' }).getAttribute('href')).toBe('/open-source-notices') expect(screen.getByText('Release rollout checklist')).toBeTruthy() expect(screen.getByText('Read the actual lane that changed')).toBeTruthy() expect(screen.getByText(/Pair the release note with the current packaged-validation summary/i)).toBeTruthy() diff --git a/website/src/pages/public-page-helpers.tsx b/website/src/pages/public-page-helpers.tsx index 4e7fbed..0b784c7 100644 --- a/website/src/pages/public-page-helpers.tsx +++ b/website/src/pages/public-page-helpers.tsx @@ -4,8 +4,8 @@ import { useQuery } from '@tanstack/react-query' import { Link } from 'react-router-dom' import { getReleaseManifest } from '../auth/auth-api' import { ReleaseValidationSummary } from '../components/ui/ReleaseValidationSummary' -import type { ReleaseManifestPlatformView } from '../release-manifest' -import { resolveReleaseManifestView } from '../release-manifest' +import type { ReleaseManifestPlatformView, ReleaseManifestView } from '../release-manifest' +import { resolveReleaseCommerceView, resolveReleaseManifestView } from '../release-manifest' import { brandConfig, downloadTargets, @@ -20,6 +20,7 @@ import { buildLoginPath, buildProtectedDownloadPath, buildRegisterPath, + buildSupportPath, isExternalHref, } from '../site-routes' @@ -410,6 +411,216 @@ export function ReleaseAuthorityBundleSection({ ) } +type PublicReleaseDecisionAction = { + label: string + to: string +} + +type PublicReleaseDecisionCard = { + badge: string + title: string + summary: string + bullets: readonly string[] + actions: readonly PublicReleaseDecisionAction[] +} + +function describePublicReleaseTarget(platform: ReleaseManifestPlatformView) { + const parts = [platform.platform] + + if (platform.version) { + parts.push(`v${platform.version}`) + } + + if (platform.channel) { + parts.push(`(${platform.channel})`) + } + + return parts.join(' ') +} + +function buildPublicReleaseDecisionCards(releaseManifest: ReleaseManifestView): readonly PublicReleaseDecisionCard[] { + const configuredTargets = releaseManifest.platforms.filter((platform) => platform.configured) + const windowsTarget = releaseManifest.platforms.find((platform) => platform.platform_key === 'windows') ?? null + const primaryTarget = configuredTargets.find((platform) => platform.platform_key === 'windows') + ?? configuredTargets[0] + ?? windowsTarget + const releaseCommerce = resolveReleaseCommerceView(releaseManifest, releaseCommerceFallback) + const supportEmail = releaseManifest.support_email || brandConfig.contact.email + const configuredReferenceCount = [ + releaseManifest.public_docs_url, + releaseManifest.release_notes_url, + releaseManifest.corresponding_source_url, + releaseManifest.open_source_repo_url, + releaseManifest.support_email, + ].filter(Boolean).length + const checkoutConfigured = Boolean( + releaseCommerce.operator_checkout_url || releaseCommerce.studio_checkout_url, + ) + + const releaseTargetCard: PublicReleaseDecisionCard = primaryTarget?.configured + ? { + badge: 'Desktop release target published', + title: 'Need the protected desktop-download lane?', + summary: + 'The public site can confirm the current desktop release target, but actual package delivery still belongs to the signed-in protected release lane.', + bullets: [ + `Current public target: ${describePublicReleaseTarget(primaryTarget)}`, + `Configured release targets: ${configuredTargets.length}/${releaseManifest.platforms.length}`, + primaryTarget.validation_summary + ? 'Public packaged proof is already attached to this lane.' + : 'This lane is published, but public packaged proof is not attached yet.', + ], + actions: [ + { label: 'Sign in for protected downloads', to: buildLoginPath(buildProtectedDownloadPath('windows')) }, + { label: 'Create account for desktop access', to: buildRegisterPath(buildProtectedDownloadPath('windows')) }, + { label: 'Open download center', to: '/download' }, + ], + } + : { + badge: 'Package publication pending', + title: 'Need the protected desktop-download lane?', + summary: + 'The public site can still show release posture, but no published desktop target is configured yet, so the next move is protected follow-through and launch-readiness support rather than direct package delivery.', + bullets: [ + `Configured release targets: ${configuredTargets.length}/${releaseManifest.platforms.length}`, + `Primary default lane: ${primaryTarget ? primaryTarget.platform : 'Windows desktop lane pending publication'}`, + `Support contact: ${supportEmail}`, + ], + actions: [ + { label: 'Sign in for protected downloads', to: buildLoginPath(buildProtectedDownloadPath('windows')) }, + { label: 'Open download center', to: '/download' }, + { label: 'Open launch-readiness support', to: buildSupportPath('launch-readiness') }, + ], + } + + const provisioningCard: PublicReleaseDecisionCard = checkoutConfigured + ? { + badge: 'Checkout or provisioning route ready', + title: 'Need plan selection, billing, or operator provisioning?', + summary: + 'The website is not a brochure-only surface here: pricing is the public handoff into plan selection, then the protected dashboard resolves entitlement, downloads, and operator follow-through.', + bullets: [ + `Operator checkout: ${releaseCommerce.operator_checkout_url ? 'configured' : 'manual help required'}`, + `Studio checkout: ${releaseCommerce.studio_checkout_url ? 'configured' : 'manual help required'}`, + 'Keep pricing on the public site, then move into the protected dashboard for account-aware release work.', + ], + actions: [ + { label: 'Open pricing', to: '/pricing' }, + { label: 'Sign in for protected dashboard', to: buildLoginPath('/app') }, + ], + } + : { + badge: 'Manual provisioning route', + title: 'Need plan selection, billing, or operator provisioning?', + summary: + 'Pricing can still explain the real delivery model, but manual operator help remains the truthful path because live checkout targets are not configured yet.', + bullets: [ + `Operator checkout: ${releaseCommerce.operator_checkout_url ? 'configured' : 'manual help required'}`, + `Studio checkout: ${releaseCommerce.studio_checkout_url ? 'configured' : 'manual help required'}`, + `Support contact: ${supportEmail}`, + ], + actions: [ + { label: 'Open pricing', to: '/pricing' }, + { label: 'Open operator-access support', to: buildSupportPath('operator-access') }, + ], + } + + const browserContinuityCard: PublicReleaseDecisionCard = { + badge: 'Protected browser shell', + title: 'Need account state, pairing, or protected browser follow-through?', + summary: + 'The web lane is not superfluous: it owns identity, entitlement, billing posture, protected notices, and browser-to-desktop pairing while the native runtime keeps simulator authority.', + bullets: [ + 'Use the protected dashboard for account state, auth/runtime truth, and release-manifest follow-through.', + 'Use the protected browser-access lane when you need account-aware browser continuity rather than simulator execution.', + 'Move into the desktop runtime for the actual training loop, coaching, and higher-dimensional interaction.', + ], + actions: [ + { label: 'Sign in for protected dashboard', to: buildLoginPath('/app') }, + { label: 'Sign in for protected browser access', to: buildLoginPath('/app/browser-access') }, + ], + } + + const noticesCard: PublicReleaseDecisionCard = { + badge: configuredReferenceCount === 5 ? 'Release references configured' : 'Some release references are still pending', + title: 'Need notices, corresponding source, or release follow-through?', + summary: + 'Public notices and release references stay on the website first, while the protected notices lane remains the signed-in follow-through surface when account context matters.', + bullets: [ + `Configured public release references: ${configuredReferenceCount}/5`, + 'Keep notices, release notes, and corresponding-source posture visible anywhere downloadable distribution is discussed.', + `Support contact: ${supportEmail}`, + ], + actions: [ + { label: 'Review public notices', to: '/open-source-notices' }, + { label: 'Review release notes', to: '/changelog' }, + { label: 'Sign in for protected notices', to: buildLoginPath('/app/notices') }, + ], + } + + return [releaseTargetCard, provisioningCard, browserContinuityCard, noticesCard] +} + +function SurfaceActionLink({ + to, + label, + className = 'button button--ghost', +}: { + to: string + label: string + className?: string +}) { + if (isExternalHref(to)) { + return ( + + {label} + + ) + } + + return ( + + {label} + + ) +} + +export function PublicReleaseDecisionGuideSection({ + releaseManifest, + title = 'Choose the next release move', + description = 'These cards keep the public pages practical: they show whether the next honest step is pricing, protected browser/account work, protected desktop access, or notices/source follow-through.', +}: { + releaseManifest: ReleaseManifestView + title?: string + description?: string +}) { + const cards = buildPublicReleaseDecisionCards(releaseManifest) + + return ( +
+
+ {cards.map((card) => ( +
+

{card.badge}

+

{card.title}

+

{card.summary}

+
    + {card.bullets.map((bullet) => ( +
  • {bullet}
  • + ))} +
+
+ {card.actions.map((action) => ( + + ))} +
+
+ ))} +
+
+ ) +} + export function PlanActionLink({ href, label }: { href: string; label: string }) { if (isExternalHref(href)) { return ( diff --git a/website/src/pages/public-pages-commerce.tsx b/website/src/pages/public-pages-commerce.tsx index 644873d..c47e332 100644 --- a/website/src/pages/public-pages-commerce.tsx +++ b/website/src/pages/public-pages-commerce.tsx @@ -30,6 +30,7 @@ import { operatorFallbackPlan, PlanActionLink, PublicPackagedDesktopProofSection, + PublicReleaseDecisionGuideSection, ReleaseAuthorityBundleSection, releaseCommerceFallback, Section, @@ -128,6 +129,11 @@ export function PricingPage() { + +
+ +
+ +
+ + + +
+ +
+ + + + + +