From 1237daa434db29210d58431862019c0afd005ed9 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Fri, 22 May 2026 20:38:11 +0200 Subject: [PATCH] Implement Phase 6R-O qbr webcam shell --- .../HyperTwistContractLibrary.cpp | 114 +++++++ .../HyperTwistTrainingSubsystem.cpp | 142 +++++++++ .../HyperTwistRecognitionTypes.h | 168 ++++++++++ ...wistQbrPhase6ROWebcamShellContractTest.cpp | 299 ++++++++++++++++++ docs/REPO_LICENSE_TRACKING.md | 16 +- ..._SHELL_IMPLEMENTATION_PACKET_2026-05-22.md | 130 ++++++++ ..._UI_SHELL_PREPARATION_PACKET_2026-05-22.md | 190 +++++++++++ ...PERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md | 13 +- .../HyperTwist/FEATURE_REGISTRY.md | 5 +- .../HyperTwist/ROADMAP.md | 27 +- 10 files changed, 1082 insertions(+), 22 deletions(-) create mode 100644 UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistQbrPhase6ROWebcamShellContractTest.cpp create mode 100644 docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md create mode 100644 docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_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 83101ae..7d0274b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -239,6 +239,59 @@ namespace HyperTwistContractLibraryInternal return Profile; } + FHyperTwistVisionShellProfile MakeDefaultClassicCubeWebcamShellProfile(const FString& ProfileId) + { + FHyperTwistVisionShellProfile Profile; + Profile.ShellProfileId = ProfileId; + Profile.ShellKind = TEXT("webcam-overlay"); + Profile.LocaleRoutingMode = TEXT("first-party-locale-cycle"); + + auto AddPanel = [&Profile]( + const TCHAR* PanelId, + const TCHAR* PanelKind, + const TCHAR* AnchorId, + const int32 GridRows, + const int32 GridColumns, + const bool bVisibleByDefault + ) + { + FHyperTwistVisionShellPanelLayout Panel; + Panel.PanelId = PanelId; + Panel.PanelKind = PanelKind; + Panel.AnchorId = AnchorId; + Panel.GridRows = GridRows; + Panel.GridColumns = GridColumns; + Panel.bVisibleByDefault = bVisibleByDefault; + Profile.Panels.Add(Panel); + }; + + 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); + }; + + AddPanel(TEXT("preview-sticker-grid"), TEXT("sticker-grid"), TEXT("top-left"), 3, 3, true); + AddPanel(TEXT("snapshot-sticker-grid"), TEXT("sticker-grid"), TEXT("left-stack-below-preview"), 3, 3, true); + AddPanel(TEXT("scanned-side-counter"), TEXT("status-readout"), TEXT("bottom-left"), 0, 0, true); + AddPanel(TEXT("classic-cube-net"), TEXT("cube-net"), TEXT("bottom-right"), 3, 4, true); + AddPanel(TEXT("locale-indicator"), TEXT("status-readout"), TEXT("top-right"), 0, 0, true); + AddPanel(TEXT("calibration-swatch-rail"), TEXT("calibration-rail"), TEXT("top-left-offset"), 6, 1, false); + + AddAction(TEXT("capture-snapshot"), TEXT("Space"), TEXT("snapshot-sticker-grid")); + AddAction(TEXT("toggle-calibration"), TEXT("C"), TEXT("calibration-swatch-rail")); + AddAction(TEXT("cycle-locale"), TEXT("L"), TEXT("locale-indicator")); + AddAction(TEXT("close-session"), TEXT("Escape"), TEXT("session-root")); + return Profile; + } + void ResolveMockClassicFaceSwatch( const FString& FaceId, FString& OutColorId, @@ -402,6 +455,50 @@ namespace HyperTwistContractLibraryInternal Session.bHasCompleteClassicCubeNet = Session.MissingFaces.Num() == 0; return Session; } + + FHyperTwistVisionShellState MakeClassicCubeWebcamShellState( + const FHyperTwistVisionSessionConfig& SessionConfig, + const FHyperTwistVisionReconstructionSession& ReconstructionSession, + const FString& PreferredTargetFaceHint, + const FString& PreferredCommittedFaceId + ) + { + FHyperTwistVisionShellState ShellState; + ShellState.ShellProfileId = !SessionConfig.ShellProfileId.IsEmpty() + ? SessionConfig.ShellProfileId + : TEXT("classic-cube-webcam-shell-v1"); + ShellState.LocaleCode = TEXT("en"); + ShellState.TargetSideCount = 6; + ShellState.TargetFaceHint = !PreferredTargetFaceHint.IsEmpty() ? PreferredTargetFaceHint : TEXT("F"); + ShellState.LastCommittedFaceId = PreferredCommittedFaceId; + ShellState.CalibrationProfileId = !SessionConfig.CalibrationProfile.IsEmpty() + ? SessionConfig.CalibrationProfile + : SessionConfig.CalibrationProfileDefinition.ProfileId; + + if (ReconstructionSession.IsStructurallyValid()) + { + ShellState.ScannedSideCount = FMath::Max(0, ReconstructionSession.TotalCommittedFaceCount); + for (const FHyperTwistVisionCommittedFaceState& FaceState : ReconstructionSession.CommittedFaces) + { + if (!FaceState.FaceId.IsEmpty()) + { + ShellState.CapturedFaceIds.Add(FaceState.FaceId); + } + } + + if (ShellState.TargetFaceHint.IsEmpty() && ReconstructionSession.MissingFaces.Num() > 0) + { + ShellState.TargetFaceHint = ReconstructionSession.MissingFaces[0]; + } + + if (ShellState.LastCommittedFaceId.IsEmpty() && ReconstructionSession.CommittedFaces.Num() > 0) + { + ShellState.LastCommittedFaceId = ReconstructionSession.CommittedFaces.Last().FaceId; + } + } + + return ShellState; + } } FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition() @@ -631,6 +728,11 @@ FHyperTwistVisionSessionConfig UHyperTwistContractLibrary::MakeSampleVisionSessi HyperTwistContractLibraryInternal::MakeDefaultClassicCubeCalibrationProfile( Config.CalibrationProfile ); + Config.ShellProfileId = TEXT("classic-cube-webcam-shell-v1"); + Config.ShellProfileDefinition = + HyperTwistContractLibraryInternal::MakeDefaultClassicCubeWebcamShellProfile( + Config.ShellProfileId + ); Config.FrameSize = FIntPoint(1280, 720); Config.MaxFrameRate = 30; return Config; @@ -651,6 +753,12 @@ FHyperTwistVisionPreviewResult UHyperTwistContractLibrary::MakeMockVisionPreview 0.84f, false ); + Result.ShellState = HyperTwistContractLibraryInternal::MakeClassicCubeWebcamShellState( + UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(), + FHyperTwistVisionReconstructionSession(), + Result.ObservedFace.FaceId, + FString() + ); Result.Guidance = TEXT("capture-ready"); return Result; } @@ -679,6 +787,12 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe {TEXT("F")}, Result.Confidence ); + Result.ShellState = HyperTwistContractLibraryInternal::MakeClassicCubeWebcamShellState( + UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(), + Result.ReconstructionSession, + Result.ObservedFace.FaceId, + Result.CommittedUnit + ); return Result; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index 7e240ad..3b301c9 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -2383,6 +2383,110 @@ namespace HyperTwistTrainingSubsystemInternal return Profile; } + FHyperTwistVisionShellProfile BuildDefaultClassicCubeWebcamShellProfile(const FString& ProfileId) + { + FHyperTwistVisionShellProfile Profile; + Profile.ShellProfileId = ProfileId; + Profile.ShellKind = TEXT("webcam-overlay"); + Profile.LocaleRoutingMode = TEXT("first-party-locale-cycle"); + + auto AddPanel = [&Profile]( + const TCHAR* PanelId, + const TCHAR* PanelKind, + const TCHAR* AnchorId, + const int32 GridRows, + const int32 GridColumns, + const bool bVisibleByDefault + ) + { + FHyperTwistVisionShellPanelLayout Panel; + Panel.PanelId = PanelId; + Panel.PanelKind = PanelKind; + Panel.AnchorId = AnchorId; + Panel.GridRows = GridRows; + Panel.GridColumns = GridColumns; + Panel.bVisibleByDefault = bVisibleByDefault; + Profile.Panels.Add(Panel); + }; + + 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); + }; + + AddPanel(TEXT("preview-sticker-grid"), TEXT("sticker-grid"), TEXT("top-left"), 3, 3, true); + AddPanel(TEXT("snapshot-sticker-grid"), TEXT("sticker-grid"), TEXT("left-stack-below-preview"), 3, 3, true); + AddPanel(TEXT("scanned-side-counter"), TEXT("status-readout"), TEXT("bottom-left"), 0, 0, true); + AddPanel(TEXT("classic-cube-net"), TEXT("cube-net"), TEXT("bottom-right"), 3, 4, true); + AddPanel(TEXT("locale-indicator"), TEXT("status-readout"), TEXT("top-right"), 0, 0, true); + AddPanel(TEXT("calibration-swatch-rail"), TEXT("calibration-rail"), TEXT("top-left-offset"), 6, 1, false); + + AddAction(TEXT("capture-snapshot"), TEXT("Space"), TEXT("snapshot-sticker-grid")); + AddAction(TEXT("toggle-calibration"), TEXT("C"), TEXT("calibration-swatch-rail")); + AddAction(TEXT("cycle-locale"), TEXT("L"), TEXT("locale-indicator")); + AddAction(TEXT("close-session"), TEXT("Escape"), TEXT("session-root")); + return Profile; + } + + FHyperTwistVisionShellState BuildClassicCubeWebcamShellState( + const FHyperTwistVisionSessionConfig& SessionConfig, + const FHyperTwistVisionReconstructionSession& ReconstructionSession, + const FString& PreferredTargetFaceHint, + const FString& PreferredCommittedFaceId + ) + { + FHyperTwistVisionShellState ShellState; + ShellState.ShellProfileId = !SessionConfig.ShellProfileId.IsEmpty() + ? SessionConfig.ShellProfileId + : TEXT("classic-cube-webcam-shell-v1"); + ShellState.LocaleCode = TEXT("en"); + ShellState.TargetSideCount = 6; + ShellState.TargetFaceHint = !PreferredTargetFaceHint.IsEmpty() + ? NormalizeRecognitionFaceId(PreferredTargetFaceHint) + : TEXT("F"); + ShellState.LastCommittedFaceId = !PreferredCommittedFaceId.IsEmpty() + ? NormalizeRecognitionFaceId(PreferredCommittedFaceId) + : FString(); + ShellState.CalibrationProfileId = !SessionConfig.CalibrationProfile.IsEmpty() + ? SessionConfig.CalibrationProfile + : SessionConfig.CalibrationProfileDefinition.ProfileId; + + if (ReconstructionSession.IsStructurallyValid()) + { + ShellState.ScannedSideCount = FMath::Max(0, ReconstructionSession.TotalCommittedFaceCount); + for (const FHyperTwistVisionCommittedFaceState& FaceState : ReconstructionSession.CommittedFaces) + { + const FString FaceId = NormalizeRecognitionFaceId(FaceState.FaceId); + if (!FaceId.IsEmpty()) + { + ShellState.CapturedFaceIds.Add(FaceId); + } + } + + if ((PreferredTargetFaceHint.IsEmpty() || ShellState.TargetFaceHint.IsEmpty()) + && ReconstructionSession.MissingFaces.Num() > 0) + { + ShellState.TargetFaceHint = NormalizeRecognitionFaceId(ReconstructionSession.MissingFaces[0]); + } + + if (ShellState.LastCommittedFaceId.IsEmpty() && ReconstructionSession.CommittedFaces.Num() > 0) + { + ShellState.LastCommittedFaceId = + NormalizeRecognitionFaceId(ReconstructionSession.CommittedFaces.Last().FaceId); + } + } + + return ShellState; + } + EHyperTwistPuzzleFamily ResolvePuzzleFamilyFromPuzzleId(const FString& PuzzleId) { FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; @@ -6659,6 +6763,15 @@ FHyperTwistVisionPreviewResult UHyperTwistTrainingSubsystem::SubmitActiveRecogni { Result.PreviewState.State.SourceSessionId = NormalizedFrame.SessionId; } + if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig)) + { + Result.ShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeWebcamShellState( + ActiveRecognitionSessionState.SessionConfig, + ActiveRecognitionSessionState.ActiveReconstructionSession, + !Result.ObservedFace.FaceId.IsEmpty() ? Result.ObservedFace.FaceId : NormalizedFrame.TargetFaceHint, + FString() + ); + } ActiveRecognitionSessionState.bHasPreviewResult = true; ActiveRecognitionSessionState.LastPreviewResult = Result; @@ -6790,6 +6903,15 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit Result.Snapshot = FHyperTwistStateSnapshot(); } } + if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig)) + { + Result.ShellState = HyperTwistTrainingSubsystemInternal::BuildClassicCubeWebcamShellState( + ActiveRecognitionSessionState.SessionConfig, + Result.ReconstructionSession, + Result.ObservedFace.FaceId, + Result.CommittedUnit + ); + } const FString ResolvedStage = !Result.CommittedUnit.IsEmpty() ? Result.CommittedUnit @@ -7719,6 +7841,26 @@ FHyperTwistVisionSessionConfig UHyperTwistTrainingSubsystem::BuildActiveRecognit { SessionConfig.CalibrationProfileDefinition.ProfileId = SessionConfig.CalibrationProfile; } + if (SessionConfig.ShellProfileId.IsEmpty() + && SessionConfig.ShellProfileDefinition.IsStructurallyValid()) + { + SessionConfig.ShellProfileId = SessionConfig.ShellProfileDefinition.ShellProfileId; + } + if (SessionConfig.ShellProfileId.IsEmpty()) + { + SessionConfig.ShellProfileId = TEXT("classic-cube-webcam-shell-v1"); + } + if (!SessionConfig.ShellProfileDefinition.IsStructurallyValid()) + { + SessionConfig.ShellProfileDefinition = + HyperTwistTrainingSubsystemInternal::BuildDefaultClassicCubeWebcamShellProfile( + SessionConfig.ShellProfileId + ); + } + if (SessionConfig.ShellProfileDefinition.ShellProfileId.IsEmpty()) + { + SessionConfig.ShellProfileDefinition.ShellProfileId = SessionConfig.ShellProfileId; + } } return SessionConfig; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h index e39e187..fff50cb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h @@ -375,6 +375,162 @@ struct FHyperTwistVisionReconstructionSession } }; +USTRUCT(BlueprintType) +struct FHyperTwistVisionShellActionBinding +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString InputBinding; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SurfaceId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bEnabled = true; + + bool IsStructurallyValid() const + { + return !ActionId.IsEmpty() && !InputBinding.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionShellPanelLayout +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PanelId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PanelKind; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString AnchorId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GridRows = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GridColumns = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bVisibleByDefault = true; + + bool IsStructurallyValid() const + { + return !PanelId.IsEmpty() && !PanelKind.IsEmpty() && !AnchorId.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionShellProfile +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ShellProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ShellKind = TEXT("webcam-overlay"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LocaleRoutingMode = TEXT("first-party-locale-cycle"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Panels; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray ActionBindings; + + bool IsStructurallyValid() const + { + if (ShellProfileId.IsEmpty() || ShellKind.IsEmpty() || Panels.Num() <= 0 || ActionBindings.Num() <= 0) + { + return false; + } + + for (const FHyperTwistVisionShellPanelLayout& Panel : Panels) + { + if (!Panel.IsStructurallyValid()) + { + return false; + } + } + + for (const FHyperTwistVisionShellActionBinding& ActionBinding : ActionBindings) + { + if (!ActionBinding.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionShellState +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ShellProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LocaleCode = TEXT("en"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ScannedSideCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TargetSideCount = 6; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TargetFaceHint; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LastCommittedFaceId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CalibrationProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreviewPanelVisible = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSnapshotPanelVisible = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bScannedSideCounterVisible = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCubeNetVisible = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bLocaleIndicatorVisible = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCalibrationRailVisible = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray CapturedFaceIds; + + bool IsStructurallyValid() const + { + return !ShellProfileId.IsEmpty() + && !LocaleCode.IsEmpty() + && !CalibrationProfileId.IsEmpty() + && ScannedSideCount >= 0 + && TargetSideCount > 0; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistVisionSessionConfig { @@ -404,6 +560,12 @@ struct FHyperTwistVisionSessionConfig UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistVisionCalibrationProfile CalibrationProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ShellProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionShellProfile ShellProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FIntPoint FrameSize = FIntPoint(1280, 720); @@ -467,6 +629,9 @@ struct FHyperTwistVisionPreviewResult UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistVisionFaceObservation ObservedFace; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionShellState ShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString Guidance; }; @@ -515,6 +680,9 @@ struct FHyperTwistVisionCommitResult UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistVisionReconstructionSession ReconstructionSession; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionShellState ShellState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") TArray Warnings; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistQbrPhase6ROWebcamShellContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistQbrPhase6ROWebcamShellContractTest.cpp new file mode 100644 index 0000000..2718d86 --- /dev/null +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistQbrPhase6ROWebcamShellContractTest.cpp @@ -0,0 +1,299 @@ +// 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 HyperTwistQbrPhase6ROTestInternal +{ + FHyperTwistTrainingDeck MakeRecognitionDeck() + { + FHyperTwistTrainingDeck Deck; + Deck.DeckId = TEXT("phase6r-o/qbr-webcam-shell"); + Deck.Title = TEXT("Phase 6R-O Qbr Webcam Shell"); + Deck.DeliveryModes = { + EHyperTwistTrainingDeliveryMode::RecognitionAssisted, + EHyperTwistTrainingDeliveryMode::CoachReviewed + }; + + FHyperTwistTrainingCase TrainingCase; + TrainingCase.CaseId = TEXT("phase6r-o-case"); + TrainingCase.PuzzleId = TEXT("cube/3x3x3"); + TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition; + TrainingCase.PromptLabel = TEXT("Phase 6R-O 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")); + } + } + + const FHyperTwistVisionShellPanelLayout* FindPanel( + const FHyperTwistVisionShellProfile& Profile, + const FString& PanelId + ) + { + return Profile.Panels.FindByPredicate( + [&PanelId](const FHyperTwistVisionShellPanelLayout& Candidate) + { + return Candidate.PanelId == PanelId; + } + ); + } + + const FHyperTwistVisionShellActionBinding* FindAction( + const FHyperTwistVisionShellProfile& Profile, + const FString& ActionId + ) + { + return Profile.ActionBindings.FindByPredicate( + [&ActionId](const FHyperTwistVisionShellActionBinding& Candidate) + { + return Candidate.ActionId == ActionId; + } + ); + } +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistQbrPhase6ROWebcamShellSessionConfigTest, + "HyperTwist.Permissive.Qbr.Phase6R.O.WebcamShellSessionConfig", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistQbrPhase6ROWebcamShellSessionConfigTest::RunTest(const FString& Parameters) +{ + UGameInstance* GameInstance = NewObject(GetTransientPackage()); + TestNotNull(TEXT("Game instance outer must exist for subsystem construction."), GameInstance); + if (GameInstance == nullptr) + { + return false; + } + + UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject(GameInstance); + TestNotNull(TEXT("Training subsystem must be constructed."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + HyperTwistQbrPhase6ROTestInternal::ForceMockRecognitionClient(TrainingSubsystem); + + const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck( + HyperTwistQbrPhase6ROTestInternal::MakeRecognitionDeck(), + TEXT("phase6r-o-user"), + TEXT("phase6r-o-session"), + EHyperTwistTrainingDeliveryMode::RecognitionAssisted + ); + TestTrue(TEXT("The recognition-assisted run must start successfully."), RunState.IsStructurallyValid()); + if (!RunState.IsStructurallyValid()) + { + return false; + } + + FString OpenError; + TestTrue(TEXT("The recognition session must open for the qbr 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 qbr webcam shell route must normalize the first-party shell profile id."), + SessionState.SessionConfig.ShellProfileId, + TEXT("classic-cube-webcam-shell-v1") + ); + TestTrue( + TEXT("The qbr webcam shell route must provide a structurally valid shell profile definition."), + SessionState.SessionConfig.ShellProfileDefinition.IsStructurallyValid() + ); + TestEqual( + TEXT("The shell profile must retain six bounded panels."), + SessionState.SessionConfig.ShellProfileDefinition.Panels.Num(), + 6 + ); + TestEqual( + TEXT("The shell profile must retain four bounded action bindings."), + SessionState.SessionConfig.ShellProfileDefinition.ActionBindings.Num(), + 4 + ); + + const FHyperTwistVisionShellPanelLayout* PreviewPanel = + HyperTwistQbrPhase6ROTestInternal::FindPanel( + SessionState.SessionConfig.ShellProfileDefinition, + TEXT("preview-sticker-grid") + ); + TestNotNull(TEXT("The preview sticker grid panel must be present."), PreviewPanel); + if (PreviewPanel != nullptr) + { + TestEqual(TEXT("The preview sticker grid must stay anchored top-left."), PreviewPanel->AnchorId, TEXT("top-left")); + TestEqual(TEXT("The preview sticker grid must retain a 3x3 shape."), PreviewPanel->GridRows, 3); + } + + const FHyperTwistVisionShellPanelLayout* CubeNetPanel = + HyperTwistQbrPhase6ROTestInternal::FindPanel( + SessionState.SessionConfig.ShellProfileDefinition, + TEXT("classic-cube-net") + ); + TestNotNull(TEXT("The classic cube net panel must be present."), CubeNetPanel); + if (CubeNetPanel != nullptr) + { + TestEqual(TEXT("The cube net must stay anchored bottom-right."), CubeNetPanel->AnchorId, TEXT("bottom-right")); + TestEqual(TEXT("The cube net must retain the bounded 3x4 layout."), CubeNetPanel->GridColumns, 4); + } + + const FHyperTwistVisionShellActionBinding* SnapshotAction = + HyperTwistQbrPhase6ROTestInternal::FindAction( + SessionState.SessionConfig.ShellProfileDefinition, + TEXT("capture-snapshot") + ); + TestNotNull(TEXT("The snapshot action binding must be present."), SnapshotAction); + if (SnapshotAction != nullptr) + { + TestEqual(TEXT("The snapshot action must keep the Space binding."), SnapshotAction->InputBinding, TEXT("Space")); + } + + const FHyperTwistVisionShellActionBinding* LocaleAction = + HyperTwistQbrPhase6ROTestInternal::FindAction( + SessionState.SessionConfig.ShellProfileDefinition, + TEXT("cycle-locale") + ); + TestNotNull(TEXT("The locale-cycle action binding must be present."), LocaleAction); + if (LocaleAction != nullptr) + { + TestEqual(TEXT("The locale-cycle action must keep the L binding."), LocaleAction->InputBinding, TEXT("L")); + } + + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistQbrPhase6ROWebcamShellPreviewStateTest, + "HyperTwist.Permissive.Qbr.Phase6R.O.WebcamShellPreviewState", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistQbrPhase6ROWebcamShellPreviewStateTest::RunTest(const FString& Parameters) +{ + UGameInstance* GameInstance = NewObject(GetTransientPackage()); + TestNotNull(TEXT("Game instance outer must exist for subsystem construction."), GameInstance); + if (GameInstance == nullptr) + { + return false; + } + + UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject(GameInstance); + TestNotNull(TEXT("Training subsystem must be constructed."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + HyperTwistQbrPhase6ROTestInternal::ForceMockRecognitionClient(TrainingSubsystem); + + const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck( + HyperTwistQbrPhase6ROTestInternal::MakeRecognitionDeck(), + TEXT("phase6r-o-user"), + TEXT("phase6r-o-preview-session"), + EHyperTwistTrainingDeliveryMode::RecognitionAssisted + ); + TestTrue(TEXT("The recognition-assisted run must start successfully."), RunState.IsStructurallyValid()); + if (!RunState.IsStructurallyValid()) + { + return false; + } + + FHyperTwistVisionFrameEnvelope Frame; + Frame.TargetFaceHint = TEXT("F"); + const FHyperTwistVisionPreviewResult Result = TrainingSubsystem->SubmitActiveRecognitionFrame(Frame); + + TestTrue(TEXT("The qbr preview shell state must be structurally valid."), Result.ShellState.IsStructurallyValid()); + TestEqual(TEXT("The preview shell must preserve the first-party shell profile id."), Result.ShellState.ShellProfileId, TEXT("classic-cube-webcam-shell-v1")); + TestEqual(TEXT("The preview shell must start with no scanned sides."), Result.ShellState.ScannedSideCount, 0); + TestEqual(TEXT("The preview shell must keep the six-side target."), Result.ShellState.TargetSideCount, 6); + TestEqual(TEXT("The preview shell must preserve the current face hint."), Result.ShellState.TargetFaceHint, TEXT("F")); + TestEqual(TEXT("The preview shell must preserve the classic default calibration profile id."), Result.ShellState.CalibrationProfileId, TEXT("classic-cube-default")); + TestEqual(TEXT("The preview shell must preserve the default locale code."), Result.ShellState.LocaleCode, TEXT("en")); + TestTrue(TEXT("The preview panel must remain visible."), Result.ShellState.bPreviewPanelVisible); + TestTrue(TEXT("The snapshot panel must remain visible."), Result.ShellState.bSnapshotPanelVisible); + TestTrue(TEXT("The cube net panel must remain visible."), Result.ShellState.bCubeNetVisible); + TestTrue(TEXT("The locale indicator must remain visible."), Result.ShellState.bLocaleIndicatorVisible); + TestFalse(TEXT("The calibration rail should stay hidden outside explicit calibration mode."), Result.ShellState.bCalibrationRailVisible); + TestEqual(TEXT("The preview shell should not yet report captured faces."), Result.ShellState.CapturedFaceIds.Num(), 0); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistQbrPhase6ROWebcamShellCommitStateTest, + "HyperTwist.Permissive.Qbr.Phase6R.O.WebcamShellCommitState", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistQbrPhase6ROWebcamShellCommitStateTest::RunTest(const FString& Parameters) +{ + UGameInstance* GameInstance = NewObject(GetTransientPackage()); + TestNotNull(TEXT("Game instance outer must exist for subsystem construction."), GameInstance); + if (GameInstance == nullptr) + { + return false; + } + + UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject(GameInstance); + TestNotNull(TEXT("Training subsystem must be constructed."), TrainingSubsystem); + if (TrainingSubsystem == nullptr) + { + return false; + } + HyperTwistQbrPhase6ROTestInternal::ForceMockRecognitionClient(TrainingSubsystem); + + const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck( + HyperTwistQbrPhase6ROTestInternal::MakeRecognitionDeck(), + TEXT("phase6r-o-user"), + TEXT("phase6r-o-commit-session"), + EHyperTwistTrainingDeliveryMode::RecognitionAssisted + ); + TestTrue(TEXT("The recognition-assisted run must start successfully."), RunState.IsStructurallyValid()); + if (!RunState.IsStructurallyValid()) + { + return false; + } + + FHyperTwistVisionFrameEnvelope Frame; + Frame.TargetFaceHint = TEXT("F"); + TrainingSubsystem->SubmitActiveRecognitionFrame(Frame); + + FHyperTwistVisionCommitRequest Request; + Request.TargetFace = TEXT("F"); + const FHyperTwistVisionCommitResult Result = + TrainingSubsystem->CommitActiveRecognitionObservation(Request); + + TestTrue(TEXT("The qbr commit shell state must be structurally valid."), Result.ShellState.IsStructurallyValid()); + TestEqual(TEXT("The committed shell must report one scanned side."), Result.ShellState.ScannedSideCount, 1); + TestEqual(TEXT("The committed shell must preserve the six-side target."), Result.ShellState.TargetSideCount, 6); + TestEqual(TEXT("The committed shell must preserve the last committed face."), Result.ShellState.LastCommittedFaceId, TEXT("F")); + TestTrue(TEXT("The committed shell must retain the scanned-side counter surface."), Result.ShellState.bScannedSideCounterVisible); + TestTrue(TEXT("The committed shell must retain the cube-net surface."), Result.ShellState.bCubeNetVisible); + TestTrue(TEXT("The committed shell must track the committed face id."), Result.ShellState.CapturedFaceIds.Contains(TEXT("F"))); + TestEqual(TEXT("The committed shell must retain the classic default calibration profile id."), Result.ShellState.CalibrationProfileId, TEXT("classic-cube-default")); + return true; +} + +#endif diff --git a/docs/REPO_LICENSE_TRACKING.md b/docs/REPO_LICENSE_TRACKING.md index 952b28b..27c7340 100644 --- a/docs/REPO_LICENSE_TRACKING.md +++ b/docs/REPO_LICENSE_TRACKING.md @@ -126,6 +126,8 @@ Status update on `2026-05-21`: - `HactarCE/Hyperspeedcube` remains partially incorporated - the bounded permissive `Phase 6R-B` `kkoomen/qbr` classic-cube recognition calibration and ordered face-observation packet is now landed in current code +- the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in + current code - `kkoomen/qbr` remains partially incorporated - the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face reconstruction and face-vote replacement packet is now landed in current code @@ -149,8 +151,9 @@ 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-O` `kkoomen/qbr` webcam UI shell - preparation/control pass, not a new restrictive packet by default +- 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 - use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A` ownership contract for the current queue after that correction @@ -254,8 +257,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-O` `kkoomen/qbr` preparation/control pass - for a bounded webcam UI shell seam +- 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 Companion docs: @@ -740,11 +744,11 @@ Approved working posture: - `Copyright (c) 2016 Kim Koomen` - keep the row as a partially incorporated recognition-substrate donor, not as a blanket promotion to full recognition-shell ownership -- the landed packet is bounded to: +- the landed packets are bounded to: - classic-cube recognition calibration contract - ordered face-observation contract + - classic-cube webcam shell - keep the following families deferred to later queue decisions: - - webcam UI shell - bundled-font redistribution and multilingual solve shell - multi-face reconstruction / correction / explanation - if later implementation copies bundled non-code assets, capture and preserve any asset-specific diff --git a/docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md b/docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md new file mode 100644 index 0000000..121a58d --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md @@ -0,0 +1,130 @@ +# HyperTwist Phase 6R-O qbr webcam UI shell implementation packet + +Created on `2026-05-22` + +## Status + +- first-party HyperTwist packet +- bounded permissive `Phase 6R-O` implementation slice + +## Purpose + +This packet lands the current retained `qbr` webcam shell family: + +- first-party classic-cube webcam shell profile and shell-state composition + +It is not: + +- a full `qbr` row transplant +- a multilingual solve shell packet +- a bundled-font redistribution packet +- a multi-face correction or explanation packet +- a `rubix-cube-solver` browser-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_O_QBR_WEBCAM_UI_SHELL_PREPARATION_PACKET_2026-05-22.md` + +The retained owner remains: + +- `kkoomen/qbr` + +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 `kkoomen/qbr` bounded to the narrower `A1 / R1 / F2` classic-cube webcam + shell slice + +## Landed scope + +The current code now owns a retained `qbr` webcam shell contract through: + +- first-party `HyperTwistRecognition` contract types for: + - shell action bindings + - shell panel layout + - shell profile definition + - shell state readout +- recognition session config defaults for: + - first-party classic-cube webcam shell profile id + - classic-cube webcam shell profile definition +- first-party preview and commit result shaping for: + - scanned-side count + - captured-face ids + - target-face hint + - last committed face id + - bounded shell panel visibility +- sample contract routing in: + - `UHyperTwistContractLibrary` +- active subsystem normalization in: + - `UHyperTwistTrainingSubsystem` +- focused automation coverage in: + - `HyperTwistQbrPhase6ROWebcamShellContractTest.cpp` + +## Why this is still intentionally bounded + +This packet lands the bounded webcam shell seam, but it does not widen into neighboring families or +broader ownership. + +Still excluded: + +- bundled-font redistribution +- multilingual solve shell ownership +- multi-face reconstruction / correction / explanation ownership +- broad browser shell or recommendation ownership +- `rubix-cube-solver` browser/webcam shell ownership + +## 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-O-Qbr-Verify.log -ReportExportPath=C:\HyperTwist\UnrealHyperTwist\Saved\AutomationReports\Phase6R-O-Qbr-Verify -ExecCmds="Automation RunTests HyperTwist.Permissive.Qbr.Phase6R.O; Quit" -TestExit="Automation Test Queue Empty"` + +Regression automation validation: + +- `HyperTwist.Permissive.Qbr.Phase6R.B` +- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.C` +- `HyperTwist.Permissive.Hyperspeedcube.Phase6R.N` +- `HyperTwist.CleanRoom.CubeDesk` + +Expected covered tests: + +- `WebcamShellSessionConfig` +- `WebcamShellPreviewState` +- `WebcamShellCommitState` +- existing `qbr`, `rubix-cube-solver`, `Hyperspeedcube`, and `CubeDesk` regression suites + +## Queue effect + +This packet consumes the current `Phase 6R-O` implementation slice. + +`kkoomen/qbr` remains partially incorporated: + +- landed now: + - classic-cube recognition calibration contract + - ordered face-observation contract + - classic-cube webcam shell +- still deferred: + - bundled-font redistribution and multilingual solve shell + - multi-face correction / explanation ownership + +The next clean move is: + +- source-backed `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell + preparation/control pass + +Keep the future sequencing guards visible: + +- keep the `qbr` font guard explicit: + - preserve separate confirmation or replacement before any shipping path redistributes + `src/assets/arial-unicode-ms.ttf` +- keep the `rubix-cube-solver` browser-shell widening narrow: + - do not widen directly into bundled `twistysim.min.js` redistribution or broad solve + explanation/recommendation ownership in the next packet diff --git a/docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_PREPARATION_PACKET_2026-05-22.md b/docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_PREPARATION_PACKET_2026-05-22.md new file mode 100644 index 0000000..b3c73b9 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_PREPARATION_PACKET_2026-05-22.md @@ -0,0 +1,190 @@ +# HyperTwist Phase 6R-O qbr webcam UI shell preparation packet + +Created on `2026-05-22` + +## Status + +- historical consumed preparation authority +- bounded post-`Phase 6R-N` 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-N` `HactarCE/Hyperspeedcube` +puzzle-definition DSL slice. + +The open task was: + +- define the first bounded source-backed widening packet for `kkoomen/qbr` as the retained + classic-cube webcam UI shell above the already landed calibration and ordered face-observation + seam + +It is not: + +- a full `qbr` row transplant +- a multilingual solve shell packet +- a bundled-font redistribution packet +- a multi-face reconstruction or correction packet +- a browser-shell or recommendation-shell packet from `rubix-cube-solver` +- 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_B_QBR_CALIBRATED_FACE_OBSERVATION_IMPLEMENTATION_PACKET_2026-05-20.md` +- `docs/arch/HYPERTWIST_PHASE6R_C_RUBIX_CUBE_SOLVER_RECONSTRUCTION_IMPLEMENTATION_PACKET_2026-05-20.md` + +The key accepted routing facts are: + +- `kkoomen/qbr` remains the retained recognition-substrate donor for the bounded classic-cube + webcam shell seam +- the already landed `Phase 6R-B` slice owns: + - classic-cube recognition calibration contract + - ordered face-observation contract +- the donor value now strongest for the next narrower slice is: + - preview sticker-grid shell + - snapshot sticker-grid shell + - scanned-side counter shell + - classic cube-net readout shell + - locale-indicator and input-action shell semantics +- ownership denied there is: + - do not widen into bundled `arial-unicode-ms.ttf` redistribution + - do not widen into multilingual solve text ownership + - do not widen into multi-face reconstruction or correction ownership + - do not let the row absorb the queued `rubix-cube-solver` browser/webcam shell + +## 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` + +That advanced the live queue to: + +- `Phase 6R-O` `kkoomen/qbr` + +with the adjacent retained recognition row ordered behind it: + +- `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` + +## Required result + +The source-backed control pass for this packet is now complete. + +The first actual `6R-O` implementation packet should: + +- define one bounded retained shell slice from `qbr` +- keep the slice inside the accepted classic-cube recognition-shell seam +- widen only the first shell family that can stand on its own without dragging in bundled-font + shipping, multilingual copy ownership, or browser-shell scope +- 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` +- `src/video.py` +- `src/constants.py` +- `src/helpers.py` +- `src/config.py` + +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/HyperTwistQbrPhase6RBCalibrationContractTest.cpp` + +## Narrowed 6R-O slice decision + +The first widening slice is fixed as: + +1. classic-cube webcam shell profile and shell-state composition + +not as the broader multilingual or correction shell. + +That narrowed slice covers: + +- first-party webcam shell profile for: + - preview sticker grid + - snapshot sticker grid + - scanned-side counter + - classic cube-net readout + - locale indicator + - calibration swatch rail as a bounded shell surface +- first-party input-action contract for: + - capture snapshot + - toggle calibration + - cycle locale + - close session +- first-party shell-state readout for: + - scanned-side count + - captured-face ids + - target-face hint + - last committed face id + - bounded panel visibility +- subsystem normalization and focused automation coverage + +## Deferred neighboring families + +The first `6R-O` implementation packet must keep these capability families closed: + +- bundled-font redistribution +- multilingual solve shell ownership +- multi-face reconstruction / correction / explanation +- browser/webcam shell ownership from `rubix-cube-solver` +- bundled `twistysim.min.js` redistribution +- broad recommendation or solve-explanation ownership + +## Why this slice was first + +- `qbr` is strongest for the compact webcam overlay shell that sits directly on top of the already + landed calibration and ordered face-observation seam +- current first-party recognition code already had the session, preview, and commit envelopes, but + it did not yet own a richer shell profile and shell-state contract for that route +- widening into multilingual copy or correction/explanation would prematurely absorb adjacent legal + and routing concerns from `qbr` and `rubix-cube-solver` + +## Out of scope for the first 6R-O packet + +- bundled font asset shipping +- multilingual solve guidance ownership +- multi-face correction or explanation ownership +- browser/webcam shell ownership beyond the bounded classic-cube webcam overlay +- general recommendation shell widening + +## Acceptance criteria + +- the packet records why the live queue advanced to `6R-O` +- the packet records that the first `6R-O` widening slice is the classic-cube webcam shell only +- the packet states exact out-of-scope families for the first `6R-O` pass +- the packet keeps the `qbr` font-asset guard visible +- the packet keeps the `rubix-cube-solver` browser-shell guard visible for the next packet + +## Validation checklist + +1. confirm the retained `qbr` seam narrows cleanly to a webcam shell profile and shell state +2. confirm the next implementation packet is framed as bounded source-backed widening rather than a + donor UI transplant +3. confirm bundled-font and multilingual 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 ab96172..fdc9f19 100644 --- a/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md +++ b/docs/ops/HYPERTWIST_IMPLEMENTATION_PHASE_1_KICKOFF.md @@ -94,10 +94,15 @@ The next bounded move is now: implementation packet is now landed in current code 4. the bounded permissive `Phase 6R-N` `HactarCE/Hyperspeedcube` puzzle-definition DSL implementation packet is now landed in current code -5. source-backed `Phase 6R-O` `kkoomen/qbr` webcam UI shell preparation/control pass is now the - next bounded move -6. keep it narrower than bundled-font redistribution, multilingual solve shell, and broader - correction/explanation ownership in the same packet +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: + - 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 4d2fe3d..f3a291d 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md @@ -107,6 +107,7 @@ Do not collapse those three tiers into one undifferentiated "features" voice. | Publication/leaderboard projection and entitlement gating | Implemented now | landed `kash/cubedesk` bounded packets | Publication and entitlement are live first-party training surfaces. | | Local social challenge bundle | Implemented now | landed `kash/cubedesk` `Bound 5` | Challenge/social training slice is real, but bounded. | | 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. | | 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. | @@ -141,10 +142,12 @@ repo. | Feature | Status | Primary authority | Notes | |---|---|---|---| | 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. | | 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/webcam recognition shell | Deep-source grounded retained | `rubix-cube-solver` retained remainder | Retained, not shipped. | +| 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 464e02c..2a4e59b 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -62,9 +62,10 @@ Canonical discovery surfaces for roadmap interpretation: - the generic source-backed `Phase 6R-B` `kkoomen/qbr` control pass is now consumed - the bounded permissive `Phase 6R-B` `kkoomen/qbr` classic-cube recognition calibration and ordered face-observation packet is now landed in current code -- `kkoomen/qbr` remains partially incorporated; webcam UI shell, bundled-font redistribution, - multilingual solve shell, and multi-face reconstruction/correction/explanation families stay - deferred +- the bounded permissive `Phase 6R-O` `kkoomen/qbr` webcam UI shell packet is now landed in + current code +- `kkoomen/qbr` remains partially incorporated; bundled-font redistribution, multilingual solve + shell, and multi-face reconstruction/correction/explanation families stay deferred - the generic source-backed `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` control pass is now consumed - the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face @@ -115,8 +116,10 @@ Canonical discovery surfaces for roadmap interpretation: - the bounded permissive `Phase 6R-M` `HactarCE/Hyperspeedcube` stats-shape and solve-record 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-O` `kkoomen/qbr` webcam UI shell - preparation/control pass for one bounded recognition-shell widening slice +- 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 repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than waiting on another first-party packet @@ -178,8 +181,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-O` -`kkoomen/qbr` webcam UI shell preparation/control pass. +The next bounded move is a source-backed `Phase 6R-P` +`vivaansinghvi07/rubix-cube-solver` browser/webcam shell preparation/control pass. Queue interpretation after that packet: @@ -200,8 +203,10 @@ Queue interpretation after that packet: - landed: - classic-cube recognition calibration contract - ordered face-observation contract + - classic-cube webcam UI shell - still deferred: - - webcam UI shell + - bundled-font redistribution and multilingual solve shell + - multi-face correction / explanation ownership - `roice3/Magic120Cell` remains a partially landed row rather than a closed row: - landed: - dedicated `120-cell` family runtime profile @@ -301,10 +306,10 @@ Queue interpretation after that packet: - viseme / gesture runtime integration - broad assistant-platform scope - the next queue shape is now: - - `kkoomen/qbr` deferred recognition-shell remainder + - `vivaansinghvi07/rubix-cube-solver` deferred browser/webcam shell remainder - the next bounded packet should stay narrow: - - webcam UI shell preparation/control before widening into bundled-font redistribution, - multilingual solve shell, or multi-face correction/explanation ownership + - browser/webcam shell preparation/control before widening into bundled `twistysim.min.js` + redistribution or broad solve explanation/recommendation 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: