diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp index 6b2d78e..e1649eb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -97,6 +97,72 @@ namespace HyperTwistContractLibraryInternal DrillDeck.SourceAttribution = Attribution; return DrillDeck; } + + FHyperTwistVisionCalibrationProfile MakeDefaultClassicCubeCalibrationProfile(const FString& ProfileId) + { + FHyperTwistVisionCalibrationProfile Profile; + Profile.ProfileId = ProfileId; + Profile.PuzzleId = TEXT("cube/3x3x3"); + Profile.DominantColorMethod = TEXT("kmeans-1"); + Profile.ColorDistanceMethod = TEXT("ciede2000"); + Profile.bPersistsPerUserPalette = true; + + auto AddSwatch = [&Profile]( + const TCHAR* ColorId, + const TCHAR* Notation, + const FIntVector Bgr, + const FVector Lab + ) + { + FHyperTwistVisionCalibrationSwatch Swatch; + Swatch.ColorId = ColorId; + Swatch.Notation = Notation; + Swatch.ReferenceBgr = Bgr; + Swatch.ReferenceLab = Lab; + Profile.PaletteSwatches.Add(Swatch); + }; + + AddSwatch(TEXT("green"), TEXT("F"), FIntVector(0, 255, 0), FVector(87.737f, -86.185f, 83.181f)); + AddSwatch(TEXT("red"), TEXT("R"), FIntVector(0, 0, 255), FVector(53.233f, 80.109f, 67.220f)); + AddSwatch(TEXT("blue"), TEXT("B"), FIntVector(255, 0, 0), FVector(32.303f, 79.197f, -107.864f)); + AddSwatch(TEXT("orange"), TEXT("L"), FIntVector(0, 165, 255), FVector(74.200f, 23.900f, 78.900f)); + AddSwatch(TEXT("white"), TEXT("U"), FIntVector(255, 255, 255), FVector(100.000f, 0.000f, 0.000f)); + AddSwatch(TEXT("yellow"), TEXT("D"), FIntVector(0, 255, 255), FVector(97.139f, -21.554f, 94.478f)); + return Profile; + } + + FHyperTwistVisionFaceObservation MakeMockClassicFaceObservation( + const FString& ObservationId, + const FString& FaceId, + const float StickerConfidence, + const bool bCommitReady + ) + { + FHyperTwistVisionFaceObservation Observation; + Observation.ObservationId = ObservationId; + Observation.FaceId = FaceId; + Observation.OrderingProfile = TEXT("3x3-row-major"); + Observation.CandidateContourCount = 12; + Observation.AcceptedContourCount = 9; + Observation.bFoundStableThreeByThreeGrid = true; + Observation.bOrderingStable = true; + Observation.bCommitReady = bCommitReady; + + for (int32 GridIndex = 0; GridIndex < 9; ++GridIndex) + { + FHyperTwistVisionObservedSticker Sticker; + Sticker.GridIndex = GridIndex; + Sticker.GridRow = GridIndex / 3; + Sticker.GridColumn = GridIndex % 3; + Sticker.ColorId = TEXT("green"); + Sticker.Notation = TEXT("F"); + Sticker.SampleBgr = FIntVector(0, 255, 0); + Sticker.Confidence = StickerConfidence; + Observation.OrderedStickers.Add(Sticker); + } + + return Observation; + } } FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition() @@ -321,6 +387,11 @@ FHyperTwistVisionSessionConfig UHyperTwistContractLibrary::MakeSampleVisionSessi Config.PuzzleId = TEXT("cube/3x3x3"); Config.ExpectedPuzzleFamily = EHyperTwistPuzzleFamily::ClassicCube; Config.NotationProfile = TEXT("classic-wca"); + Config.CalibrationProfile = TEXT("classic-cube-default"); + Config.CalibrationProfileDefinition = + HyperTwistContractLibraryInternal::MakeDefaultClassicCubeCalibrationProfile( + Config.CalibrationProfile + ); Config.FrameSize = FIntPoint(1280, 720); Config.MaxFrameRate = 30; return Config; @@ -335,6 +406,12 @@ FHyperTwistVisionPreviewResult UHyperTwistContractLibrary::MakeMockVisionPreview Result.PreviewState.SnapshotId = TEXT("snap_prev_10"); Result.PreviewState.State = MakeSampleClassicRecognitionPreviewState(); Result.PreviewState.DerivedHash = TEXT("preview_hash"); + Result.ObservedFace = HyperTwistContractLibraryInternal::MakeMockClassicFaceObservation( + TEXT("observation_preview_10"), + TEXT("F"), + 0.84f, + false + ); Result.Guidance = TEXT("capture-ready"); return Result; } @@ -352,6 +429,12 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe Result.Snapshot.DerivedHash = TEXT("commit_hash"); Result.Confidence = 0.96f; Result.CommittedUnit = TEXT("F"); + Result.ObservedFace = HyperTwistContractLibraryInternal::MakeMockClassicFaceObservation( + TEXT("observation_commit"), + TEXT("F"), + 0.96f, + true + ); return Result; } @@ -377,7 +460,12 @@ FHyperTwistVisionServiceHealth UHyperTwistContractLibrary::MakeMockVisionService Health.ProviderLabel = TEXT("mock"); Health.ServiceVersion = TEXT("mock-vision/v1"); Health.ServiceEndpoint = TEXT("in-process://mock"); - Health.Capabilities = {TEXT("liveCamera3x3"), TEXT("faceCommitFlow"), TEXT("calibrationProfiles")}; + Health.Capabilities = { + TEXT("liveCamera3x3"), + TEXT("faceCommitFlow"), + TEXT("calibrationProfiles"), + TEXT("orderedFaceObservation") + }; Health.bReady = true; return Health; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpVisionClient.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpVisionClient.cpp index d3cae55..36c0adf 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpVisionClient.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpVisionClient.cpp @@ -373,7 +373,9 @@ FHyperTwistVisionServiceHealth UHyperTwistHttpVisionClient::GetVisionServiceHeal Health.bReady = false; Health.Capabilities = { TEXT("transport:http"), - TEXT("provider-backed-recognition") + TEXT("provider-backed-recognition"), + TEXT("calibrationProfiles"), + TEXT("orderedFaceObservation") }; HyperTwistHttpVisionClientInternal::FHttpJsonResponse Response; @@ -399,7 +401,9 @@ FHyperTwistVisionServiceHealth UHyperTwistHttpVisionClient::GetVisionServiceHeal Health.bReady = false; Health.Capabilities = { TEXT("transport:http"), - TEXT("provider-backed-recognition") + TEXT("provider-backed-recognition"), + TEXT("calibrationProfiles"), + TEXT("orderedFaceObservation") }; Health.LastError = LastTransportError; return Health; @@ -417,6 +421,8 @@ FHyperTwistVisionServiceHealth UHyperTwistHttpVisionClient::GetVisionServiceHeal Health.Capabilities.AddUnique(TEXT("transport:http")); Health.Capabilities.AddUnique(TEXT("provider-backed-recognition")); + Health.Capabilities.AddUnique(TEXT("calibrationProfiles")); + Health.Capabilities.AddUnique(TEXT("orderedFaceObservation")); Health.LastError.Reset(); LastTransportError.Reset(); return Health; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockVisionClient.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockVisionClient.cpp index b3b2a66..6cc97db 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockVisionClient.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockVisionClient.cpp @@ -26,7 +26,12 @@ FHyperTwistVisionPreviewResult UHyperTwistMockVisionClient::SubmitVisionFrame(co const int32 NextFrameCount = SessionFrameCounts.FindRef(Frame.SessionId) + 1; SessionFrameCounts.Add(Frame.SessionId, NextFrameCount); - Result.Guidance = NextFrameCount >= 3 ? TEXT("commit-ready") : TEXT("keep-scanning"); + Result.ObservedFace.FaceId = !Frame.TargetFaceHint.IsEmpty() ? Frame.TargetFaceHint : Result.ObservedFace.FaceId; + Result.ObservedFace.ObservationId = Result.FrameId.IsEmpty() + ? TEXT("observation_preview") + : FString::Printf(TEXT("%s/preview"), *Result.FrameId); + Result.ObservedFace.bCommitReady = NextFrameCount >= 3; + Result.Guidance = Result.ObservedFace.bCommitReady ? TEXT("commit-ready") : TEXT("keep-scanning"); return Result; } @@ -38,6 +43,11 @@ FHyperTwistVisionCommitResult UHyperTwistMockVisionClient::CommitVisionObservati Result.CommitKind = Request.CommitKind; Result.CommittedUnit = !Request.TargetFace.IsEmpty() ? Request.TargetFace : Request.TargetStage; Result.Snapshot.State.SourceSessionId = Request.SessionId; + Result.ObservedFace.FaceId = !Request.TargetFace.IsEmpty() ? Request.TargetFace : Result.ObservedFace.FaceId; + Result.ObservedFace.ObservationId = Request.SessionId.IsEmpty() + ? TEXT("observation_commit") + : FString::Printf(TEXT("%s/commit"), *Request.SessionId); + Result.ObservedFace.bCommitReady = true; if (!SessionConfigs.Contains(Request.SessionId)) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index b97a311..63075ac 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -1866,6 +1866,45 @@ namespace HyperTwistTrainingSubsystemInternal return FString::Printf(TEXT("%s_frame_%03d"), *SessionId, FMath::Max(1, FrameOrdinal)); } + bool IsQbrClassicCubeRoute(const FHyperTwistVisionSessionConfig& SessionConfig) + { + return SessionConfig.ExpectedPuzzleFamily == EHyperTwistPuzzleFamily::ClassicCube + && SessionConfig.PuzzleId == TEXT("cube/3x3x3"); + } + + FHyperTwistVisionCalibrationProfile BuildDefaultClassicCubeCalibrationProfile(const FString& ProfileId) + { + FHyperTwistVisionCalibrationProfile Profile; + Profile.ProfileId = ProfileId; + Profile.PuzzleId = TEXT("cube/3x3x3"); + Profile.DominantColorMethod = TEXT("kmeans-1"); + Profile.ColorDistanceMethod = TEXT("ciede2000"); + Profile.bPersistsPerUserPalette = true; + + auto AddSwatch = [&Profile]( + const TCHAR* ColorId, + const TCHAR* Notation, + const FIntVector Bgr, + const FVector Lab + ) + { + FHyperTwistVisionCalibrationSwatch Swatch; + Swatch.ColorId = ColorId; + Swatch.Notation = Notation; + Swatch.ReferenceBgr = Bgr; + Swatch.ReferenceLab = Lab; + Profile.PaletteSwatches.Add(Swatch); + }; + + AddSwatch(TEXT("green"), TEXT("F"), FIntVector(0, 255, 0), FVector(87.737f, -86.185f, 83.181f)); + AddSwatch(TEXT("red"), TEXT("R"), FIntVector(0, 0, 255), FVector(53.233f, 80.109f, 67.220f)); + AddSwatch(TEXT("blue"), TEXT("B"), FIntVector(255, 0, 0), FVector(32.303f, 79.197f, -107.864f)); + AddSwatch(TEXT("orange"), TEXT("L"), FIntVector(0, 165, 255), FVector(74.200f, 23.900f, 78.900f)); + AddSwatch(TEXT("white"), TEXT("U"), FIntVector(255, 255, 255), FVector(100.000f, 0.000f, 0.000f)); + AddSwatch(TEXT("yellow"), TEXT("D"), FIntVector(0, 255, 255), FVector(97.139f, -21.554f, 94.478f)); + return Profile; + } + EHyperTwistPuzzleFamily ResolvePuzzleFamilyFromPuzzleId(const FString& PuzzleId) { FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; @@ -6094,6 +6133,11 @@ FHyperTwistVisionPreviewResult UHyperTwistTrainingSubsystem::SubmitActiveRecogni { NormalizedFrame.CapturedAtUtc = FDateTime::UtcNow().ToIso8601(); } + if (NormalizedFrame.ObservationProfile.IsEmpty() + && HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig)) + { + NormalizedFrame.ObservationProfile = TEXT("classic-3x3-face-observation-v1"); + } Result = VisionClient->SubmitVisionFrame(NormalizedFrame); if (Result.SessionId.IsEmpty()) @@ -6782,6 +6826,29 @@ FHyperTwistVisionSessionConfig UHyperTwistTrainingSubsystem::BuildActiveRecognit { SessionConfig.MaxFrameRate = 30; } + if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(SessionConfig)) + { + if (SessionConfig.CalibrationProfile.IsEmpty() + && SessionConfig.CalibrationProfileDefinition.IsStructurallyValid()) + { + SessionConfig.CalibrationProfile = SessionConfig.CalibrationProfileDefinition.ProfileId; + } + if (SessionConfig.CalibrationProfile.IsEmpty()) + { + SessionConfig.CalibrationProfile = TEXT("classic-cube-default"); + } + if (!SessionConfig.CalibrationProfileDefinition.IsStructurallyValid()) + { + SessionConfig.CalibrationProfileDefinition = + HyperTwistTrainingSubsystemInternal::BuildDefaultClassicCubeCalibrationProfile( + SessionConfig.CalibrationProfile + ); + } + if (SessionConfig.CalibrationProfileDefinition.ProfileId.IsEmpty()) + { + SessionConfig.CalibrationProfileDefinition.ProfileId = SessionConfig.CalibrationProfile; + } + } return SessionConfig; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h index f9b298a..ad79120 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h @@ -11,6 +11,168 @@ enum class EHyperTwistVisionCaptureMode : uint8 BatchVideo UMETA(DisplayName = "Batch Video") }; +USTRUCT(BlueprintType) +struct FHyperTwistVisionCalibrationSwatch +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ColorId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Notation; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FIntVector ReferenceBgr = FIntVector::ZeroValue; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FVector ReferenceLab = FVector::ZeroVector; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUserCalibrated = false; + + bool IsStructurallyValid() const + { + return !ColorId.IsEmpty() && !Notation.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionCalibrationProfile +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ProfileId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PuzzleId = TEXT("cube/3x3x3"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DominantColorMethod = TEXT("kmeans-1"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ColorDistanceMethod = TEXT("ciede2000"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPersistsPerUserPalette = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray PaletteSwatches; + + bool IsStructurallyValid() const + { + if (ProfileId.IsEmpty() || PuzzleId.IsEmpty() || PaletteSwatches.Num() < 6) + { + return false; + } + + for (const FHyperTwistVisionCalibrationSwatch& Swatch : PaletteSwatches) + { + if (!Swatch.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionObservedSticker +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GridIndex = -1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GridRow = -1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GridColumn = -1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ColorId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Notation; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FIntVector SampleBgr = FIntVector::ZeroValue; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float Confidence = 0.0f; + + bool IsStructurallyValid() const + { + return GridIndex >= 0 + && GridRow >= 0 + && GridColumn >= 0 + && !ColorId.IsEmpty() + && !Notation.IsEmpty(); + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistVisionFaceObservation +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ObservationId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FaceId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString OrderingProfile = TEXT("3x3-row-major"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CandidateContourCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 AcceptedContourCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bFoundStableThreeByThreeGrid = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bOrderingStable = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCommitReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray OrderedStickers; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray NormalizationWarnings; + + bool IsStructurallyValid() const + { + if (ObservationId.IsEmpty() + || FaceId.IsEmpty() + || OrderedStickers.Num() != 9 + || !bFoundStableThreeByThreeGrid + || !bOrderingStable) + { + return false; + } + + for (const FHyperTwistVisionObservedSticker& Sticker : OrderedStickers) + { + if (!Sticker.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistVisionSessionConfig { @@ -37,6 +199,9 @@ struct FHyperTwistVisionSessionConfig UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString CalibrationProfile; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionCalibrationProfile CalibrationProfileDefinition; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FIntPoint FrameSize = FIntPoint(1280, 720); @@ -72,6 +237,12 @@ struct FHyperTwistVisionFrameEnvelope UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 SourceTimestampMs = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TargetFaceHint; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ObservationProfile = TEXT("classic-3x3-face-observation-v1"); }; USTRUCT(BlueprintType) @@ -91,6 +262,9 @@ struct FHyperTwistVisionPreviewResult UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FHyperTwistStateSnapshot PreviewState; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionFaceObservation ObservedFace; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString Guidance; }; @@ -133,6 +307,9 @@ struct FHyperTwistVisionCommitResult UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString CommittedUnit; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistVisionFaceObservation ObservedFace; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") TArray Warnings; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistQbrPhase6RBCalibrationContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistQbrPhase6RBCalibrationContractTest.cpp new file mode 100644 index 0000000..7f7743e --- /dev/null +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistQbrPhase6RBCalibrationContractTest.cpp @@ -0,0 +1,237 @@ +// 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 HyperTwistQbrPhase6RBTestInternal +{ + FHyperTwistTrainingDeck MakeRecognitionDeck() + { + FHyperTwistTrainingDeck Deck; + Deck.DeckId = TEXT("phase6r-b/qbr-recognition"); + Deck.Title = TEXT("Phase 6R-B Qbr Recognition"); + Deck.DeliveryModes = { + EHyperTwistTrainingDeliveryMode::RecognitionAssisted, + EHyperTwistTrainingDeliveryMode::CoachReviewed + }; + + FHyperTwistTrainingCase TrainingCase; + TrainingCase.CaseId = TEXT("phase6r-b-case"); + TrainingCase.PuzzleId = TEXT("cube/3x3x3"); + TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition; + TrainingCase.PromptLabel = TEXT("Phase 6R-B 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")); + } + } +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistQbrPhase6RBCalibrationSessionConfigTest, + "HyperTwist.Permissive.Qbr.Phase6R.B.CalibrationSessionConfig", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistQbrPhase6RBCalibrationSessionConfigTest::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; + } + HyperTwistQbrPhase6RBTestInternal::ForceMockRecognitionClient(TrainingSubsystem); + + const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck( + HyperTwistQbrPhase6RBTestInternal::MakeRecognitionDeck(), + TEXT("phase6r-b-user"), + TEXT("phase6r-b-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 a classic 3x3 run."), + 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-bounded route must default to the classic-cube calibration profile id."), + SessionState.SessionConfig.CalibrationProfile, + TEXT("classic-cube-default") + ); + TestTrue( + TEXT("The qbr-bounded route must provide a structurally valid calibration profile definition."), + SessionState.SessionConfig.CalibrationProfileDefinition.IsStructurallyValid() + ); + TestEqual( + TEXT("The calibration profile must preserve six retained swatches."), + SessionState.SessionConfig.CalibrationProfileDefinition.PaletteSwatches.Num(), + 6 + ); + if (SessionState.SessionConfig.CalibrationProfileDefinition.PaletteSwatches.Num() > 0) + { + TestEqual( + TEXT("The front swatch must preserve the green/F calibration mapping."), + SessionState.SessionConfig.CalibrationProfileDefinition.PaletteSwatches[0].Notation, + TEXT("F") + ); + } + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistQbrPhase6RBPreviewObservationTest, + "HyperTwist.Permissive.Qbr.Phase6R.B.PreviewObservation", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistQbrPhase6RBPreviewObservationTest::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; + } + HyperTwistQbrPhase6RBTestInternal::ForceMockRecognitionClient(TrainingSubsystem); + + const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck( + HyperTwistQbrPhase6RBTestInternal::MakeRecognitionDeck(), + TEXT("phase6r-b-user"), + TEXT("phase6r-b-preview-session"), + EHyperTwistTrainingDeliveryMode::RecognitionAssisted + ); + TestTrue(TEXT("The recognition-assisted run must start successfully."), RunState.IsStructurallyValid()); + if (!RunState.IsStructurallyValid()) + { + return false; + } + + FHyperTwistVisionPreviewResult Result; + for (int32 FrameOrdinal = 1; FrameOrdinal <= 3; ++FrameOrdinal) + { + FHyperTwistVisionFrameEnvelope Frame; + Frame.FrameOrdinal = FrameOrdinal; + Frame.TargetFaceHint = TEXT("F"); + Result = TrainingSubsystem->SubmitActiveRecognitionFrame(Frame); + } + + TestEqual(TEXT("The third preview should move the mock into commit-ready guidance."), Result.Guidance, TEXT("commit-ready")); + TestTrue( + TEXT("The preview must expose a structurally valid ordered face observation."), + Result.ObservedFace.IsStructurallyValid() + ); + TestEqual(TEXT("The ordered face observation must resolve the target face."), Result.ObservedFace.FaceId, TEXT("F")); + TestEqual(TEXT("The ordered face observation must contain nine stickers."), Result.ObservedFace.OrderedStickers.Num(), 9); + TestTrue(TEXT("The preview observation must report commit readiness."), Result.ObservedFace.bCommitReady); + TestEqual( + TEXT("The preview observation must preserve row-major ordering."), + Result.ObservedFace.OrderingProfile, + TEXT("3x3-row-major") + ); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistQbrPhase6RBCommitObservationTest, + "HyperTwist.Permissive.Qbr.Phase6R.B.CommitObservation", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistQbrPhase6RBCommitObservationTest::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; + } + HyperTwistQbrPhase6RBTestInternal::ForceMockRecognitionClient(TrainingSubsystem); + + const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck( + HyperTwistQbrPhase6RBTestInternal::MakeRecognitionDeck(), + TEXT("phase6r-b-user"), + TEXT("phase6r-b-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); + + TestEqual(TEXT("The commit result must keep the normalized face commit kind."), Result.CommitKind, TEXT("face")); + TestEqual(TEXT("The commit result must report the committed face."), Result.CommittedUnit, TEXT("F")); + TestTrue( + TEXT("The commit result must expose a structurally valid ordered face observation."), + Result.ObservedFace.IsStructurallyValid() + ); + TestTrue(TEXT("The commit face observation must be commit-ready."), Result.ObservedFace.bCommitReady); + TestTrue(TEXT("The mock commit path should not report warnings for an open session."), Result.Warnings.IsEmpty()); + return true; +} + +#endif diff --git a/docs/REPO_LICENSE_TRACKING.md b/docs/REPO_LICENSE_TRACKING.md index 5ead8ce..c71543a 100644 --- a/docs/REPO_LICENSE_TRACKING.md +++ b/docs/REPO_LICENSE_TRACKING.md @@ -94,7 +94,7 @@ Status update on `2026-05-20`: - the current canonical repo-row portfolio count is `75` - all `75` current portfolio rows now have explicit root-tracker coverage -- the current implemented-row count is `25` +- the current implemented-row count is `26` - the four later restrictive clean-room rows now implemented after the original `2026-05-13` truth block are: - `cubing/alg.js` @@ -103,30 +103,36 @@ Status update on `2026-05-20`: - `kash/cubedesk` - the fifth additional implemented row is the same-day bounded permissive partial row: - `HactarCE/Hyperspeedcube` +- the sixth additional implemented row is the same-day bounded permissive partial row: + - `kkoomen/qbr` - the earlier first-party packet block is now confirmed landed in current code through: - `d4f4ad3` `Add primary coach orchestration entry lane` - `1860f14` `Add provider-backed recognition sidecar client` - `2473665` `Overlay imported training catalog packs and finite bundles` - the bounded permissive `Phase 6R-A` `HactarCE/Hyperspeedcube` puzzle-catalog contract widening packet is now landed in current code -- `HactarCE/Hyperspeedcube` remains partially incorporated; the current next bounded move is a - source-backed `Phase 6R-B` `kkoomen/qbr` preparation/control pass, not a new restrictive packet - by default +- `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 +- `kkoomen/qbr` remains partially incorporated; the current next bounded move is a source-backed + `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` 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 This file now also preserves the current truth that future models must not lose: - current curated HyperTwist shallow-eval set: `75` repos -- currently verified live/implemented in checked `UnrealHyperTwist` surfaces: `25` -- permissive live lanes: `14` +- currently verified live/implemented in checked `UnrealHyperTwist` surfaces: `26` +- permissive live lanes: `15` - boundary-sensitive live lanes: `6` - restrictive live lanes: `5` -The fourteen permissive live lanes are: +The fifteen permissive live lanes are: - `Aarav2709/KubeTimr` - `HactarCE/Hyperspeedcube` +- `kkoomen/qbr` - `apache/echarts` - `abunickabhi/5style-Trainer` - `google/model-viewer` @@ -170,7 +176,7 @@ Interpretation rule: Reset rule: -- preserve the twenty landed/live lanes +- preserve the twenty-six landed/live lanes - do not treat the remaining non-live rows as already absorbed - route all non-live rows only through the closed `Phase 1R` retained-set contract and the relevant `0R-*` packet @@ -209,7 +215,8 @@ Current practical interpretation: - the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md` - the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md` - the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md` -- the next bounded move is a source-backed `Phase 6R-B` `kkoomen/qbr` preparation/control pass +- the next bounded move is a source-backed `Phase 6R-C` + `vivaansinghvi07/rubix-cube-solver` preparation/control pass Companion docs: @@ -657,8 +664,8 @@ Current licensing judgment: - explicit `MIT` - repo code is usable for HyperTwist under the checked permissive license text - not a clean-room case -- treat as the retained recognition-substrate donor queued behind the current `Hyperspeedcube` - runtime-anchor packet +- treat as the retained recognition-substrate donor whose first narrower bounded slice is now + landed in current code Source basis: @@ -675,6 +682,9 @@ Important distinction: - calibration flow - color-detection bookkeeping - multilingual solve guidance shell +- the first landed HyperTwist slice is intentionally narrower than that broader donor value: + - classic-cube recognition calibration contract + - ordered face-observation contract - permissive posture alone does not let it outrank the already accepted route sequencing or absorb the whole product recognition shell prematurely - the checked mirror also contains a bundled font asset: @@ -687,8 +697,15 @@ Approved working posture: - HyperTwist may use the codebase directly under the checked `MIT` posture - preserve the upstream `MIT` license text and copyright notice: - `Copyright (c) 2016 Kim Koomen` -- keep the row queued as the recognition-substrate candidate after `Phase 6R-A`, not as a blanket - promotion to current implementation +- 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: + - classic-cube recognition calibration contract + - ordered face-observation contract +- 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 license or replace the asset with a clearly redistributable alternative @@ -1241,9 +1258,10 @@ Approved working posture: permission to transplant the whole Rust application shell - do not let this row absorb non-Euclidean topology ownership that remains queued for `roice3/MagicTile` -- after the landed puzzle-catalog slice, the next queue head should move to a source-backed - `Phase 6R-B` `kkoomen/qbr` preparation/control pass rather than pretending the entire - `Hyperspeedcube` family is already closed +- after the landed puzzle-catalog slice and the landed bounded `qbr` calibration slice, the next + queue head should move to a source-backed `Phase 6R-C` + `vivaansinghvi07/rubix-cube-solver` preparation/control pass rather than pretending the entire + retained recognition family is already closed ### `coqui-ai/TTS` diff --git a/docs/arch/HYPERTWIST_PHASE6R_B_QBR_CALIBRATED_FACE_OBSERVATION_IMPLEMENTATION_PACKET_2026-05-20.md b/docs/arch/HYPERTWIST_PHASE6R_B_QBR_CALIBRATED_FACE_OBSERVATION_IMPLEMENTATION_PACKET_2026-05-20.md new file mode 100644 index 0000000..6ced332 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_B_QBR_CALIBRATED_FACE_OBSERVATION_IMPLEMENTATION_PACKET_2026-05-20.md @@ -0,0 +1,139 @@ +# HyperTwist Phase 6R-B qbr calibrated face observation implementation packet + +Created on `2026-05-20` + +## Status + +- first-party HyperTwist packet +- bounded permissive `Phase 6R-B` implementation slice + +## Purpose + +This packet lands the first narrower bounded slice from the retained `kkoomen/qbr` +recognition-substrate row. + +The landed slice is: + +- first-party classic-cube recognition calibration plus ordered face-observation contract + +It is not: + +- a full `qbr` row transplant +- a browser/webcam UI shell packet +- a multilingual solve-shell packet +- a bundled-font redistribution packet +- a multi-face reconstruction or correction packet +- a `rubix-cube-solver` packet + +## Current authority basis + +This implementation packet stands on: + +- `docs/HYPERTWIST_PHASE_0R_PACKET_0R_A_EVALUATION_2026-05-12.md` +- `docs/HYPERTWIST_PHASE_2R_PACKET_2R_A_OWNERSHIP_AND_ACCEPTANCE_CONTRACT_2026-05-13.md` +- `docs/REPO_LICENSE_TRACKING.md` +- `docs/arch/HYPERTWIST_PHASE6R_B_QBR_RECOGNITION_CALIBRATION_PREPARATION_PACKET_2026-05-20.md` + +The retained owner remains: + +- `kkoomen/qbr` + +The granted family remains bounded to: + +- recognition calibration flow +- ordered face observation +- color-detection bookkeeping +- bounded capture-session guidance + +This packet lands only the first narrower contract slice in that granted set. + +## Landed scope + +The current code now owns a retained classic-cube recognition contract through: + +- retained recognition contract types for: + - calibration swatches + - calibration profiles + - observed stickers + - observed face envelopes +- recognition session config defaults for: + - classic-cube calibration profile id + - calibration-profile definition + - classic ordered face-observation profile +- `UHyperTwistContractLibrary` sample calibration-profile and observed-face routing +- `UHyperTwistTrainingSubsystem` classic `cube/3x3x3` recognition-session normalization +- `UHyperTwistHttpVisionClient` capability exposure for: + - calibration profiles + - ordered face observation +- `UHyperTwistMockVisionClient` preview and commit contract responses for ordered face observation +- focused automation coverage in: + - `HyperTwistQbrPhase6RBCalibrationContractTest.cpp` + +## Why this is still intentionally bounded + +This packet replaces the thinner recognition sample and session heuristics with a richer first-party +calibration and observed-face contract, but it does not widen into the neighboring retained +families. + +Still deferred: + +- webcam UI shell +- multilingual solve shell +- bundled-font redistribution +- multi-face reconstruction +- correction or solve-explanation flow +- `rubix-cube-solver` companion-lane ownership + +## Validation + +Build validation: + +- `Build.bat UnrealHyperTwistEditor Win64 Development -Project='C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject' -WaitMutex -NoHotReloadFromIDE` + +Focused automation validation: + +- `Automation RunTests HyperTwist.Permissive.Qbr.Phase6R.B` + +Regression automation validation: + +- `Automation RunTests HyperTwist.Permissive.Hyperspeedcube.Phase6R.A` +- `Automation RunTests HyperTwist.CleanRoom.CubeDesk` + +Expected covered tests: + +- `CalibrationSessionConfig` +- `PreviewObservation` +- `CommitObservation` +- `CatalogLookup` +- `SampleDefinition` +- `TrainingRunDefinition` +- existing `CubeDesk` clean-room regression suite + +## Queue effect + +This packet consumes the current `Phase 6R-B` implementation slice. + +`kkoomen/qbr` remains only partially incorporated: + +- landed now: + - classic-cube recognition calibration contract + - ordered face-observation contract +- still deferred: + - webcam UI shell + - bundled-font redistribution and multilingual solve shell + - multi-face reconstruction / correction / explanation + +The next clean move is not another immediate `qbr` widening by default. + +The next queue head should be: + +- a source-backed `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` preparation/control pass + +Keep the legal sequencing guards visible there: + +- `kkoomen/qbr` + - do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation or + replacement in any future widening that would ship that asset +- `vivaansinghvi07/rubix-cube-solver` + - do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing + its upstream provenance/license diff --git a/docs/arch/HYPERTWIST_PHASE6R_B_QBR_RECOGNITION_CALIBRATION_PREPARATION_PACKET_2026-05-20.md b/docs/arch/HYPERTWIST_PHASE6R_B_QBR_RECOGNITION_CALIBRATION_PREPARATION_PACKET_2026-05-20.md new file mode 100644 index 0000000..3acee43 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_B_QBR_RECOGNITION_CALIBRATION_PREPARATION_PACKET_2026-05-20.md @@ -0,0 +1,189 @@ +# HyperTwist Phase 6R-B qbr recognition calibration preparation packet + +Created on `2026-05-20` + +## Status + +- historical same-day preparation authority +- bounded post-Phase-`6R-A` preparation slice +- the first bounded implementation slice now lands separately under: + - `docs/arch/HYPERTWIST_PHASE6R_B_QBR_CALIBRATED_FACE_OBSERVATION_IMPLEMENTATION_PACKET_2026-05-20.md` + +## Purpose + +This packet freezes the next widening order after the landed `Phase 6R-A` +`HactarCE/Hyperspeedcube` puzzle-catalog contract. + +The open task is: + +- define the first bounded source-backed widening packet for `kkoomen/qbr` as the retained + `Phase 6R-B` recognition-calibration nucleus + +It is not: + +- a full `qbr` row transplant +- a `rubix-cube-solver` reconstruction packet +- a `MagicTile` topology packet +- a multi-face reconstruction or correction packet +- a solve-explanation or multilingual-shell packet +- a direct redistribution packet for the bundled `arial-unicode-ms.ttf` asset + +## Current authority basis + +This preparation packet stands on already-closed authority: + +- `docs/HYPERTWIST_PHASE_0R_PACKET_0R_A_EVALUATION_2026-05-12.md` +- `docs/HYPERTWIST_PHASE_2R_PACKET_2R_A_OWNERSHIP_AND_ACCEPTANCE_CONTRACT_2026-05-13.md` +- `docs/REPO_LICENSE_TRACKING.md` +- `docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_PUZZLE_CATALOG_IMPLEMENTATION_PACKET_2026-05-20.md` + +The key accepted routing facts are: + +- `kkoomen/qbr` is the retained recognition-substrate donor immediately after the landed `6R-A` + slice +- earliest widening route is `Phase 6R / Packet 6R-B` +- the retained donor value there is: + - classic-cube recognition calibration flow + - ordered face-observation capture + - color-detection bookkeeping + - bounded capture-session guidance +- ownership denied there is: + - do not let the repo absorb the whole recognition shell + - do not widen into multi-face reconstruction, correction, or solve explanation + - do not widen into browser UI shell or multilingual packaging + - do not copy or redistribute the bundled `arial-unicode-ms.ttf` asset without separate + confirmation or replacement + - do not let the row absorb the queued `rubix-cube-solver` companion lane + +## Why this is now the next packet + +The earlier `Phase 6R-A` queue head is already landed: + +- retained hyper puzzle catalog contract +- runtime family/notation resolution above that catalog seam + +That means the live queue advances to: + +- `Phase 6R-B` `kkoomen/qbr` + +with the adjacent retained rows still ordered behind it: + +- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` +- `Phase 6R-D` `roice3/MagicTile` + +## Required result + +The source-backed control pass for this packet is now complete. + +The first actual `6R-B` implementation packet should: + +- use the retained `0R-A` and `2R-A` authority surfaces plus the inspected `qbr` source basis +- define one bounded retained slice from `qbr` +- keep the slice inside the accepted recognition-substrate ownership +- widen only the first recognition contract family that can stand on its own without dragging in + UI, explanation, or companion-lane 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/qbr.py` +- `src/colordetection.py` +- `src/video.py` +- `src/config.py` +- `src/helpers.py` +- `src/constants.py` + +Inspected first-party receiving basis: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistVisionClient.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistHttpVisionClient.cpp` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockVisionClient.cpp` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp` + +## Narrowed 6R-B slice decision + +The first widening slice is now fixed as: + +1. classic-cube recognition calibration plus ordered face-observation contract + +not as the broader recognition shell. + +That narrowed slice should cover: + +- first-party calibration swatches and calibration-profile definition +- classic-cube face observation with: + - ordered stickers + - face id + - observation id + - commit-ready state +- session defaults for calibration profile and observation profile +- client capability exposure and mock/client contract responses +- subsystem normalization for the classic `cube/3x3x3` recognition route +- sample contract outputs and focused automation coverage + +## Deferred neighboring families + +The first `6R-B` implementation packet must keep these capability families closed: + +- webcam UI shell +- multilingual solve shell +- bundled-font redistribution +- multi-face reconstruction +- correction or solve-explanation flow +- downstream solver-or-companion ownership that belongs with `rubix-cube-solver` +- broader provider-backed recognition productionization beyond the retained contract seam + +Why this slice is first: + +- `qbr` exposes the strongest narrow retained seam at calibration and ordered face observation +- current first-party recognition code already has session, frame, preview, and commit envelopes, + but it still lacks a richer owned contract for calibration profiles and observed-face structure +- multi-face reconstruction and correction would prematurely absorb the next queued companion row + +## Out of scope for the first 6R-B packet + +- full webcam/browser UI transplant +- multilingual solve shell and bundled font asset redistribution +- multi-face solve-state reconstruction +- correction or explanation ownership +- `rubix-cube-solver` companion-lane widening +- `MagicTile` or broader topology widening + +## Queue-adjacent legal sequencing guards + +Do not omit the already-recorded legal caveats when the neighboring retained rows later come up: + +- `Phase 6R-B` `kkoomen/qbr` + - confirm or replace the bundled `src/assets/arial-unicode-ms.ttf` before direct redistribution + if any packet copies or ships that asset +- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` + - preserve or replace the bundled `frontend/lib/twistysim.min.js` with its upstream + provenance/license intact before redistribution if any packet copies or ships that dependency + +## Acceptance criteria + +- the packet records why the live queue now advances from landed `6R-A` to `6R-B` +- the packet promotes `qbr` as the current queue head without widening the neighboring retained + rows prematurely +- the packet records that the first `6R-B` widening slice is classic-cube recognition calibration + plus ordered face observation only +- the packet states exact out-of-scope families for the first `6R-B` pass +- the packet keeps the `qbr` font and `rubix-cube-solver` bundled dependency caveats visible in + later queue sequencing + +## Validation checklist + +1. confirm the landed `Phase 6R-A` packet is now the consumed prior queue head +2. confirm the retained recognition-substrate seam in `qbr` narrows cleanly to calibration and + ordered face observation +3. confirm the next packet is framed as bounded source-backed widening rather than README-only + routing + +That is the packet. diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md index 7d37889..573cca5 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -3,7 +3,7 @@ Status update on `2026-05-20`: - the canonical HyperTwist repo-row portfolio is now treated as `75` rows, not `71` -- currently implemented rows are now `25`, not `20` +- currently implemented rows are now `26`, not `20` - four of the additional current implemented rows are the later-landed restrictive clean-room lanes: - `cubing/alg.js` - `cubing/twisty.js` @@ -11,6 +11,8 @@ Status update on `2026-05-20`: - `kash/cubedesk` - the fifth additional current implemented row is the same-day bounded permissive partial row: - `HactarCE/Hyperspeedcube` +- the sixth additional current implemented row is the same-day bounded permissive partial row: + - `kkoomen/qbr` - the current next bounded move is **not** another restrictive packet by default - the earlier first-party packet block that was still being treated as next is now confirmed landed: - `d4f4ad3` `Add primary coach orchestration entry lane` @@ -21,8 +23,14 @@ Status update on `2026-05-20`: packet is now landed in current code - `HactarCE/Hyperspeedcube` remains partially incorporated; DSL, notation, replay verification, and stats-shape families stay deferred -- the current next bounded move is a source-backed `Phase 6R-B` `kkoomen/qbr` preparation/control - pass +- 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 current next bounded move is a source-backed `Phase 6R-C` + `vivaansinghvi07/rubix-cube-solver` preparation/control pass - the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than waiting on another first-party packet @@ -50,8 +58,8 @@ Donor-strength rule: - route difficulty changes how retained value may enter the product, not whether it may win technically - current shallow-eval set: `75` repos -- currently verified live/implemented in checked Unreal surfaces: `25` -- permissive live lanes: `14` +- currently verified live/implemented in checked Unreal surfaces: `26` +- permissive live lanes: `15` - boundary-sensitive live lanes: `6` - restrictive live lanes: `5` - the restrictive landed lanes are: @@ -61,7 +69,7 @@ Donor-strength rule: - `HactarCE/2x2x2x2-Scrambler` - `kash/cubedesk` - each restrictive landed lane is to be treated as properly clean-roomed and implemented afterward -- `Phase 0R` is now closed for the remaining `50` non-live rows +- `Phase 0R` is now closed for the remaining `49` non-live rows - `Phase 1R` is now closed as the retained-set contract and handoff overhaul - `Phase 2R` is now closed as the retained-set ownership and acceptance packet sequence - `Phase 3R-A` is now closed as the landed `Aarav2709/KubeTimr` timer subsystem widening packet @@ -81,18 +89,19 @@ Current routing truth: - retained rows total: `68` - discarded from the active retained set: `3` -- active non-live implementation-board rows: `38` +- active non-live implementation-board rows: `37` - retained benchmark, oracle, or clean-room-later rows outside the active implementation board: `9` -The next bounded move is a source-backed `Phase 6R-B` `kkoomen/qbr` preparation/control pass. +The next bounded move is a source-backed `Phase 6R-C` +`vivaansinghvi07/rubix-cube-solver` preparation/control pass. Queue interpretation after that packet: - then continue with the retained repo-row queue - highest current repo-row queue pressure sits in: - - `kkoomen/qbr` - `vivaansinghvi07/rubix-cube-solver` - `roice3/MagicTile` + - the bounded speech-input / voice sidecar set - `HactarCE/Hyperspeedcube` remains a partially landed row rather than a closed row: - landed: - puzzle catalog contract @@ -101,14 +110,21 @@ Queue interpretation after that packet: - notation / serialization - replay verification - stats-shape -- after the `Phase 6R-B` control pass, keep the adjacent legal sequencing guards visible: +- `kkoomen/qbr` remains a partially landed row rather than a closed row: + - landed: + - classic-cube recognition calibration contract + - ordered face-observation contract + - still deferred: + - webcam UI shell + - bundled-font redistribution and multilingual solve shell + - multi-face reconstruction / correction / explanation +- after the `Phase 6R-B` implementation packet, keep the adjacent legal sequencing guards visible: - `kkoomen/qbr` - - do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation - or replacement + - 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 - `vivaansinghvi07/rubix-cube-solver` - do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing its upstream provenance/license - - the bounded speech-input / voice sidecar set - keep the optional `Phase 3R-G` browser comparison lane deferred unless the landed primary browser spatial stack exposes a real gap