From 3c1a025e3c3128aa0e7310fca0bb7480bd66752c Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Sat, 23 May 2026 01:55:29 +0200 Subject: [PATCH] Implement Phase 6R-P browser webcam shell --- .../HyperTwistContractLibrary.cpp | 116 ++++++++ .../HyperTwistTrainingSubsystem.cpp | 123 +++++++++ .../HyperTwistRecognitionTypes.h | 139 ++++++++++ ...SolverPhase6RPBrowserShellContractTest.cpp | 250 ++++++++++++++++++ docs/REPO_LICENSE_TRACKING.md | 18 +- ..._SHELL_IMPLEMENTATION_PACKET_2026-05-22.md | 131 +++++++++ ...CAM_SHELL_PREPARATION_PACKET_2026-05-22.md | 198 ++++++++++++++ ...PERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md | 12 +- .../HyperTwist/FEATURE_REGISTRY.md | 3 +- .../HyperTwist/ROADMAP.md | 29 +- 10 files changed, 994 insertions(+), 25 deletions(-) create mode 100644 UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RPBrowserShellContractTest.cpp create mode 100644 docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md create mode 100644 docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_PREPARATION_PACKET_2026-05-22.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp index 7d0274b..50f9941 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -292,6 +292,49 @@ namespace HyperTwistContractLibraryInternal return Profile; } + FString NormalizeBrowserFaceHint(const FString& FaceId) + { + FString Normalized = FaceId; + Normalized = Normalized.TrimStartAndEnd(); + Normalized.ToUpperInline(); + return Normalized; + } + + FHyperTwistVisionBrowserShellProfile MakeDefaultClassicCubeBrowserShellProfile(const FString& ProfileId) + { + FHyperTwistVisionBrowserShellProfile Profile; + Profile.BrowserShellProfileId = ProfileId; + Profile.TransportKind = TEXT("websocket"); + Profile.TransportEndpoint = TEXT("ws://localhost:8090/"); + Profile.CaptureIntervalMs = 100; + Profile.bSupportsManualCubeEdit = true; + Profile.bSupportsPlaybackStage = false; + Profile.OrderedStageIds = { + TEXT("home-page"), + TEXT("video-stream"), + TEXT("cube-edit") + }; + + 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-browser-capture"), TEXT("button:start-capture"), TEXT("home-page")); + AddAction(TEXT("finish-browser-capture"), TEXT("button:finish-capture"), TEXT("video-stream")); + AddAction(TEXT("apply-manual-corrections"), TEXT("button:apply-corrections"), TEXT("cube-edit")); + AddAction(TEXT("restart-browser-session"), TEXT("button:restart-session"), TEXT("cube-edit")); + return Profile; + } + void ResolveMockClassicFaceSwatch( const FString& FaceId, FString& OutColorId, @@ -499,6 +542,64 @@ namespace HyperTwistContractLibraryInternal return ShellState; } + + FHyperTwistVisionBrowserShellState MakeClassicCubeBrowserShellState( + const FHyperTwistVisionSessionConfig& SessionConfig, + const FHyperTwistVisionReconstructionSession& ReconstructionSession, + const FHyperTwistVisionFaceObservation& ObservedFace + ) + { + FHyperTwistVisionBrowserShellState BrowserShellState; + const FHyperTwistVisionBrowserShellProfile BrowserShellProfile = + SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid() + ? SessionConfig.BrowserShellProfileDefinition + : MakeDefaultClassicCubeBrowserShellProfile( + !SessionConfig.BrowserShellProfileId.IsEmpty() + ? SessionConfig.BrowserShellProfileId + : TEXT("classic-cube-browser-shell-v1") + ); + + BrowserShellState.BrowserShellProfileId = BrowserShellProfile.BrowserShellProfileId; + BrowserShellState.TransportKind = BrowserShellProfile.TransportKind; + BrowserShellState.TransportEndpoint = BrowserShellProfile.TransportEndpoint; + BrowserShellState.CaptureIntervalMs = BrowserShellProfile.CaptureIntervalMs; + BrowserShellState.CubeDimension = 3; + BrowserShellState.ExpectedFaceCount = 6; + BrowserShellState.CommittedFaceCount = ReconstructionSession.IsStructurallyValid() + ? FMath::Max(0, ReconstructionSession.TotalCommittedFaceCount) + : 0; + BrowserShellState.TargetFaceHint = !ObservedFace.FaceId.IsEmpty() + ? NormalizeBrowserFaceHint(ObservedFace.FaceId) + : TEXT("F"); + BrowserShellState.bTransportRouteReady = true; + BrowserShellState.bPlaybackDeferred = !BrowserShellProfile.bSupportsPlaybackStage; + BrowserShellState.bSnapshotReady = ObservedFace.IsStructurallyValid() && ObservedFace.bCommitReady; + + if (ReconstructionSession.bHasCompleteClassicCubeNet) + { + BrowserShellState.ActiveStageId = TEXT("cube-edit"); + BrowserShellState.bVideoStreamVisible = false; + BrowserShellState.bManualCubeEditVisible = true; + BrowserShellState.bManualCubeEditReady = true; + } + else + { + BrowserShellState.ActiveStageId = TEXT("video-stream"); + BrowserShellState.bVideoStreamVisible = true; + BrowserShellState.bManualCubeEditVisible = false; + BrowserShellState.bManualCubeEditReady = false; + } + + if (BrowserShellState.TargetFaceHint.IsEmpty() + && ReconstructionSession.IsStructurallyValid() + && ReconstructionSession.MissingFaces.Num() > 0) + { + BrowserShellState.TargetFaceHint = + NormalizeBrowserFaceHint(ReconstructionSession.MissingFaces[0]); + } + + return BrowserShellState; + } } FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition() @@ -733,6 +834,11 @@ FHyperTwistVisionSessionConfig UHyperTwistContractLibrary::MakeSampleVisionSessi HyperTwistContractLibraryInternal::MakeDefaultClassicCubeWebcamShellProfile( Config.ShellProfileId ); + Config.BrowserShellProfileId = TEXT("classic-cube-browser-shell-v1"); + Config.BrowserShellProfileDefinition = + HyperTwistContractLibraryInternal::MakeDefaultClassicCubeBrowserShellProfile( + Config.BrowserShellProfileId + ); Config.FrameSize = FIntPoint(1280, 720); Config.MaxFrameRate = 30; return Config; @@ -759,6 +865,11 @@ FHyperTwistVisionPreviewResult UHyperTwistContractLibrary::MakeMockVisionPreview Result.ObservedFace.FaceId, FString() ); + Result.BrowserShellState = HyperTwistContractLibraryInternal::MakeClassicCubeBrowserShellState( + UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(), + FHyperTwistVisionReconstructionSession(), + Result.ObservedFace + ); Result.Guidance = TEXT("capture-ready"); return Result; } @@ -793,6 +904,11 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe Result.ObservedFace.FaceId, Result.CommittedUnit ); + Result.BrowserShellState = HyperTwistContractLibraryInternal::MakeClassicCubeBrowserShellState( + UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(), + Result.ReconstructionSession, + Result.ObservedFace + ); return Result; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index 3b301c9..9598f7a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -2436,6 +2436,41 @@ namespace HyperTwistTrainingSubsystemInternal return Profile; } + FHyperTwistVisionBrowserShellProfile BuildDefaultClassicCubeBrowserShellProfile(const FString& ProfileId) + { + FHyperTwistVisionBrowserShellProfile Profile; + Profile.BrowserShellProfileId = ProfileId; + Profile.TransportKind = TEXT("websocket"); + Profile.TransportEndpoint = TEXT("ws://localhost:8090/"); + Profile.CaptureIntervalMs = 100; + Profile.bSupportsManualCubeEdit = true; + Profile.bSupportsPlaybackStage = false; + Profile.OrderedStageIds = { + TEXT("home-page"), + TEXT("video-stream"), + TEXT("cube-edit") + }; + + 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-browser-capture"), TEXT("button:start-capture"), TEXT("home-page")); + AddAction(TEXT("finish-browser-capture"), TEXT("button:finish-capture"), TEXT("video-stream")); + AddAction(TEXT("apply-manual-corrections"), TEXT("button:apply-corrections"), TEXT("cube-edit")); + AddAction(TEXT("restart-browser-session"), TEXT("button:restart-session"), TEXT("cube-edit")); + return Profile; + } + FHyperTwistVisionShellState BuildClassicCubeWebcamShellState( const FHyperTwistVisionSessionConfig& SessionConfig, const FHyperTwistVisionReconstructionSession& ReconstructionSession, @@ -2487,6 +2522,62 @@ namespace HyperTwistTrainingSubsystemInternal return ShellState; } + FHyperTwistVisionBrowserShellState BuildClassicCubeBrowserShellState( + const FHyperTwistVisionSessionConfig& SessionConfig, + const FHyperTwistVisionReconstructionSession& ReconstructionSession, + const FHyperTwistVisionFaceObservation& ObservedFace + ) + { + FHyperTwistVisionBrowserShellState BrowserShellState; + const FHyperTwistVisionBrowserShellProfile BrowserShellProfile = + SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid() + ? SessionConfig.BrowserShellProfileDefinition + : BuildDefaultClassicCubeBrowserShellProfile( + !SessionConfig.BrowserShellProfileId.IsEmpty() + ? SessionConfig.BrowserShellProfileId + : TEXT("classic-cube-browser-shell-v1") + ); + + BrowserShellState.BrowserShellProfileId = BrowserShellProfile.BrowserShellProfileId; + BrowserShellState.TransportKind = BrowserShellProfile.TransportKind; + BrowserShellState.TransportEndpoint = BrowserShellProfile.TransportEndpoint; + BrowserShellState.CaptureIntervalMs = BrowserShellProfile.CaptureIntervalMs; + BrowserShellState.CubeDimension = 3; + BrowserShellState.CommittedFaceCount = ReconstructionSession.IsStructurallyValid() + ? FMath::Max(0, ReconstructionSession.TotalCommittedFaceCount) + : 0; + BrowserShellState.ExpectedFaceCount = 6; + BrowserShellState.TargetFaceHint = !ObservedFace.FaceId.IsEmpty() + ? NormalizeRecognitionFaceId(ObservedFace.FaceId) + : TEXT("F"); + BrowserShellState.bTransportRouteReady = true; + BrowserShellState.bPlaybackDeferred = !BrowserShellProfile.bSupportsPlaybackStage; + BrowserShellState.bSnapshotReady = ObservedFace.IsStructurallyValid() && ObservedFace.bCommitReady; + + if (ReconstructionSession.bHasCompleteClassicCubeNet) + { + BrowserShellState.ActiveStageId = TEXT("cube-edit"); + BrowserShellState.bVideoStreamVisible = false; + BrowserShellState.bManualCubeEditVisible = true; + BrowserShellState.bManualCubeEditReady = true; + } + else + { + BrowserShellState.ActiveStageId = TEXT("video-stream"); + BrowserShellState.bVideoStreamVisible = true; + BrowserShellState.bManualCubeEditVisible = false; + BrowserShellState.bManualCubeEditReady = false; + } + + if (BrowserShellState.TargetFaceHint.IsEmpty() && ReconstructionSession.MissingFaces.Num() > 0) + { + BrowserShellState.TargetFaceHint = + NormalizeRecognitionFaceId(ReconstructionSession.MissingFaces[0]); + } + + return BrowserShellState; + } + EHyperTwistPuzzleFamily ResolvePuzzleFamilyFromPuzzleId(const FString& PuzzleId) { FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; @@ -6771,6 +6862,11 @@ FHyperTwistVisionPreviewResult UHyperTwistTrainingSubsystem::SubmitActiveRecogni !Result.ObservedFace.FaceId.IsEmpty() ? Result.ObservedFace.FaceId : NormalizedFrame.TargetFaceHint, FString() ); + Result.BrowserShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeBrowserShellState( + ActiveRecognitionSessionState.SessionConfig, + ActiveRecognitionSessionState.ActiveReconstructionSession, + Result.ObservedFace + ); } ActiveRecognitionSessionState.bHasPreviewResult = true; @@ -6911,6 +7007,11 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit Result.ObservedFace.FaceId, Result.CommittedUnit ); + Result.BrowserShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeBrowserShellState( + ActiveRecognitionSessionState.SessionConfig, + Result.ReconstructionSession, + Result.ObservedFace + ); } const FString ResolvedStage = !Result.CommittedUnit.IsEmpty() @@ -7861,6 +7962,28 @@ FHyperTwistVisionSessionConfig UHyperTwistTrainingSubsystem::BuildActiveRecognit { SessionConfig.ShellProfileDefinition.ShellProfileId = SessionConfig.ShellProfileId; } + if (SessionConfig.BrowserShellProfileId.IsEmpty() + && SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid()) + { + SessionConfig.BrowserShellProfileId = + SessionConfig.BrowserShellProfileDefinition.BrowserShellProfileId; + } + if (SessionConfig.BrowserShellProfileId.IsEmpty()) + { + SessionConfig.BrowserShellProfileId = TEXT("classic-cube-browser-shell-v1"); + } + if (!SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid()) + { + SessionConfig.BrowserShellProfileDefinition = + HyperTwistTrainingSubsystemInternal::BuildDefaultClassicCubeBrowserShellProfile( + SessionConfig.BrowserShellProfileId + ); + } + if (SessionConfig.BrowserShellProfileDefinition.BrowserShellProfileId.IsEmpty()) + { + SessionConfig.BrowserShellProfileDefinition.BrowserShellProfileId = + SessionConfig.BrowserShellProfileId; + } } return SessionConfig; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h index fff50cb..ec3601e 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h @@ -531,6 +531,133 @@ struct FHyperTwistVisionShellState } }; +USTRUCT(BlueprintType) +struct FHyperTwistVisionBrowserShellProfile +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BrowserShellProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TransportKind = TEXT("websocket"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TransportEndpoint = TEXT("ws://localhost:8090/"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CaptureIntervalMs = 100; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsManualCubeEdit = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSupportsPlaybackStage = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray OrderedStageIds; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ActionBindings; + + bool IsStructurallyValid() const + { + if (BrowserShellProfileId.IsEmpty() + || TransportKind.IsEmpty() + || TransportEndpoint.IsEmpty() + || CaptureIntervalMs <= 0 + || OrderedStageIds.Num() <= 0 + || ActionBindings.Num() <= 0) + { + return false; + } + + for (const FString& StageId : OrderedStageIds) + { + if (StageId.IsEmpty()) + { + return false; + } + } + + for (const FHyperTwistVisionShellActionBinding& ActionBinding : ActionBindings) + { + if (!ActionBinding.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionBrowserShellState +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BrowserShellProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveStageId = TEXT("home-page"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TransportKind = TEXT("websocket"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TransportEndpoint = TEXT("ws://localhost:8090/"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CaptureIntervalMs = 100; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CubeDimension = 3; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CommittedFaceCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ExpectedFaceCount = 6; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TargetFaceHint; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bTransportRouteReady = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bVideoStreamVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bManualCubeEditVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bManualCubeEditReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPlaybackDeferred = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSnapshotReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSessionRestartVisible = true; + + bool IsStructurallyValid() const + { + return !BrowserShellProfileId.IsEmpty() + && !ActiveStageId.IsEmpty() + && !TransportKind.IsEmpty() + && !TransportEndpoint.IsEmpty() + && CaptureIntervalMs > 0 + && CubeDimension > 0 + && CommittedFaceCount >= 0 + && ExpectedFaceCount > 0; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistVisionSessionConfig { @@ -566,6 +693,12 @@ struct FHyperTwistVisionSessionConfig UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistVisionShellProfile ShellProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BrowserShellProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionBrowserShellProfile BrowserShellProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FIntPoint FrameSize = FIntPoint(1280, 720); @@ -632,6 +765,9 @@ struct FHyperTwistVisionPreviewResult UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistVisionShellState ShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionBrowserShellState BrowserShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString Guidance; }; @@ -683,6 +819,9 @@ struct FHyperTwistVisionCommitResult UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistVisionShellState ShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionBrowserShellState BrowserShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") TArray Warnings; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RPBrowserShellContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RPBrowserShellContractTest.cpp new file mode 100644 index 0000000..f5e4395 --- /dev/null +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RPBrowserShellContractTest.cpp @@ -0,0 +1,250 @@ +// 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 HyperTwistRubixCubeSolverPhase6RPTestInternal +{ + FHyperTwistTrainingDeck MakeRecognitionDeck() + { + FHyperTwistTrainingDeck Deck; + Deck.DeckId = TEXT("phase6r-p/rubix-cube-solver-browser-shell"); + Deck.Title = TEXT("Phase 6R-P Rubix Cube Solver Browser Shell"); + Deck.DeliveryModes = { + EHyperTwistTrainingDeliveryMode::RecognitionAssisted, + EHyperTwistTrainingDeliveryMode::CoachReviewed + }; + + FHyperTwistTrainingCase TrainingCase; + TrainingCase.CaseId = TEXT("phase6r-p-case"); + TrainingCase.PuzzleId = TEXT("cube/3x3x3"); + TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition; + TrainingCase.PromptLabel = TEXT("Phase 6R-P 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-p-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( + FHyperTwistRubixCubeSolverPhase6RPBrowserShellSessionConfigTest, + "HyperTwist.Permissive.RubixCubeSolver.Phase6R.P.BrowserShellSessionConfig", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistRubixCubeSolverPhase6RPBrowserShellSessionConfigTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistRubixCubeSolverPhase6RPTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-p-config-session")); + TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-P."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + FString OpenError; + TestTrue(TEXT("The recognition session must open for the browser-shell 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 browser shell route must normalize the first-party browser profile id."), + SessionState.SessionConfig.BrowserShellProfileId, + TEXT("classic-cube-browser-shell-v1") + ); + TestTrue( + TEXT("The browser shell route must provide a structurally valid browser shell profile definition."), + SessionState.SessionConfig.BrowserShellProfileDefinition.IsStructurallyValid() + ); + TestEqual( + TEXT("The browser shell must retain three ordered browser stages."), + SessionState.SessionConfig.BrowserShellProfileDefinition.OrderedStageIds.Num(), + 3 + ); + TestEqual( + TEXT("The browser shell must retain four bounded browser actions."), + SessionState.SessionConfig.BrowserShellProfileDefinition.ActionBindings.Num(), + 4 + ); + TestEqual( + TEXT("The browser shell must keep the websocket transport kind."), + SessionState.SessionConfig.BrowserShellProfileDefinition.TransportKind, + TEXT("websocket") + ); + TestEqual( + TEXT("The browser shell must keep the local websocket endpoint."), + SessionState.SessionConfig.BrowserShellProfileDefinition.TransportEndpoint, + TEXT("ws://localhost:8090/") + ); + TestEqual( + TEXT("The browser shell must preserve the donor-like capture interval."), + SessionState.SessionConfig.BrowserShellProfileDefinition.CaptureIntervalMs, + 100 + ); + TestTrue( + TEXT("The browser shell must allow manual cube edit in the bounded route."), + SessionState.SessionConfig.BrowserShellProfileDefinition.bSupportsManualCubeEdit + ); + TestFalse( + TEXT("The browser shell must still defer playback-stage ownership in this packet."), + SessionState.SessionConfig.BrowserShellProfileDefinition.bSupportsPlaybackStage + ); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistRubixCubeSolverPhase6RPBrowserShellPreviewStateTest, + "HyperTwist.Permissive.RubixCubeSolver.Phase6R.P.BrowserShellPreviewState", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistRubixCubeSolverPhase6RPBrowserShellPreviewStateTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistRubixCubeSolverPhase6RPTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-p-preview-session")); + TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-P."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + + FHyperTwistVisionFrameEnvelope Frame; + Frame.TargetFaceHint = TEXT("F"); + const FHyperTwistVisionPreviewResult Result = TrainingSubsystem->SubmitActiveRecognitionFrame(Frame); + + TestTrue(TEXT("The preview result must carry a structurally valid browser shell state."), Result.BrowserShellState.IsStructurallyValid()); + TestEqual(TEXT("The preview browser shell must preserve the first-party profile id."), Result.BrowserShellState.BrowserShellProfileId, TEXT("classic-cube-browser-shell-v1")); + TestEqual(TEXT("The preview browser shell must stay in the video-stream stage."), Result.BrowserShellState.ActiveStageId, TEXT("video-stream")); + TestEqual(TEXT("The preview browser shell must keep the websocket transport kind."), Result.BrowserShellState.TransportKind, TEXT("websocket")); + TestEqual(TEXT("The preview browser shell must keep the local websocket endpoint."), Result.BrowserShellState.TransportEndpoint, TEXT("ws://localhost:8090/")); + TestEqual(TEXT("The preview browser shell must preserve the 100ms capture interval."), Result.BrowserShellState.CaptureIntervalMs, 100); + TestEqual(TEXT("The preview browser shell must preserve the classic cube dimension."), Result.BrowserShellState.CubeDimension, 3); + TestEqual(TEXT("The preview browser shell must keep the six-face expectation."), Result.BrowserShellState.ExpectedFaceCount, 6); + TestEqual(TEXT("The preview browser shell must report no committed faces yet."), Result.BrowserShellState.CommittedFaceCount, 0); + TestEqual(TEXT("The preview browser shell must preserve the current face hint."), Result.BrowserShellState.TargetFaceHint, TEXT("F")); + TestTrue(TEXT("The preview browser shell must keep the transport route ready."), Result.BrowserShellState.bTransportRouteReady); + TestTrue(TEXT("The preview browser shell must keep the video-stream surface visible."), Result.BrowserShellState.bVideoStreamVisible); + TestFalse(TEXT("The preview browser shell must keep manual edit hidden."), Result.BrowserShellState.bManualCubeEditVisible); + TestFalse(TEXT("The preview browser shell must keep manual edit unready."), Result.BrowserShellState.bManualCubeEditReady); + TestTrue(TEXT("The preview browser shell must keep playback deferred."), Result.BrowserShellState.bPlaybackDeferred); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistRubixCubeSolverPhase6RPBrowserShellCompleteSessionTest, + "HyperTwist.Permissive.RubixCubeSolver.Phase6R.P.BrowserShellCompleteSession", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistRubixCubeSolverPhase6RPBrowserShellCompleteSessionTest::RunTest(const FString& Parameters) +{ + UHyperTwistTrainingSubsystem* TrainingSubsystem = + HyperTwistRubixCubeSolverPhase6RPTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-p-complete-session")); + TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-P."), 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 = HyperTwistRubixCubeSolverPhase6RPTestInternal::CommitFace( + TrainingSubsystem, + FaceOrder[FaceOrdinal], + FaceOrdinal + 1 + ); + } + + TestTrue(TEXT("The completed browser-shell route must carry a structurally valid reconstruction session."), Result.ReconstructionSession.IsStructurallyValid()); + TestTrue(TEXT("The completed browser-shell route must carry a structurally valid browser shell state."), Result.BrowserShellState.IsStructurallyValid()); + TestTrue(TEXT("The completed reconstruction must expose a full classic cube net."), Result.ReconstructionSession.bHasCompleteClassicCubeNet); + TestEqual(TEXT("The browser shell must transition into cube-edit after a complete reconstruction."), Result.BrowserShellState.ActiveStageId, TEXT("cube-edit")); + TestEqual(TEXT("The browser shell must report six committed faces after a complete reconstruction."), Result.BrowserShellState.CommittedFaceCount, 6); + TestFalse(TEXT("The browser shell must hide the live video stream once manual correction is ready."), Result.BrowserShellState.bVideoStreamVisible); + TestTrue(TEXT("The browser shell must expose manual cube edit once the reconstruction is complete."), Result.BrowserShellState.bManualCubeEditVisible); + TestTrue(TEXT("The browser shell must mark manual cube edit ready once the reconstruction is complete."), Result.BrowserShellState.bManualCubeEditReady); + TestTrue(TEXT("The browser shell must keep playback deferred even after the reconstruction completes."), Result.BrowserShellState.bPlaybackDeferred); + return true; +} + +#endif diff --git a/docs/REPO_LICENSE_TRACKING.md b/docs/REPO_LICENSE_TRACKING.md index 27c7340..384d104 100644 --- a/docs/REPO_LICENSE_TRACKING.md +++ b/docs/REPO_LICENSE_TRACKING.md @@ -151,9 +151,11 @@ Status update on `2026-05-21`: - the bounded permissive `Phase 6R-N` `HactarCE/Hyperspeedcube` puzzle-definition DSL packet is now landed in current code - `HactarCE/Hyperspeedcube` is now closed for the currently justified retained families -- the current next bounded move is a source-backed `Phase 6R-P` - `vivaansinghvi07/rubix-cube-solver` browser/webcam shell preparation/control pass, not a new - restrictive packet by default +- 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 - use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A` ownership contract for the current queue after that correction @@ -257,9 +259,9 @@ 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-P` - `vivaansinghvi07/rubix-cube-solver` preparation/control pass for a bounded browser/webcam shell - seam +- 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 Companion docs: @@ -796,12 +798,12 @@ Approved working posture: - preserve the row as the bounded recognition companion lane after `kkoomen/qbr` - if later implementation ships or copies bundled third-party frontend assets, capture and preserve those assets' own upstream license/provenance rather than flattening them into the repo-level `MIT` -- the first bounded retained slice is now landed in current code: +- the first two bounded retained slices are now landed in current code: - 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 - keep the row only partially incorporated by default: - - browser/webcam shell remains deferred - broad solve explanation or recommendation shell 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_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md b/docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md new file mode 100644 index 0000000..0295372 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md @@ -0,0 +1,131 @@ +# HyperTwist Phase 6R-P rubix-cube-solver browser/webcam shell implementation packet + +Created on `2026-05-22` + +## Status + +- first-party HyperTwist packet +- bounded permissive `Phase 6R-P` implementation slice + +## Purpose + +This packet lands the current retained `rubix-cube-solver` browser/webcam shell family: + +- first-party classic-cube browser shell profile and browser-shell session-state composition above + the already landed reconstruction seam + +It is not: + +- a full `rubix-cube-solver` row transplant +- a broad solve explanation or recommendation packet +- a bundled `twistysim.min.js` redistribution packet +- a generalized solver backend 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_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_PREPARATION_PACKET_2026-05-22.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 / F2` + browser/webcam shell slice + +## Landed scope + +The current code now owns a retained `rubix-cube-solver` browser/webcam shell contract through: + +- first-party `HyperTwistRecognition` contract types for: + - browser shell profile definition + - browser shell session-state readout + - browser shell profile id on recognition session config + - preview and commit browser-shell result payloads +- recognition session config defaults for: + - first-party classic-cube browser shell profile id + - browser shell profile definition +- first-party preview and commit result shaping for: + - active browser stage id + - transport route readiness + - transport endpoint and capture cadence + - committed-face count + - target-face hint + - snapshot readiness + - video/manual-edit visibility + - playback deferred state +- sample contract routing in: + - `UHyperTwistContractLibrary` +- active subsystem normalization in: + - `UHyperTwistTrainingSubsystem` +- focused automation coverage in: + - `HyperTwistRubixCubeSolverPhase6RPBrowserShellContractTest.cpp` + +## Why this is still intentionally bounded + +This packet lands the bounded browser/webcam shell seam, but it does not widen into neighboring +families or broader ownership. + +Still excluded: + +- broad solve explanation or recommendation ownership +- bundled `twistysim.min.js` redistribution +- generalized solver backend 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` + +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-P-Rubix-Verify.log -ReportExportPath=C:\HyperTwist\UnrealHyperTwist\Saved\AutomationReports\Phase6R-P-Rubix-Verify -ExecCmds="Automation RunTests HyperTwist.Permissive.RubixCubeSolver.Phase6R.P; Quit" -TestExit="Automation Test Queue Empty"` + +Regression automation validation: + +- `HyperTwist.Permissive.Qbr.Phase6R.O` +- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.C` +- `HyperTwist.Permissive.Hyperspeedcube.Phase6R.N` +- `HyperTwist.CleanRoom.CubeDesk` + +Expected covered tests: + +- `BrowserShellSessionConfig` +- `BrowserShellPreviewState` +- `BrowserShellCompleteSession` +- existing `qbr`, `rubix-cube-solver`, `Hyperspeedcube`, and `CubeDesk` regression suites + +## Queue effect + +This packet consumes the current `Phase 6R-P` 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 +- still deferred: + - broad solve explanation or recommendation shell + - bundled `twistysim.min.js` redistribution + +The next clean move is: + +- source-backed `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` solve + explanation/recommendation preparation/control pass + +Keep the future sequencing 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 diff --git a/docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_PREPARATION_PACKET_2026-05-22.md b/docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_PREPARATION_PACKET_2026-05-22.md new file mode 100644 index 0000000..c6523db --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_PREPARATION_PACKET_2026-05-22.md @@ -0,0 +1,198 @@ +# HyperTwist Phase 6R-P rubix-cube-solver browser/webcam shell preparation packet + +Created on `2026-05-22` + +## Status + +- historical consumed preparation authority +- bounded post-`Phase 6R-O` 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-O` `kkoomen/qbr` webcam UI +shell slice. + +The open task was: + +- define the first bounded source-backed widening packet for `vivaansinghvi07/rubix-cube-solver` + as the retained browser/webcam shell above the already landed reconstruction seam + +It is not: + +- a full `rubix-cube-solver` row transplant +- a broad solve explanation or recommendation packet +- a bundled `twistysim.min.js` redistribution packet +- a generalized solver backend packet +- a `qbr` multilingual shell packet +- a provider-backed recognition platform 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` + +The key accepted routing facts are: + +- `vivaansinghvi07/rubix-cube-solver` remains the retained recognition-companion donor for the + bounded browser-assisted recognition shell 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-O` slice owns: + - classic-cube webcam overlay shell + - bounded preview/commit shell-state readout +- the donor value now strongest for the next narrower slice is: + - browser session stage routing + - websocket transport session semantics + - frame-capture cadence semantics + - manual cube-edit handoff semantics +- ownership denied there is: + - do not widen into bundled `twistysim.min.js` redistribution + - do not widen into broad solve explanation or recommendation ownership + - do not widen into generalized solver backend ownership + - do not let the row absorb `qbr` multilingual or bundled-font ownership + +## 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` + +That advanced the live queue to: + +- `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` + +with the adjacent retained recognition remainder ordered behind it: + +- `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` + +## Required result + +The source-backed control pass for this packet is now complete. + +The first actual `6R-P` implementation packet should: + +- define one bounded retained browser/webcam shell slice from `rubix-cube-solver` +- keep the slice inside the accepted recognition-shell seam above the landed reconstruction session +- widen only the first browser session-state family that can stand on its own without dragging in + bundled frontend-asset redistribution or broad recommendation 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/HyperTwistQbrPhase6ROWebcamShellContractTest.cpp` + +## Narrowed 6R-P slice decision + +The first widening slice is fixed as: + +1. classic-cube browser/webcam shell profile and browser-shell session-state composition + +not as the broader solve explanation shell. + +That narrowed slice covers: + +- first-party browser shell profile for: + - websocket transport kind and endpoint + - browser capture interval + - ordered stage ids: + - `home-page` + - `video-stream` + - `cube-edit` + - bounded action bindings: + - `start-browser-capture` + - `finish-browser-capture` + - `apply-manual-corrections` + - `restart-browser-session` +- first-party browser shell state readout for: + - committed-face count + - expected-face count + - target-face hint + - transport route readiness + - snapshot readiness + - video/manual-edit visibility + - playback deferred state +- subsystem normalization and focused automation coverage + +## Deferred neighboring families + +The first `6R-P` implementation packet must keep these capability families closed: + +- broad solve explanation or recommendation shell +- bundled `twistysim.min.js` redistribution +- generalized solver backend ownership +- `qbr` multilingual shell ownership +- `qbr` bundled-font redistribution +- provider-backed recognition platform widening + +## Why this slice was first + +- `rubix-cube-solver` is strongest for the browser-assisted session-state shell that sits directly + above the already landed reconstruction seam +- current first-party recognition code already had the session, preview, commit, reconstruction, + and bounded `qbr` shell envelopes, but it did not yet own a richer browser-stage profile and + browser-shell session state for that route +- widening into broad solve explanation or bundled frontend-asset redistribution would prematurely + absorb adjacent legal and routing concerns that are still narrower and separately governed + +## Out of scope for the first 6R-P packet + +- bundled `twistysim.min.js` shipping +- broad solve explanation or recommendation ownership +- generalized solver backend ownership +- `qbr` multilingual or bundled-font ownership +- broad playback runtime ownership + +## Acceptance criteria + +- the packet records why the live queue advanced to `6R-P` +- the packet records that the first `6R-P` widening slice is browser/webcam shell only +- the packet states exact out-of-scope families for the first `6R-P` pass +- the packet keeps the `rubix-cube-solver` frontend-asset guard visible +- the packet frames the next widening slice as a separate `6R-Q` explanation/recommendation pass + +## Validation checklist + +1. confirm the retained `rubix-cube-solver` seam narrows cleanly to a browser/webcam shell profile + and session-state contract +2. confirm the next implementation packet is framed as bounded source-backed widening rather than a + donor frontend transplant +3. confirm bundled frontend-asset redistribution and broad explanation widening 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 fdc9f19..a457bd8 100644 --- a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md +++ b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md @@ -96,11 +96,13 @@ The next bounded move is now: implementation packet is now landed in current code 5. the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in current code -6. source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell - preparation/control pass is now the next bounded move -7. keep `Phase 6R-P` narrower than bundled `twistysim.min.js` redistribution and broader solve - explanation/recommendation ownership in the same packet -8. keep the `qbr` guard visible: +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 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 f3a291d..eb00997 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md @@ -109,6 +109,7 @@ Do not collapse those three tiers into one undifferentiated "features" voice. | Classic-cube calibration and ordered face observation | Implemented now | landed `qbr` packet | Calibration contract and ordered face-observation contract are live. | | 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. | | 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. | @@ -144,10 +145,10 @@ repo. | Calibration profile and ordered face observation | Implemented now | `qbr` bounded packet | Live bounded recognition intake. | | 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. | | 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. | -| Browser recognition shell | Deep-source grounded retained | `rubix-cube-solver` retained remainder | Retained, not shipped. | ### 3. Training, coaching, and progression cockpit diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md index 2a4e59b..8f9a034 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -70,8 +70,12 @@ Canonical discovery surfaces for roadmap interpretation: consumed - the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face reconstruction and face-vote replacement packet is now landed in current code -- `vivaansinghvi07/rubix-cube-solver` remains partially incorporated; browser/webcam shell, broad - solve explanation, and bundled `twistysim.min.js` redistribution stay deferred +- the generic source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` control pass is now + consumed +- the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell + packet is now landed in current code +- `vivaansinghvi07/rubix-cube-solver` remains partially incorporated; broad solve explanation and + bundled `twistysim.min.js` redistribution stay deferred - the generic source-backed `Phase 6R-D` `roice3/MagicTile` control pass is now consumed - the bounded permissive `Phase 6R-D` `roice3/MagicTile` tiling topology and geometry-family packet is now landed in current code @@ -118,8 +122,11 @@ Canonical discovery surfaces for roadmap interpretation: - `HactarCE/Hyperspeedcube` is now closed for the currently justified retained families - the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in current code -- the current next bounded move is a source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` - browser/webcam shell preparation/control pass for one bounded recognition-shell widening slice +- 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 repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than waiting on another first-party packet @@ -181,8 +188,8 @@ 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-P` -`vivaansinghvi07/rubix-cube-solver` browser/webcam shell preparation/control pass. +The next bounded move is a source-backed `Phase 6R-Q` +`vivaansinghvi07/rubix-cube-solver` solve explanation/recommendation preparation/control pass. Queue interpretation after that packet: @@ -234,11 +241,11 @@ Queue interpretation after that packet: - 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 - still deferred: - - browser/webcam shell - broad solve explanation or recommendation shell - bundled `twistysim.min.js` redistribution -- after the `Phase 6R-C` implementation packet, keep the future legal sequencing guard visible: +- after the `Phase 6R-P` implementation packet, keep the future legal sequencing guard 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 @@ -306,10 +313,10 @@ Queue interpretation after that packet: - viseme / gesture runtime integration - broad assistant-platform scope - the next queue shape is now: - - `vivaansinghvi07/rubix-cube-solver` deferred browser/webcam shell remainder + - `vivaansinghvi07/rubix-cube-solver` deferred solve explanation / recommendation remainder - the next bounded packet should stay narrow: - - browser/webcam shell preparation/control before widening into bundled `twistysim.min.js` - redistribution or broad solve explanation/recommendation ownership + - solve explanation/recommendation preparation/control before widening into bundled + `twistysim.min.js` redistribution or generalized solver backend ownership - 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: