diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp index 50f9941..ae7d5f0 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -335,6 +335,123 @@ namespace HyperTwistContractLibraryInternal return Profile; } + FHyperTwistVisionSolveExplanationProfile MakeDefaultClassicCubeSolveExplanationProfile( + const FString& ProfileId + ) + { + FHyperTwistVisionSolveExplanationProfile Profile; + Profile.ExplanationProfileId = ProfileId; + Profile.PuzzleId = TEXT("cube/3x3x3"); + Profile.RecommendationMode = TEXT("bounded-stage-ladder-v1"); + Profile.bSupportsStageRecommendation = true; + Profile.bSupportsStepNavigation = true; + Profile.bSupportsPlaybackStage = false; + + auto AddStep = [&Profile]( + const TCHAR* StepId, + const TCHAR* StageId, + const TCHAR* Title, + const TCHAR* Description, + const TCHAR* RecommendationLine, + const int32 StageOrdinal + ) + { + FHyperTwistVisionSolveExplanationStep Step; + Step.StepId = StepId; + Step.StageId = StageId; + Step.Title = Title; + Step.Description = Description; + Step.RecommendationLine = RecommendationLine; + Step.StageOrdinal = StageOrdinal; + Step.bPlaybackDeferred = true; + Profile.Steps.Add(Step); + }; + + AddStep( + TEXT("orient-centers-step"), + TEXT("orient-centers"), + TEXT("Orient Centers"), + TEXT("Check the cube orientation and align the center reference before turning into the solve ladder."), + TEXT("Confirm the edited net and lock the reference orientation before solve guidance begins."), + 0 + ); + AddStep( + TEXT("solve-white-cross-step"), + TEXT("solve-white-cross"), + TEXT("Build First Cross"), + TEXT("Assemble the opening cross with edge placement matched against the side colors."), + TEXT("Start with a stable cross so later first-layer turns stay bounded and readable."), + 1 + ); + AddStep( + TEXT("solve-first-layer-corners-step"), + TEXT("solve-first-layer-corners"), + TEXT("Place First-Layer Corners"), + TEXT("Insert the matching corners to finish the first layer without disturbing the cross."), + TEXT("Close the first layer before moving into the middle-band edge work."), + 2 + ); + AddStep( + TEXT("solve-second-layer-edges-step"), + TEXT("solve-second-layer-edges"), + TEXT("Solve Middle-Layer Edges"), + TEXT("Move the non-top edges into their middle-layer slots while preserving the solved first layer."), + TEXT("Complete the middle layer so orientation-only work remains on the last layer."), + 3 + ); + AddStep( + TEXT("solve-oll-edges-step"), + TEXT("solve-oll-edges"), + TEXT("Orient Last-Layer Edges"), + TEXT("Turn the last-layer edges so the top-face color is established across the edge ring."), + TEXT("Orient the last-layer edges before promoting corner orientation."), + 4 + ); + AddStep( + TEXT("solve-oll-corners-step"), + TEXT("solve-oll-corners"), + TEXT("Orient Last-Layer Corners"), + TEXT("Resolve the remaining top-face corner orientation while keeping the lower layers intact."), + TEXT("Finish top-face orientation before any final permutation pass."), + 5 + ); + AddStep( + TEXT("solve-pll-corners-step"), + TEXT("solve-pll-corners"), + TEXT("Permute Last-Layer Corners"), + TEXT("Cycle the last-layer corners into their destination positions after orientation is complete."), + TEXT("Place the last-layer corners first so the final edge permutation stays bounded."), + 6 + ); + AddStep( + TEXT("solve-pll-edges-step"), + TEXT("solve-pll-edges"), + TEXT("Permute Last-Layer Edges"), + TEXT("Finish the solve by cycling the last-layer edges into their final positions."), + TEXT("Close with the last-layer edges; playback ownership remains deferred in this packet."), + 7 + ); + + auto AddAction = [&Profile]( + const TCHAR* ActionId, + const TCHAR* InputBinding, + const TCHAR* SurfaceId + ) + { + FHyperTwistVisionShellActionBinding ActionBinding; + ActionBinding.ActionId = ActionId; + ActionBinding.InputBinding = InputBinding; + ActionBinding.SurfaceId = SurfaceId; + Profile.ActionBindings.Add(ActionBinding); + }; + + AddAction(TEXT("start-solve-explanation"), TEXT("button:start-explanation"), TEXT("cube-edit")); + AddAction(TEXT("step-solve-forward"), TEXT("button:next-step"), TEXT("solve-explanation-stage")); + AddAction(TEXT("step-solve-backward"), TEXT("button:previous-step"), TEXT("solve-explanation-stage")); + AddAction(TEXT("restart-solve-explanation"), TEXT("button:restart-explanation"), TEXT("solve-explanation-stage")); + return Profile; + } + void ResolveMockClassicFaceSwatch( const FString& FaceId, FString& OutColorId, @@ -600,6 +717,64 @@ namespace HyperTwistContractLibraryInternal return BrowserShellState; } + + FHyperTwistVisionSolveExplanationState MakeClassicCubeSolveExplanationState( + const FHyperTwistVisionSessionConfig& SessionConfig, + const FHyperTwistVisionReconstructionSession& ReconstructionSession + ) + { + FHyperTwistVisionSolveExplanationState SolveExplanationState; + const FHyperTwistVisionSolveExplanationProfile SolveExplanationProfile = + SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid() + ? SessionConfig.SolveExplanationProfileDefinition + : MakeDefaultClassicCubeSolveExplanationProfile( + !SessionConfig.SolveExplanationProfileId.IsEmpty() + ? SessionConfig.SolveExplanationProfileId + : TEXT("classic-cube-solve-explanation-v1") + ); + + SolveExplanationState.ExplanationProfileId = SolveExplanationProfile.ExplanationProfileId; + SolveExplanationState.TotalStepCount = SolveExplanationProfile.Steps.Num(); + SolveExplanationState.bPlaybackDeferred = !SolveExplanationProfile.bSupportsPlaybackStage; + SolveExplanationState.bRequiresFullReconstruction = true; + + const int32 RemainingFaceCount = ReconstructionSession.IsStructurallyValid() + ? ReconstructionSession.MissingFaces.Num() + : 6; + if (ReconstructionSession.bHasCompleteClassicCubeNet) + { + SolveExplanationState.RecommendedStageId = + SolveExplanationProfile.Steps.Num() > 0 + ? SolveExplanationProfile.Steps[0].StageId + : TEXT("orient-centers"); + SolveExplanationState.RecommendationStatusLine = + TEXT("Solve guidance is ready after cube-edit review."); + SolveExplanationState.RecommendationDetailLine = + TEXT("Review any manual corrections, then start the bounded stage ladder. Playback ownership stays deferred in this packet."); + SolveExplanationState.CurrentStepOrdinal = -1; + SolveExplanationState.bReadyForExplanation = true; + SolveExplanationState.bStartExplanationVisible = true; + SolveExplanationState.bNextStepVisible = false; + SolveExplanationState.bPreviousStepVisible = false; + } + else + { + SolveExplanationState.RecommendedStageId = TEXT("capture-remaining-faces"); + SolveExplanationState.RecommendationStatusLine = + TEXT("Solve guidance unlocks after capture completes."); + SolveExplanationState.RecommendationDetailLine = FString::Printf( + TEXT("Commit the remaining %d face(s) and finish the bounded reconstruction pass before solve guidance unlocks."), + RemainingFaceCount + ); + SolveExplanationState.CurrentStepOrdinal = -1; + SolveExplanationState.bReadyForExplanation = false; + SolveExplanationState.bStartExplanationVisible = false; + SolveExplanationState.bNextStepVisible = false; + SolveExplanationState.bPreviousStepVisible = false; + } + + return SolveExplanationState; + } } FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition() @@ -839,6 +1014,11 @@ FHyperTwistVisionSessionConfig UHyperTwistContractLibrary::MakeSampleVisionSessi HyperTwistContractLibraryInternal::MakeDefaultClassicCubeBrowserShellProfile( Config.BrowserShellProfileId ); + Config.SolveExplanationProfileId = TEXT("classic-cube-solve-explanation-v1"); + Config.SolveExplanationProfileDefinition = + HyperTwistContractLibraryInternal::MakeDefaultClassicCubeSolveExplanationProfile( + Config.SolveExplanationProfileId + ); Config.FrameSize = FIntPoint(1280, 720); Config.MaxFrameRate = 30; return Config; @@ -898,17 +1078,23 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe {TEXT("F")}, Result.Confidence ); + const FHyperTwistVisionSessionConfig SessionConfig = + UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(); Result.ShellState = HyperTwistContractLibraryInternal::MakeClassicCubeWebcamShellState( - UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(), + SessionConfig, Result.ReconstructionSession, Result.ObservedFace.FaceId, Result.CommittedUnit ); Result.BrowserShellState = HyperTwistContractLibraryInternal::MakeClassicCubeBrowserShellState( - UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(), + SessionConfig, Result.ReconstructionSession, Result.ObservedFace ); + Result.SolveExplanationState = HyperTwistContractLibraryInternal::MakeClassicCubeSolveExplanationState( + SessionConfig, + Result.ReconstructionSession + ); return Result; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index 9598f7a..5bd57cb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -2471,6 +2471,123 @@ namespace HyperTwistTrainingSubsystemInternal return Profile; } + FHyperTwistVisionSolveExplanationProfile BuildDefaultClassicCubeSolveExplanationProfile( + const FString& ProfileId + ) + { + FHyperTwistVisionSolveExplanationProfile Profile; + Profile.ExplanationProfileId = ProfileId; + Profile.PuzzleId = TEXT("cube/3x3x3"); + Profile.RecommendationMode = TEXT("bounded-stage-ladder-v1"); + Profile.bSupportsStageRecommendation = true; + Profile.bSupportsStepNavigation = true; + Profile.bSupportsPlaybackStage = false; + + auto AddStep = [&Profile]( + const TCHAR* StepId, + const TCHAR* StageId, + const TCHAR* Title, + const TCHAR* Description, + const TCHAR* RecommendationLine, + const int32 StageOrdinal + ) + { + FHyperTwistVisionSolveExplanationStep Step; + Step.StepId = StepId; + Step.StageId = StageId; + Step.Title = Title; + Step.Description = Description; + Step.RecommendationLine = RecommendationLine; + Step.StageOrdinal = StageOrdinal; + Step.bPlaybackDeferred = true; + Profile.Steps.Add(Step); + }; + + AddStep( + TEXT("orient-centers-step"), + TEXT("orient-centers"), + TEXT("Orient Centers"), + TEXT("Check the cube orientation and align the center reference before turning into the solve ladder."), + TEXT("Confirm the edited net and lock the reference orientation before solve guidance begins."), + 0 + ); + AddStep( + TEXT("solve-white-cross-step"), + TEXT("solve-white-cross"), + TEXT("Build First Cross"), + TEXT("Assemble the opening cross with edge placement matched against the side colors."), + TEXT("Start with a stable cross so later first-layer turns stay bounded and readable."), + 1 + ); + AddStep( + TEXT("solve-first-layer-corners-step"), + TEXT("solve-first-layer-corners"), + TEXT("Place First-Layer Corners"), + TEXT("Insert the matching corners to finish the first layer without disturbing the cross."), + TEXT("Close the first layer before moving into the middle-band edge work."), + 2 + ); + AddStep( + TEXT("solve-second-layer-edges-step"), + TEXT("solve-second-layer-edges"), + TEXT("Solve Middle-Layer Edges"), + TEXT("Move the non-top edges into their middle-layer slots while preserving the solved first layer."), + TEXT("Complete the middle layer so orientation-only work remains on the last layer."), + 3 + ); + AddStep( + TEXT("solve-oll-edges-step"), + TEXT("solve-oll-edges"), + TEXT("Orient Last-Layer Edges"), + TEXT("Turn the last-layer edges so the top-face color is established across the edge ring."), + TEXT("Orient the last-layer edges before promoting corner orientation."), + 4 + ); + AddStep( + TEXT("solve-oll-corners-step"), + TEXT("solve-oll-corners"), + TEXT("Orient Last-Layer Corners"), + TEXT("Resolve the remaining top-face corner orientation while keeping the lower layers intact."), + TEXT("Finish top-face orientation before any final permutation pass."), + 5 + ); + AddStep( + TEXT("solve-pll-corners-step"), + TEXT("solve-pll-corners"), + TEXT("Permute Last-Layer Corners"), + TEXT("Cycle the last-layer corners into their destination positions after orientation is complete."), + TEXT("Place the last-layer corners first so the final edge permutation stays bounded."), + 6 + ); + AddStep( + TEXT("solve-pll-edges-step"), + TEXT("solve-pll-edges"), + TEXT("Permute Last-Layer Edges"), + TEXT("Finish the solve by cycling the last-layer edges into their final positions."), + TEXT("Close with the last-layer edges; playback ownership remains deferred in this packet."), + 7 + ); + + auto AddAction = [&Profile]( + const TCHAR* ActionId, + const TCHAR* InputBinding, + const TCHAR* SurfaceId + ) + { + FHyperTwistVisionShellActionBinding ActionBinding; + ActionBinding.ActionId = ActionId; + ActionBinding.InputBinding = InputBinding; + ActionBinding.SurfaceId = SurfaceId; + Profile.ActionBindings.Add(ActionBinding); + }; + + AddAction(TEXT("start-solve-explanation"), TEXT("button:start-explanation"), TEXT("cube-edit")); + AddAction(TEXT("step-solve-forward"), TEXT("button:next-step"), TEXT("solve-explanation-stage")); + AddAction(TEXT("step-solve-backward"), TEXT("button:previous-step"), TEXT("solve-explanation-stage")); + AddAction(TEXT("restart-solve-explanation"), TEXT("button:restart-explanation"), TEXT("solve-explanation-stage")); + return Profile; + } + FHyperTwistVisionShellState BuildClassicCubeWebcamShellState( const FHyperTwistVisionSessionConfig& SessionConfig, const FHyperTwistVisionReconstructionSession& ReconstructionSession, @@ -2578,6 +2695,64 @@ namespace HyperTwistTrainingSubsystemInternal return BrowserShellState; } + FHyperTwistVisionSolveExplanationState BuildClassicCubeSolveExplanationState( + const FHyperTwistVisionSessionConfig& SessionConfig, + const FHyperTwistVisionReconstructionSession& ReconstructionSession + ) + { + FHyperTwistVisionSolveExplanationState SolveExplanationState; + const FHyperTwistVisionSolveExplanationProfile SolveExplanationProfile = + SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid() + ? SessionConfig.SolveExplanationProfileDefinition + : BuildDefaultClassicCubeSolveExplanationProfile( + !SessionConfig.SolveExplanationProfileId.IsEmpty() + ? SessionConfig.SolveExplanationProfileId + : TEXT("classic-cube-solve-explanation-v1") + ); + + SolveExplanationState.ExplanationProfileId = SolveExplanationProfile.ExplanationProfileId; + SolveExplanationState.TotalStepCount = SolveExplanationProfile.Steps.Num(); + SolveExplanationState.bPlaybackDeferred = !SolveExplanationProfile.bSupportsPlaybackStage; + SolveExplanationState.bRequiresFullReconstruction = true; + + const int32 RemainingFaceCount = ReconstructionSession.IsStructurallyValid() + ? ReconstructionSession.MissingFaces.Num() + : 6; + if (ReconstructionSession.bHasCompleteClassicCubeNet) + { + SolveExplanationState.RecommendedStageId = + SolveExplanationProfile.Steps.Num() > 0 + ? SolveExplanationProfile.Steps[0].StageId + : TEXT("orient-centers"); + SolveExplanationState.RecommendationStatusLine = + TEXT("Solve guidance is ready after cube-edit review."); + SolveExplanationState.RecommendationDetailLine = + TEXT("Review any manual corrections, then start the bounded stage ladder. Playback ownership stays deferred in this packet."); + SolveExplanationState.CurrentStepOrdinal = -1; + SolveExplanationState.bReadyForExplanation = true; + SolveExplanationState.bStartExplanationVisible = true; + SolveExplanationState.bNextStepVisible = false; + SolveExplanationState.bPreviousStepVisible = false; + } + else + { + SolveExplanationState.RecommendedStageId = TEXT("capture-remaining-faces"); + SolveExplanationState.RecommendationStatusLine = + TEXT("Solve guidance unlocks after capture completes."); + SolveExplanationState.RecommendationDetailLine = FString::Printf( + TEXT("Commit the remaining %d face(s) and finish the bounded reconstruction pass before solve guidance unlocks."), + RemainingFaceCount + ); + SolveExplanationState.CurrentStepOrdinal = -1; + SolveExplanationState.bReadyForExplanation = false; + SolveExplanationState.bStartExplanationVisible = false; + SolveExplanationState.bNextStepVisible = false; + SolveExplanationState.bPreviousStepVisible = false; + } + + return SolveExplanationState; + } + EHyperTwistPuzzleFamily ResolvePuzzleFamilyFromPuzzleId(const FString& PuzzleId) { FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; @@ -7012,6 +7187,11 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit Result.ReconstructionSession, Result.ObservedFace ); + Result.SolveExplanationState = + HyperTwistTrainingSubsystemInternal::BuildClassicCubeSolveExplanationState( + ActiveRecognitionSessionState.SessionConfig, + Result.ReconstructionSession + ); } const FString ResolvedStage = !Result.CommittedUnit.IsEmpty() @@ -7984,6 +8164,28 @@ FHyperTwistVisionSessionConfig UHyperTwistTrainingSubsystem::BuildActiveRecognit SessionConfig.BrowserShellProfileDefinition.BrowserShellProfileId = SessionConfig.BrowserShellProfileId; } + if (SessionConfig.SolveExplanationProfileId.IsEmpty() + && SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid()) + { + SessionConfig.SolveExplanationProfileId = + SessionConfig.SolveExplanationProfileDefinition.ExplanationProfileId; + } + if (SessionConfig.SolveExplanationProfileId.IsEmpty()) + { + SessionConfig.SolveExplanationProfileId = TEXT("classic-cube-solve-explanation-v1"); + } + if (!SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid()) + { + SessionConfig.SolveExplanationProfileDefinition = + HyperTwistTrainingSubsystemInternal::BuildDefaultClassicCubeSolveExplanationProfile( + SessionConfig.SolveExplanationProfileId + ); + } + if (SessionConfig.SolveExplanationProfileDefinition.ExplanationProfileId.IsEmpty()) + { + SessionConfig.SolveExplanationProfileDefinition.ExplanationProfileId = + SessionConfig.SolveExplanationProfileId; + } } return SessionConfig; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h index ec3601e..ec44738 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h @@ -658,6 +658,155 @@ struct FHyperTwistVisionBrowserShellState } }; +USTRUCT(BlueprintType) +struct FHyperTwistVisionSolveExplanationStep +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StepId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StageId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Title; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Description; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendationLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 StageOrdinal = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPlaybackDeferred = true; + + bool IsStructurallyValid() const + { + return !StepId.IsEmpty() + && !StageId.IsEmpty() + && !Title.IsEmpty() + && !Description.IsEmpty() + && !RecommendationLine.IsEmpty() + && StageOrdinal >= 0; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionSolveExplanationProfile +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ExplanationProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PuzzleId = TEXT("cube/3x3x3"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendationMode = TEXT("bounded-stage-ladder-v1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsStageRecommendation = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsStepNavigation = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsPlaybackStage = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Steps; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ActionBindings; + + bool IsStructurallyValid() const + { + if (ExplanationProfileId.IsEmpty() + || PuzzleId.IsEmpty() + || RecommendationMode.IsEmpty() + || Steps.Num() <= 0 + || ActionBindings.Num() <= 0) + { + return false; + } + + for (const FHyperTwistVisionSolveExplanationStep& Step : Steps) + { + if (!Step.IsStructurallyValid()) + { + return false; + } + } + + for (const FHyperTwistVisionShellActionBinding& ActionBinding : ActionBindings) + { + if (!ActionBinding.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionSolveExplanationState +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ExplanationProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedStageId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendationStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendationDetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentStepId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CurrentStepOrdinal = -1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TotalStepCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bReadyForExplanation = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStartExplanationVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bNextStepVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreviousStepVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPlaybackDeferred = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRequiresFullReconstruction = true; + + bool IsStructurallyValid() const + { + return !ExplanationProfileId.IsEmpty() + && CurrentStepOrdinal >= -1 + && TotalStepCount >= 0; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistVisionSessionConfig { @@ -699,6 +848,12 @@ struct FHyperTwistVisionSessionConfig UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistVisionBrowserShellProfile BrowserShellProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SolveExplanationProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionSolveExplanationProfile SolveExplanationProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FIntPoint FrameSize = FIntPoint(1280, 720); @@ -822,6 +977,9 @@ struct FHyperTwistVisionCommitResult UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistVisionBrowserShellState BrowserShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionSolveExplanationState SolveExplanationState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") TArray Warnings; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RQSolveExplanationContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RQSolveExplanationContractTest.cpp new file mode 100644 index 0000000..de3e485 --- /dev/null +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RQSolveExplanationContractTest.cpp @@ -0,0 +1,223 @@ +// Copyright HyperTwist, Inc. All Rights Reserved. + +#include "Misc/AutomationTest.h" + +#include "Engine/GameInstance.h" +#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h" +#include "UObject/UnrealType.h" + +#if WITH_AUTOMATION_TESTS + +namespace HyperTwistRubixCubeSolverPhase6RQTestInternal +{ + FHyperTwistTrainingDeck MakeRecognitionDeck() + { + FHyperTwistTrainingDeck Deck; + Deck.DeckId = TEXT("phase6r-q/rubix-cube-solver-solve-explanation"); + Deck.Title = TEXT("Phase 6R-Q Rubix Cube Solver Solve Explanation"); + Deck.DeliveryModes = { + EHyperTwistTrainingDeliveryMode::RecognitionAssisted, + EHyperTwistTrainingDeliveryMode::CoachReviewed + }; + + FHyperTwistTrainingCase TrainingCase; + TrainingCase.CaseId = TEXT("phase6r-q-case"); + TrainingCase.PuzzleId = TEXT("cube/3x3x3"); + TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition; + TrainingCase.PromptLabel = TEXT("Phase 6R-Q Recognition"); + TrainingCase.AllowedDeliveryModes = { + EHyperTwistTrainingDeliveryMode::RecognitionAssisted, + EHyperTwistTrainingDeliveryMode::CoachReviewed + }; + Deck.Cases = {TrainingCase}; + return Deck; + } + + void ForceMockRecognitionClient(UHyperTwistTrainingSubsystem* TrainingSubsystem) + { + if (TrainingSubsystem == nullptr) + { + return; + } + + if (FStrProperty* RecognitionClientKindProperty = FindFProperty( + UHyperTwistTrainingSubsystem::StaticClass(), + TEXT("RecognitionClientKind") + )) + { + RecognitionClientKindProperty->SetPropertyValue_InContainer(TrainingSubsystem, TEXT("mock")); + } + } + + UHyperTwistTrainingSubsystem* MakeRecognitionSubsystem(const FString& SessionId) + { + UGameInstance* GameInstance = NewObject(GetTransientPackage()); + if (GameInstance == nullptr) + { + return nullptr; + } + + UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject(GameInstance); + if (TrainingSubsystem == nullptr) + { + return nullptr; + } + + ForceMockRecognitionClient(TrainingSubsystem); + const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck( + MakeRecognitionDeck(), + TEXT("phase6r-q-user"), + SessionId, + EHyperTwistTrainingDeliveryMode::RecognitionAssisted + ); + return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr; + } + + void SubmitPreviewFrame(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& FaceId, const int32 FrameOrdinal) + { + FHyperTwistVisionFrameEnvelope Frame; + Frame.FrameOrdinal = FrameOrdinal; + Frame.TargetFaceHint = FaceId; + TrainingSubsystem->SubmitActiveRecognitionFrame(Frame); + } + + FHyperTwistVisionCommitResult CommitFace( + UHyperTwistTrainingSubsystem* TrainingSubsystem, + const FString& FaceId, + const int32 FrameOrdinal + ) + { + SubmitPreviewFrame(TrainingSubsystem, FaceId, FrameOrdinal); + + FHyperTwistVisionCommitRequest Request; + Request.TargetFace = FaceId; + return TrainingSubsystem->CommitActiveRecognitionObservation(Request); + } +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistRubixCubeSolverPhase6RQSolveExplanationSessionConfigTest, + "HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q.SolveExplanationSessionConfig", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistRubixCubeSolverPhase6RQSolveExplanationSessionConfigTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistRubixCubeSolverPhase6RQTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-q-config-session")); + TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-Q."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + FString OpenError; + TestTrue(TEXT("The recognition session must open for the solve-explanation route."), TrainingSubsystem->OpenActiveRecognitionSession(OpenError)); + TestTrue(TEXT("Opening the recognition session must not report an error."), OpenError.IsEmpty()); + + const FHyperTwistTrainingRecognitionSessionState SessionState = + TrainingSubsystem->GetActiveRecognitionSessionState(); + TestEqual( + TEXT("The solve-explanation route must normalize the first-party explanation profile id."), + SessionState.SessionConfig.SolveExplanationProfileId, + TEXT("classic-cube-solve-explanation-v1") + ); + TestTrue( + TEXT("The solve-explanation route must provide a structurally valid explanation profile definition."), + SessionState.SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid() + ); + TestEqual( + TEXT("The solve-explanation route must retain eight bounded solve stages."), + SessionState.SessionConfig.SolveExplanationProfileDefinition.Steps.Num(), + 8 + ); + TestEqual( + TEXT("The solve-explanation route must retain four bounded explanation actions."), + SessionState.SessionConfig.SolveExplanationProfileDefinition.ActionBindings.Num(), + 4 + ); + TestFalse( + TEXT("The solve-explanation route must still defer playback-stage ownership in this packet."), + SessionState.SessionConfig.SolveExplanationProfileDefinition.bSupportsPlaybackStage + ); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistRubixCubeSolverPhase6RQSolveExplanationPartialStateTest, + "HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q.SolveExplanationPartialState", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistRubixCubeSolverPhase6RQSolveExplanationPartialStateTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistRubixCubeSolverPhase6RQTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-q-partial-session")); + TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-Q."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + const FHyperTwistVisionCommitResult Result = + HyperTwistRubixCubeSolverPhase6RQTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 1); + + TestTrue(TEXT("The partial commit route must carry a structurally valid solve explanation state."), Result.SolveExplanationState.IsStructurallyValid()); + TestFalse(TEXT("Solve guidance must stay locked before a full reconstruction is available."), Result.SolveExplanationState.bReadyForExplanation); + TestFalse(TEXT("The solve explanation start action must stay hidden before capture completes."), Result.SolveExplanationState.bStartExplanationVisible); + TestEqual(TEXT("The partial solve route must direct the user back to capture."), Result.SolveExplanationState.RecommendedStageId, TEXT("capture-remaining-faces")); + TestTrue(TEXT("The partial solve route must keep the full reconstruction guard explicit."), Result.SolveExplanationState.bRequiresFullReconstruction); + TestEqual(TEXT("The partial solve route must still expose the full bounded step count."), Result.SolveExplanationState.TotalStepCount, 8); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistRubixCubeSolverPhase6RQSolveExplanationCompleteStateTest, + "HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q.SolveExplanationCompleteState", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistRubixCubeSolverPhase6RQSolveExplanationCompleteStateTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistRubixCubeSolverPhase6RQTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-q-complete-session")); + TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-Q."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + const TArray FaceOrder = { + TEXT("U"), + TEXT("R"), + TEXT("F"), + TEXT("D"), + TEXT("L"), + TEXT("B") + }; + + FHyperTwistVisionCommitResult Result; + for (int32 FaceOrdinal = 0; FaceOrdinal < FaceOrder.Num(); ++FaceOrdinal) + { + Result = HyperTwistRubixCubeSolverPhase6RQTestInternal::CommitFace( + TrainingSubsystem, + FaceOrder[FaceOrdinal], + FaceOrdinal + 1 + ); + } + + TestTrue(TEXT("The completed solve route must carry a structurally valid reconstruction session."), Result.ReconstructionSession.IsStructurallyValid()); + TestTrue(TEXT("The completed solve route must carry a structurally valid solve explanation state."), Result.SolveExplanationState.IsStructurallyValid()); + TestTrue(TEXT("The completed solve route must carry a full classic cube net."), Result.ReconstructionSession.bHasCompleteClassicCubeNet); + TestTrue(TEXT("Solve guidance must unlock after the full reconstruction completes."), Result.SolveExplanationState.bReadyForExplanation); + TestTrue(TEXT("The solve explanation start action must be visible after reconstruction completes."), Result.SolveExplanationState.bStartExplanationVisible); + TestFalse(TEXT("The solve explanation route must not expose next-step navigation before explanation starts."), Result.SolveExplanationState.bNextStepVisible); + TestFalse(TEXT("The solve explanation route must not expose previous-step navigation before explanation starts."), Result.SolveExplanationState.bPreviousStepVisible); + TestTrue(TEXT("The solve explanation route must keep playback deferred in this packet."), Result.SolveExplanationState.bPlaybackDeferred); + TestEqual(TEXT("The solve explanation route must recommend the orientation stage first."), Result.SolveExplanationState.RecommendedStageId, TEXT("orient-centers")); + TestEqual(TEXT("The solve explanation route must keep the bounded eight-stage ladder."), Result.SolveExplanationState.TotalStepCount, 8); + TestEqual(TEXT("The browser shell must remain in cube-edit after reconstruction completes."), Result.BrowserShellState.ActiveStageId, TEXT("cube-edit")); + return true; +} + +#endif diff --git a/docs/REPO_LICENSE_TRACKING.md b/docs/REPO_LICENSE_TRACKING.md index 384d104..9d56b1e 100644 --- a/docs/REPO_LICENSE_TRACKING.md +++ b/docs/REPO_LICENSE_TRACKING.md @@ -153,9 +153,10 @@ Status update on `2026-05-21`: - `HactarCE/Hyperspeedcube` is now closed for the currently justified retained families - the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell packet is now landed in current code -- the current next bounded move is a source-backed `Phase 6R-Q` - `vivaansinghvi07/rubix-cube-solver` solve explanation/recommendation preparation/control pass, - not a new restrictive packet by default +- the bounded permissive `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` + solve explanation/recommendation packet is now landed in current code +- the current next bounded move is a source-backed retained classic-cube recognition multi-face + correction/explanation preparation/control pass, not a new restrictive packet by default - use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A` ownership contract for the current queue after that correction @@ -259,9 +260,8 @@ Current practical interpretation: - the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md` - the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md` - the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md` -- the next bounded move is a source-backed `Phase 6R-Q` - `vivaansinghvi07/rubix-cube-solver` preparation/control pass for a bounded solve - explanation/recommendation seam +- the next bounded move is a source-backed retained classic-cube recognition multi-face + correction/explanation preparation/control pass Companion docs: @@ -803,8 +803,9 @@ Approved working posture: - face-vote replacement ledger - final classic-net shaping above aggregated committed faces - browser/webcam shell profile and browser-shell session-state composition + - bounded solve explanation/recommendation stage ladder and recommendation state - keep the row only partially incorporated by default: - - broad solve explanation or recommendation shell remains deferred + - multi-face correction or explanation shell beyond the bounded stage ladder remains deferred - any redistribution of `frontend/lib/twistysim.min.js` still requires preserved or replaced upstream provenance/license diff --git a/docs/arch/HYPERTWIST_PHASE6R_Q_RUBIX_CUBE_SOLVER_SOLVE_EXPLANATION_RECOMMENDATION_IMPLEMENTATION_PACKET_2026-05-23.md b/docs/arch/HYPERTWIST_PHASE6R_Q_RUBIX_CUBE_SOLVER_SOLVE_EXPLANATION_RECOMMENDATION_IMPLEMENTATION_PACKET_2026-05-23.md new file mode 100644 index 0000000..7433ba9 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_Q_RUBIX_CUBE_SOLVER_SOLVE_EXPLANATION_RECOMMENDATION_IMPLEMENTATION_PACKET_2026-05-23.md @@ -0,0 +1,135 @@ +# HyperTwist Phase 6R-Q rubix-cube-solver solve explanation recommendation implementation packet + +Created on `2026-05-23` + +## Status + +- first-party HyperTwist packet +- bounded permissive `Phase 6R-Q` implementation slice + +## Purpose + +This packet lands the current retained `rubix-cube-solver` solve explanation/recommendation +family: + +- first-party classic-cube solve explanation profile and recommendation-state composition above the + already landed reconstruction and browser-shell seams + +It is not: + +- a full `rubix-cube-solver` row transplant +- a bundled `twistysim.min.js` redistribution packet +- a generalized solver backend packet +- a broad multi-face correction shell packet +- a `qbr` multilingual shell packet + +## Current authority basis + +This implementation packet stands on: + +- `docs/REPO_LICENSE_TRACKING.md` +- `docs/ops/HYPERTWIST_CROSS_LANE_AUTHORITY_HIERARCHY_AND_RECONCILIATION_2026-05-20.md` +- `docs/arch/HYPERTWIST_PHASE6R_Q_RUBIX_CUBE_SOLVER_SOLVE_EXPLANATION_RECOMMENDATION_PREPARATION_PACKET_2026-05-23.md` + +The retained owner remains: + +- `vivaansinghvi07/rubix-cube-solver` + +The top-level product shell owner does not change here: + +- first-party HyperTwist remains the broader recognition-session and product-shell owner +- this packet keeps `vivaansinghvi07/rubix-cube-solver` bounded to the narrower `A1 / R1 / F3` + solve explanation/recommendation slice + +## Landed scope + +The current code now owns a retained `rubix-cube-solver` solve explanation/recommendation contract +through: + +- first-party `HyperTwistRecognition` contract types for: + - solve explanation step definition + - solve explanation profile definition + - solve explanation session-state readout + - solve explanation profile id on recognition session config + - commit result solve explanation payloads +- recognition session config defaults for: + - first-party classic-cube solve explanation profile id + - solve explanation profile definition +- first-party commit result shaping for: + - explanation readiness after full reconstruction + - recommendation stage and detail lines + - bounded step count + - start/previous/next visibility + - playback deferred state + - explicit full-reconstruction guard +- sample contract routing in: + - `UHyperTwistContractLibrary` +- active subsystem normalization in: + - `UHyperTwistTrainingSubsystem` +- focused automation coverage in: + - `HyperTwistRubixCubeSolverPhase6RQSolveExplanationContractTest.cpp` + +## Why this is still intentionally bounded + +This packet lands the bounded solve explanation/recommendation seam, but it does not widen into +neighboring families or broader ownership. + +Still excluded: + +- bundled `twistysim.min.js` redistribution +- generalized solver backend ownership +- broad multi-face correction shell ownership +- `qbr` multilingual shell ownership +- `qbr` bundled-font redistribution + +## Validation + +Build validation: + +- `C:\Program Files\Epic Games\UE_5.7\Engine\Build\BatchFiles\Build.bat UnrealHyperTwistEditor Win64 Development -Project='C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject' -WaitMutex -NoHotReloadFromIDE -NoUba` + +Focused automation validation: + +- `C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject -unattended -nop4 -nosplash -NullRHI -log -stdout -FullStdOutLogOutput -AbsLog=C:\HyperTwist\UnrealHyperTwist\Saved\Logs\Phase6R-Q-Rubix-Verify.log -ReportExportPath=C:\HyperTwist\UnrealHyperTwist\Saved\AutomationReports\Phase6R-Q-Rubix-Verify -ExecCmds="Automation RunTests HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q; Quit" -TestExit="Automation Test Queue Empty"` + +Regression automation validation: + +- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.P` +- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.C` +- `HyperTwist.Permissive.Qbr.Phase6R.O` +- `HyperTwist.CleanRoom.CubeDesk` + +Expected covered tests: + +- `SolveExplanationSessionConfig` +- `SolveExplanationPartialState` +- `SolveExplanationCompleteState` +- existing `rubix-cube-solver`, `qbr`, and `CubeDesk` regression suites + +## Queue effect + +This packet consumes the current `Phase 6R-Q` implementation slice. + +`vivaansinghvi07/rubix-cube-solver` remains partially incorporated: + +- landed now: + - committed-face reconstruction session + - face-vote replacement ledger + - final classic-net shaping above aggregated committed faces + - browser/webcam shell profile and browser-shell session-state composition + - bounded solve explanation/recommendation stage ladder and recommendation state +- still deferred: + - multi-face correction or explanation shell beyond the bounded stage ladder + - bundled `twistysim.min.js` redistribution + +The next clean move is: + +- a source-backed retained classic-cube recognition multi-face correction/explanation + preparation/control pass shared across `kkoomen/qbr` and `vivaansinghvi07/rubix-cube-solver` + +Keep the future sequencing guards visible: + +- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing its + upstream provenance/license in any future widening that would ship that asset +- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation or + replacement in any future widening that would ship that asset diff --git a/docs/arch/HYPERTWIST_PHASE6R_Q_RUBIX_CUBE_SOLVER_SOLVE_EXPLANATION_RECOMMENDATION_PREPARATION_PACKET_2026-05-23.md b/docs/arch/HYPERTWIST_PHASE6R_Q_RUBIX_CUBE_SOLVER_SOLVE_EXPLANATION_RECOMMENDATION_PREPARATION_PACKET_2026-05-23.md new file mode 100644 index 0000000..db9e3b1 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_Q_RUBIX_CUBE_SOLVER_SOLVE_EXPLANATION_RECOMMENDATION_PREPARATION_PACKET_2026-05-23.md @@ -0,0 +1,206 @@ +# HyperTwist Phase 6R-Q rubix-cube-solver solve explanation recommendation preparation packet + +Created on `2026-05-23` + +## Status + +- historical consumed preparation authority +- bounded post-`Phase 6R-P` control slice +- the first bounded implementation slice now lands separately after this packet + +## Purpose + +This packet freezes the next widening order after the landed `Phase 6R-P` +`vivaansinghvi07/rubix-cube-solver` browser/webcam shell slice. + +The open task was: + +- define the next bounded source-backed widening packet for `vivaansinghvi07/rubix-cube-solver` + as a solve explanation/recommendation seam above the already landed reconstruction and + browser-shell seams + +It is not: + +- a full `rubix-cube-solver` row transplant +- a bundled `twistysim.min.js` redistribution packet +- a generalized solver backend packet +- a broad multi-face correction shell packet +- a `qbr` multilingual shell packet +- a `qbr` bundled-font packet + +## Current authority basis + +This preparation packet stands on already-closed authority: + +- `docs/REPO_LICENSE_TRACKING.md` +- `docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md` +- `docs/arch/HYPERTWIST_PHASE6R_C_RUBIX_CUBE_SOLVER_RECONSTRUCTION_IMPLEMENTATION_PACKET_2026-05-20.md` +- `docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md` +- `docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md` + +The key accepted routing facts are: + +- `vivaansinghvi07/rubix-cube-solver` remains the retained recognition-companion donor for the + bounded browser-assisted correction and solve-guidance seam +- the already landed `Phase 6R-C` slice owns: + - committed-face reconstruction session + - face-vote replacement ledger + - final classic-net shaping above aggregated committed faces +- the already landed `Phase 6R-P` slice owns: + - classic-cube browser shell profile + - browser-stage routing + - websocket transport metadata + - manual cube-edit handoff +- the donor value now strongest for the next narrower slice is: + - solve-stage recommendation semantics + - bounded step-ladder authoring + - explanation-state readiness after full reconstruction + - next/previous/restart explanation action routing +- ownership denied here is: + - do not widen into bundled `twistysim.min.js` redistribution + - do not widen into generalized solver backend ownership + - do not widen into `qbr` multilingual or bundled-font ownership + - do not widen into the broader multi-face correction shell in the same packet + +## Why this was the next packet + +The earlier retained queue heads were already landed: + +- `Phase 6R-A` `HactarCE/Hyperspeedcube` +- `Phase 6R-B` `kkoomen/qbr` +- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` +- `Phase 6R-D` `roice3/MagicTile` +- `Phase 6R-E` `ggml-org/whisper.cpp` +- `Phase 6R-F` `SYSTRAN/faster-whisper` +- `Phase 6R-G` `rhasspy/piper` +- `Phase 6R-H` `coqui-ai/TTS` +- `Phase 6R-I` `roice3/Magic120Cell` +- `Phase 6R-J` `roice3/MagicCube5D` +- `Phase 6R-K` `HactarCE/Hyperspeedcube` +- `Phase 6R-L` `HactarCE/Hyperspeedcube` +- `Phase 6R-M` `HactarCE/Hyperspeedcube` +- `Phase 6R-N` `HactarCE/Hyperspeedcube` +- `Phase 6R-O` `kkoomen/qbr` +- `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` + +That advanced the live queue to: + +- `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` + +with the adjacent retained recognition remainder ordered behind it: + +- retained classic-cube recognition multi-face correction/explanation shell + +## Required result + +The source-backed control pass for this packet is now complete. + +The first actual `6R-Q` implementation packet should: + +- define one bounded retained solve explanation/recommendation slice from `rubix-cube-solver` +- keep the slice inside the accepted recognition-shell seam above the landed reconstruction and + browser-shell routes +- widen only the first stage-ladder guidance family that can stand on its own without dragging in + bundled frontend-asset redistribution or generalized solver backend ownership +- explicitly state which neighboring retained rows stay closed in that packet + +## Source-backed retained basis + +The queue-head decision is now source-backed rather than README-only. + +Inspected retained donor basis: + +- `README.md` +- `backend/server.py` +- `backend/cv.py` +- `frontend/src/script.js` + +Inspected first-party receiving basis: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RCReconstructionContractTest.cpp` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RPBrowserShellContractTest.cpp` + +## Narrowed 6R-Q slice decision + +The first widening slice is fixed as: + +1. classic-cube solve explanation/recommendation profile and commit-state composition + +not as bundled frontend playback or generalized solver backend ownership. + +That narrowed slice covers: + +- first-party solve explanation profile for: + - ordered bounded stage ids: + - `orient-centers` + - `solve-white-cross` + - `solve-first-layer-corners` + - `solve-second-layer-edges` + - `solve-oll-edges` + - `solve-oll-corners` + - `solve-pll-corners` + - `solve-pll-edges` + - bounded action bindings: + - `start-solve-explanation` + - `step-solve-forward` + - `step-solve-backward` + - `restart-solve-explanation` +- first-party solve explanation state readout for: + - explanation readiness after full reconstruction + - current recommendation stage + - recommendation status/detail lines + - total bounded step count + - start/previous/next visibility + - playback deferred state +- subsystem normalization and focused automation coverage + +## Deferred neighboring families + +The first `6R-Q` implementation packet must keep these capability families closed: + +- bundled `twistysim.min.js` shipping or redistribution +- generalized solver backend ownership +- `qbr` multilingual shell ownership +- `qbr` bundled-font redistribution +- broad multi-face correction shell ownership +- broad playback runtime ownership + +## Why this slice was first + +- `rubix-cube-solver` is strongest for the bounded solve-ladder guidance that sits directly above + the already landed reconstruction and browser-shell seams +- current first-party recognition code already had the session, preview, commit, reconstruction, + and browser-shell envelopes, but it did not yet own a first-party solve explanation profile or + recommendation state +- widening into bundled frontend assets or generalized backend ownership would prematurely absorb + adjacent legal and routing concerns that are still narrower and separately governed + +## Out of scope for the first 6R-Q packet + +- bundled `twistysim.min.js` shipping +- generalized solver backend ownership +- `qbr` multilingual or bundled-font ownership +- broad multi-face correction shell ownership +- broad playback runtime ownership + +## Acceptance criteria + +- the packet records why the live queue advanced to `6R-Q` +- the packet records that the first `6R-Q` widening slice is solve explanation/recommendation only +- the packet states exact out-of-scope families for the first `6R-Q` pass +- the packet keeps the `rubix-cube-solver` frontend-asset guard visible +- the packet keeps the `qbr` bundled-font guard visible + +## Validation checklist + +1. confirm the retained `rubix-cube-solver` seam narrows cleanly to a bounded solve explanation + profile and recommendation-state contract +2. confirm the implementation packet is framed as bounded source-backed widening rather than a + donor frontend transplant +3. confirm bundled frontend-asset redistribution, generalized solver backend widening, and broader + multi-face correction remain explicitly deferred + +That is the packet. diff --git a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md index a457bd8..367bea9 100644 --- a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md +++ b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md @@ -98,13 +98,17 @@ The next bounded move is now: current code 6. the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell packet is now landed in current code -7. source-backed `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` solve - explanation/recommendation preparation/control pass is now the next bounded move -8. keep `Phase 6R-Q` narrower than bundled `twistysim.min.js` redistribution and generalized - solver backend ownership in the same packet -9. keep the `qbr` guard visible: - - do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation - or replacement in any future widening that would ship that asset +7. the bounded permissive `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` solve + explanation/recommendation packet is now landed in current code +8. the next bounded move is a source-backed retained classic-cube recognition multi-face + correction/explanation preparation/control pass shared across `kkoomen/qbr` and + `vivaansinghvi07/rubix-cube-solver` +9. keep the `rubix-cube-solver` guard visible: + - do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing + its upstream provenance/license in any future widening that would ship that asset +10. keep the `qbr` guard visible: + - do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation + or replacement in any future widening that would ship that asset ## Memory-specific sequencing rule 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 eb00997..7dd64e0 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md @@ -110,6 +110,7 @@ Do not collapse those three tiers into one undifferentiated "features" voice. | Classic-cube webcam shell | Implemented now | landed `qbr` `Phase 6R-O` | First-party webcam shell profile, bounded overlay panels, and input-action contract are live. | | Committed-face reconstruction and final classic-net shaping | Implemented now | landed `rubix-cube-solver` packet | Reconstruction session boundary is live. | | Browser-assisted recognition shell | Implemented now | landed `rubix-cube-solver` `Phase 6R-P` | First-party browser-shell profile, websocket session metadata, stage routing, and manual-edit handoff are live. | +| Bounded solve explanation and recommendation shell | Implemented now | landed `rubix-cube-solver` `Phase 6R-Q` | First-party stage-ladder guidance and recommendation-state readout are live after full reconstruction. | | Hyper puzzle catalog contract | Implemented now | landed `Hyperspeedcube` `Phase 6R-A` | Narrow retained hyper-puzzle catalog slice is live. | | Hyper notation and replay-log serialization boundary | Implemented now | landed `Hyperspeedcube` `Phase 6R-K` | Narrow retained notation and log-serialization slice is live. | | Hyper replay verification boundary | Implemented now | landed `Hyperspeedcube` `Phase 6R-L` | Narrow retained replay-verification and solve-proof slice is live. | @@ -146,6 +147,7 @@ repo. | Classic-cube webcam shell | Implemented now | `qbr` bounded packet | Live first-party webcam shell profile and shell-state composition above the bounded recognition intake. | | Committed-face reconstruction session | Implemented now | `rubix-cube-solver` bounded packet | Live bounded reconstruction seam. | | Browser recognition shell | Implemented now | `rubix-cube-solver` bounded packet | Live browser session-state, websocket transport routing, and manual-edit handoff above the bounded reconstruction seam. | +| Bounded solve explanation/recommendation shell | Implemented now | `rubix-cube-solver` bounded packet | Live stage-ladder recommendation state above the bounded browser recognition shell. | | Provider-backed recognition service contract | Implemented now | first-party recognition client surfaces | First-party normalized recognition session boundary is real. | | Multilingual recognition guidance shell | Deep-source grounded retained | `qbr` retained remainder | Kept deferred until a font-safe and provenance-safe shipping path is explicit. | | Multi-face correction/explanation shell | Deep-source grounded retained | `qbr`, `rubix-cube-solver` retained remainder | Not yet widened into the live shell. | diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md index 8f9a034..9df5e54 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -124,9 +124,11 @@ Canonical discovery surfaces for roadmap interpretation: current code - the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell packet is now landed in current code -- the current next bounded move is a source-backed `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` - solve explanation/recommendation preparation/control pass for one bounded correction-shell - widening slice +- the bounded permissive `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` + solve explanation/recommendation packet is now landed in current code +- the current next bounded move is a source-backed retained classic-cube recognition multi-face + correction/explanation preparation/control pass, while keeping both the `twistysim.min.js` and + `arial-unicode-ms.ttf` redistribution guards explicit - the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than waiting on another first-party packet @@ -188,8 +190,9 @@ Current routing truth: - active non-live implementation-board rows: `34` - retained benchmark, oracle, or clean-room-later rows outside the active implementation board: `9` -The next bounded move is a source-backed `Phase 6R-Q` -`vivaansinghvi07/rubix-cube-solver` solve explanation/recommendation preparation/control pass. +The next bounded move is a source-backed retained classic-cube recognition multi-face +correction/explanation preparation/control pass shared across `kkoomen/qbr` and +`vivaansinghvi07/rubix-cube-solver`. Queue interpretation after that packet: @@ -242,13 +245,17 @@ Queue interpretation after that packet: - face-vote replacement ledger - final classic-net shaping above aggregated committed faces - browser/webcam shell profile and browser-shell session-state composition + - bounded solve explanation/recommendation stage ladder and recommendation state - still deferred: - - broad solve explanation or recommendation shell + - multi-face correction or explanation shell beyond the bounded stage ladder - bundled `twistysim.min.js` redistribution -- after the `Phase 6R-P` implementation packet, keep the future legal sequencing guard visible: +- after the `Phase 6R-Q` implementation packet, keep the future legal sequencing guards visible: - `vivaansinghvi07/rubix-cube-solver` - do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing its upstream provenance/license in any future widening that would ship that asset + - `kkoomen/qbr` + - do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation + or replacement in any future widening that would ship that asset - `roice3/MagicTile` remains a partially landed row rather than a closed row: - landed: - tiling topology and geometry-family contract @@ -313,10 +320,11 @@ Queue interpretation after that packet: - viseme / gesture runtime integration - broad assistant-platform scope - the next queue shape is now: - - `vivaansinghvi07/rubix-cube-solver` deferred solve explanation / recommendation remainder + - retained classic-cube recognition multi-face correction/explanation shell remainder - the next bounded packet should stay narrow: - - solve explanation/recommendation preparation/control before widening into bundled - `twistysim.min.js` redistribution or generalized solver backend ownership + - multi-face correction/explanation preparation/control before widening into bundled + `twistysim.min.js` redistribution, generalized solver backend ownership, or any `qbr` + multilingual/font-shipping path - keep the speech-input / voice sidecar legal sequencing guard visible: - keep code-license judgments separate from model, voice, and payload-license review - keep the provider-neutral speech-lane guard visible: