From 30b553b79255014a67b7836932e7e6c9e42ec46b Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Wed, 20 May 2026 22:33:19 +0200 Subject: [PATCH] Implement Phase 6R-A hyper puzzle catalog contract --- .../HyperTwistContractLibrary.cpp | 8 + .../HyperTwistTrainingCatalogLibrary.cpp | 107 ++++++++++++++ .../HyperTwistTrainingCoachLibrary.cpp | 10 +- .../HyperTwistTrainingLibrary.cpp | 18 +++ .../HyperTwistTrainingSubsystem.cpp | 18 +++ .../HyperTwistTrainingCatalogImportTypes.h | 106 +++++++++++++ .../HyperTwistTrainingCatalogLibrary.h | 15 ++ ...erspeedcubePhase6RACatalogContractTest.cpp | 139 ++++++++++++++++++ docs/REPO_LICENSE_TRACKING.md | 29 +++- ...ATALOG_IMPLEMENTATION_PACKET_2026-05-20.md | 130 ++++++++++++++++ ...ME_ANCHOR_PREPARATION_PACKET_2026-05-20.md | 6 +- .../HyperTwist/ROADMAP.md | 38 +++-- 12 files changed, 601 insertions(+), 23 deletions(-) create mode 100644 UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistHyperspeedcubePhase6RACatalogContractTest.cpp create mode 100644 docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_PUZZLE_CATALOG_IMPLEMENTATION_PACKET_2026-05-20.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp index ea3ea72..6b2d78e 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -114,6 +114,14 @@ FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzz FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleHyperPuzzleDefinition() { FHyperTwistPuzzleDefinitionRef Definition; + if (UHyperTwistTrainingCatalogLibrary::TryResolveRetainedHyperPuzzleDefinition( + TEXT("hypercube/2x2x2x2"), + Definition + )) + { + return Definition; + } + Definition.PuzzleId = TEXT("hypercube/2x2x2x2"); Definition.PuzzleFamily = EHyperTwistPuzzleFamily::Hypercube; Definition.Dimension = 4; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCatalogLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCatalogLibrary.cpp index 7f69dd0..a1fdc8b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCatalogLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCatalogLibrary.cpp @@ -7,6 +7,26 @@ namespace HyperTwistTrainingCatalogLibraryInternal { + FHyperTwistRetainedHyperPuzzleCatalogEntry MakeRetainedHyperCatalogEntry( + const FString& PuzzleId, + const FString& DisplayName, + const TArray& SizeVector, + const FString& Variant, + const TArray& Tags, + const TArray& AliasPuzzleIds + ) + { + FHyperTwistRetainedHyperPuzzleCatalogEntry Entry; + Entry.PuzzleId = PuzzleId; + Entry.DisplayName = DisplayName; + Entry.Dimension = SizeVector.Num(); + Entry.SizeVector = SizeVector; + Entry.Variant = Variant; + Entry.Tags = Tags; + Entry.AliasPuzzleIds = AliasPuzzleIds; + return Entry; + } + template bool LoadStructFromJsonFile( const FString& JsonPath, @@ -680,6 +700,93 @@ namespace HyperTwistTrainingCatalogLibraryInternal } } +FHyperTwistRetainedHyperPuzzleCatalog UHyperTwistTrainingCatalogLibrary::MakeRetainedHyperPuzzleCatalog() +{ + FHyperTwistRetainedHyperPuzzleCatalog Catalog; + Catalog.Entries = + { + HyperTwistTrainingCatalogLibraryInternal::MakeRetainedHyperCatalogEntry( + TEXT("hypercube/3x3x3x3"), + TEXT("3x3x3x3"), + {3, 3, 3, 3}, + FString(), + {TEXT("hypercube"), TEXT("virtual"), TEXT("entry"), TEXT("retained/hyperspeedcube")}, + {TEXT("3x3x3x3")} + ), + HyperTwistTrainingCatalogLibraryInternal::MakeRetainedHyperCatalogEntry( + TEXT("hypercube/2x2x2x2"), + TEXT("2x2x2x2"), + {2, 2, 2, 2}, + FString(), + {TEXT("hypercube"), TEXT("virtual"), TEXT("compact"), TEXT("retained/hyperspeedcube")}, + {TEXT("2x2x2x2")} + ), + HyperTwistTrainingCatalogLibraryInternal::MakeRetainedHyperCatalogEntry( + TEXT("hypercube/4x4x4x4"), + TEXT("4x4x4x4"), + {4, 4, 4, 4}, + FString(), + {TEXT("hypercube"), TEXT("virtual"), TEXT("extended"), TEXT("retained/hyperspeedcube")}, + {TEXT("4x4x4x4")} + ), + HyperTwistTrainingCatalogLibraryInternal::MakeRetainedHyperCatalogEntry( + TEXT("hypercube/3x3x3x3x3"), + TEXT("3x3x3x3x3"), + {3, 3, 3, 3, 3}, + FString(), + {TEXT("hypercube"), TEXT("virtual"), TEXT("higher-dimensional"), TEXT("retained/hyperspeedcube")}, + {TEXT("3x3x3x3x3")} + ), + HyperTwistTrainingCatalogLibraryInternal::MakeRetainedHyperCatalogEntry( + TEXT("hypercube/2x2x2x2/physical"), + TEXT("Physical 2x2x2x2"), + {2, 2, 2, 2}, + TEXT("physical"), + {TEXT("hypercube"), TEXT("physical"), TEXT("retained/hyperspeedcube")}, + {TEXT("phys_2x2x2x2")} + ) + }; + return Catalog; +} + +bool UHyperTwistTrainingCatalogLibrary::TryGetRetainedHyperPuzzleCatalogEntry( + const FString& PuzzleId, + FHyperTwistRetainedHyperPuzzleCatalogEntry& OutEntry +) +{ + if (PuzzleId.IsEmpty()) + { + return false; + } + + const FHyperTwistRetainedHyperPuzzleCatalog Catalog = MakeRetainedHyperPuzzleCatalog(); + for (const FHyperTwistRetainedHyperPuzzleCatalogEntry& Entry : Catalog.Entries) + { + if (Entry.MatchesPuzzleId(PuzzleId)) + { + OutEntry = Entry; + return true; + } + } + + return false; +} + +bool UHyperTwistTrainingCatalogLibrary::TryResolveRetainedHyperPuzzleDefinition( + const FString& PuzzleId, + FHyperTwistPuzzleDefinitionRef& OutDefinition +) +{ + FHyperTwistRetainedHyperPuzzleCatalogEntry Entry; + if (!TryGetRetainedHyperPuzzleCatalogEntry(PuzzleId, Entry)) + { + return false; + } + + OutDefinition = Entry.ToDefinitionRef(); + return true; +} + FHyperTwistContentPack UHyperTwistTrainingCatalogLibrary::MakeAlgTrainerStarterContentPack() { const FHyperTwistTrainingCase OLLT01 = HyperTwistTrainingCatalogLibraryInternal::MakeAlgTrainerCase( diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index 1b6d5f6..caf418f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -20583,7 +20583,15 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionOwnedExec const auto IncrementPuzzleFamilyCounts = [&Surface](const FString& PuzzleId) { - if (PuzzleId.StartsWith(TEXT("cube/"))) + FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; + if (UHyperTwistTrainingCatalogLibrary::TryResolveRetainedHyperPuzzleDefinition( + PuzzleId, + RetainedHyperDefinition + )) + { + ++Surface.HypercubePuzzleOptionCount; + } + else if (PuzzleId.StartsWith(TEXT("cube/"))) { ++Surface.CubePuzzleOptionCount; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingLibrary.cpp index e9e8871..037dc94 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingLibrary.cpp @@ -340,6 +340,15 @@ namespace HyperTwistTrainingLibraryInternal FHyperTwistPuzzleDefinitionRef MakeDefinitionForCase(const FHyperTwistTrainingCase& TrainingCase) { + FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; + if (UHyperTwistTrainingCatalogLibrary::TryResolveRetainedHyperPuzzleDefinition( + TrainingCase.PuzzleId, + RetainedHyperDefinition + )) + { + return RetainedHyperDefinition; + } + FHyperTwistPuzzleDefinitionRef Definition; Definition.PuzzleId = TrainingCase.PuzzleId; Definition.DefinitionVersion = TEXT("2026.04"); @@ -454,6 +463,15 @@ namespace HyperTwistTrainingLibraryInternal bool MatchesPuzzleFamily(const FString& PuzzleId, EHyperTwistPuzzleFamily PuzzleFamily) { + FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; + if (UHyperTwistTrainingCatalogLibrary::TryResolveRetainedHyperPuzzleDefinition( + PuzzleId, + RetainedHyperDefinition + )) + { + return RetainedHyperDefinition.PuzzleFamily == PuzzleFamily; + } + switch (PuzzleFamily) { case EHyperTwistPuzzleFamily::ClassicCube: diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index e74dfe9..b97a311 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -1868,6 +1868,15 @@ namespace HyperTwistTrainingSubsystemInternal EHyperTwistPuzzleFamily ResolvePuzzleFamilyFromPuzzleId(const FString& PuzzleId) { + FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; + if (UHyperTwistTrainingCatalogLibrary::TryResolveRetainedHyperPuzzleDefinition( + PuzzleId, + RetainedHyperDefinition + )) + { + return RetainedHyperDefinition.PuzzleFamily; + } + if (PuzzleId.StartsWith(TEXT("hypercube/"))) { return EHyperTwistPuzzleFamily::Hypercube; @@ -1881,6 +1890,15 @@ namespace HyperTwistTrainingSubsystemInternal FString ResolveNotationProfileFromPuzzleId(const FString& PuzzleId) { + FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition; + if (UHyperTwistTrainingCatalogLibrary::TryResolveRetainedHyperPuzzleDefinition( + PuzzleId, + RetainedHyperDefinition + )) + { + return RetainedHyperDefinition.NotationProfile; + } + if (PuzzleId.StartsWith(TEXT("hypercube/"))) { return TEXT("hyper-4d"); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCatalogImportTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCatalogImportTypes.h index 370d0a0..452fc09 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCatalogImportTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCatalogImportTypes.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "HyperTwistCore/HyperTwistCoreTypes.h" #include "HyperTwistTrainingCatalogImportTypes.generated.h" USTRUCT(BlueprintType) @@ -682,3 +683,108 @@ struct FHyperTwistTrainingCatalogImportLoadReport && bLoadedReferenceTargets; } }; + +USTRUCT(BlueprintType) +struct FHyperTwistRetainedHyperPuzzleCatalogEntry +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PuzzleId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DisplayName; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistPuzzleFamily PuzzleFamily = EHyperTwistPuzzleFamily::Hypercube; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 Dimension = 4; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DefinitionVersion = TEXT("2026.04"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString NotationProfile = TEXT("hyper-4d"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray SizeVector; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Variant; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Tags; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray AliasPuzzleIds; + + bool IsStructurallyValid() const + { + return !PuzzleId.IsEmpty() + && !DisplayName.IsEmpty() + && Dimension > 0 + && !DefinitionVersion.IsEmpty() + && !NotationProfile.IsEmpty() + && SizeVector.Num() == Dimension; + } + + bool MatchesPuzzleId(const FString& CandidatePuzzleId) const + { + if (CandidatePuzzleId.Equals(PuzzleId, ESearchCase::IgnoreCase)) + { + return true; + } + + return AliasPuzzleIds.ContainsByPredicate([&CandidatePuzzleId](const FString& AliasPuzzleId) + { + return CandidatePuzzleId.Equals(AliasPuzzleId, ESearchCase::IgnoreCase); + }); + } + + FHyperTwistPuzzleDefinitionRef ToDefinitionRef() const + { + FHyperTwistPuzzleDefinitionRef Definition; + Definition.PuzzleId = PuzzleId; + Definition.PuzzleFamily = PuzzleFamily; + Definition.Dimension = Dimension; + Definition.DefinitionVersion = DefinitionVersion; + Definition.NotationProfile = NotationProfile; + Definition.SizeVector = SizeVector; + Definition.Variant = Variant; + return Definition; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistRetainedHyperPuzzleCatalog +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CatalogId = TEXT("retained/hyperspeedcube/hyper-puzzles"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CatalogVersion = TEXT("2026.05"); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray Entries; + + bool IsStructurallyValid() const + { + if (CatalogId.IsEmpty() || CatalogVersion.IsEmpty() || Entries.Num() == 0) + { + return false; + } + + for (const FHyperTwistRetainedHyperPuzzleCatalogEntry& Entry : Entries) + { + if (!Entry.IsStructurallyValid()) + { + return false; + } + } + + return true; + } +}; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCatalogLibrary.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCatalogLibrary.h index ca57fbc..a7e0b1d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCatalogLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCatalogLibrary.h @@ -12,6 +12,21 @@ class UNREALHYPERTWIST_API UHyperTwistTrainingCatalogLibrary : public UBlueprint GENERATED_BODY() public: + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Catalog") + static FHyperTwistRetainedHyperPuzzleCatalog MakeRetainedHyperPuzzleCatalog(); + + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Catalog") + static bool TryGetRetainedHyperPuzzleCatalogEntry( + const FString& PuzzleId, + FHyperTwistRetainedHyperPuzzleCatalogEntry& OutEntry + ); + + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Catalog") + static bool TryResolveRetainedHyperPuzzleDefinition( + const FString& PuzzleId, + FHyperTwistPuzzleDefinitionRef& OutDefinition + ); + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Catalog") static FHyperTwistContentPack MakeAlgTrainerStarterContentPack(); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistHyperspeedcubePhase6RACatalogContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistHyperspeedcubePhase6RACatalogContractTest.cpp new file mode 100644 index 0000000..7f063d2 --- /dev/null +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistHyperspeedcubePhase6RACatalogContractTest.cpp @@ -0,0 +1,139 @@ +// Copyright HyperTwist, Inc. All Rights Reserved. + +#include "Misc/AutomationTest.h" + +#include "HyperTwistBootstrap/HyperTwistContractLibrary.h" +#include "HyperTwistTraining/HyperTwistTrainingCatalogLibrary.h" +#include "HyperTwistTraining/HyperTwistTrainingLibrary.h" + +#if WITH_AUTOMATION_TESTS + +namespace HyperTwistHyperspeedcubePhase6RATestInternal +{ + FHyperTwistTrainingDeck MakeHypercubeDeck(const FString& PuzzleId) + { + FHyperTwistTrainingDeck Deck; + Deck.DeckId = TEXT("phase6r-a/hypercube-catalog"); + Deck.Title = TEXT("Phase 6R-A Hyper Puzzle Catalog"); + Deck.DeliveryModes = {EHyperTwistTrainingDeliveryMode::Timer}; + + FHyperTwistTrainingCase TrainingCase; + TrainingCase.CaseId = TEXT("phase6r-a-case"); + TrainingCase.PuzzleId = PuzzleId; + TrainingCase.PromptLabel = TEXT("Phase 6R-A Hyper Puzzle"); + TrainingCase.AllowedDeliveryModes = {EHyperTwistTrainingDeliveryMode::Timer}; + Deck.Cases = {TrainingCase}; + return Deck; + } +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistHyperspeedcubePhase6RACatalogLookupTest, + "HyperTwist.Permissive.Hyperspeedcube.Phase6R.A.CatalogLookup", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistHyperspeedcubePhase6RACatalogLookupTest::RunTest(const FString& Parameters) +{ + const FHyperTwistRetainedHyperPuzzleCatalog Catalog = + UHyperTwistTrainingCatalogLibrary::MakeRetainedHyperPuzzleCatalog(); + + TestTrue(TEXT("The retained hyper puzzle catalog must be structurally valid."), Catalog.IsStructurallyValid()); + TestTrue(TEXT("The retained hyper puzzle catalog must register multiple retained entries."), Catalog.Entries.Num() >= 5); + + FHyperTwistRetainedHyperPuzzleCatalogEntry VirtualEntry; + TestTrue( + TEXT("The catalog must resolve the virtual 3x3x3x3 alias."), + UHyperTwistTrainingCatalogLibrary::TryGetRetainedHyperPuzzleCatalogEntry(TEXT("3x3x3x3"), VirtualEntry) + ); + TestEqual( + TEXT("The virtual 3x3x3x3 alias must map to the canonical retained puzzle id."), + VirtualEntry.PuzzleId, + TEXT("hypercube/3x3x3x3") + ); + TestEqual( + TEXT("The virtual 3x3x3x3 alias must resolve as a 4D hypercube."), + VirtualEntry.Dimension, + 4 + ); + + FHyperTwistRetainedHyperPuzzleCatalogEntry PhysicalEntry; + TestTrue( + TEXT("The catalog must resolve the physical 2x2x2x2 alias."), + UHyperTwistTrainingCatalogLibrary::TryGetRetainedHyperPuzzleCatalogEntry(TEXT("phys_2x2x2x2"), PhysicalEntry) + ); + TestEqual( + TEXT("The physical 2x2x2x2 alias must map to the canonical retained puzzle id."), + PhysicalEntry.PuzzleId, + TEXT("hypercube/2x2x2x2/physical") + ); + TestEqual( + TEXT("The physical 2x2x2x2 entry must preserve the physical variant label."), + PhysicalEntry.Variant, + TEXT("physical") + ); + + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistHyperspeedcubePhase6RATrainingRunDefinitionTest, + "HyperTwist.Permissive.Hyperspeedcube.Phase6R.A.TrainingRunDefinition", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistHyperspeedcubePhase6RATrainingRunDefinitionTest::RunTest(const FString& Parameters) +{ + const FHyperTwistTrainingRunState RunState = UHyperTwistTrainingLibrary::StartTrainingRun( + HyperTwistHyperspeedcubePhase6RATestInternal::MakeHypercubeDeck(TEXT("3x3x3x3")), + TEXT("phase6r-a-user"), + TEXT("phase6r-a-session"), + EHyperTwistTrainingDeliveryMode::Timer + ); + + TestTrue(TEXT("The bounded 6R-A deck must start a structurally valid run."), RunState.IsStructurallyValid()); + TestEqual( + TEXT("The replay packet must promote the virtual 3x3x3x3 alias to the canonical retained puzzle id."), + RunState.ReplayPacket.PuzzleDefinition.PuzzleId, + TEXT("hypercube/3x3x3x3") + ); + TestEqual( + TEXT("The replay packet must retain the retained hyper notation profile."), + RunState.ReplayPacket.PuzzleDefinition.NotationProfile, + TEXT("hyper-4d") + ); + TestEqual( + TEXT("The replay packet must retain the 4D hypercube family."), + RunState.ReplayPacket.PuzzleDefinition.PuzzleFamily, + EHyperTwistPuzzleFamily::Hypercube + ); + TestEqual( + TEXT("The replay packet must retain the 4D hypercube dimension."), + RunState.ReplayPacket.PuzzleDefinition.Dimension, + 4 + ); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistHyperspeedcubePhase6RASampleDefinitionTest, + "HyperTwist.Permissive.Hyperspeedcube.Phase6R.A.SampleDefinition", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistHyperspeedcubePhase6RASampleDefinitionTest::RunTest(const FString& Parameters) +{ + const FHyperTwistPuzzleDefinitionRef Definition = + UHyperTwistContractLibrary::MakeSampleHyperPuzzleDefinition(); + + TestEqual( + TEXT("The sample hyper definition must resolve through the retained hyper puzzle catalog."), + Definition.PuzzleId, + TEXT("hypercube/2x2x2x2") + ); + TestEqual(TEXT("The sample hyper definition must remain 4D."), Definition.Dimension, 4); + TestEqual(TEXT("The sample hyper definition must retain the hyper notation profile."), Definition.NotationProfile, TEXT("hyper-4d")); + return true; +} + +#endif diff --git a/docs/REPO_LICENSE_TRACKING.md b/docs/REPO_LICENSE_TRACKING.md index fe77e21..5ead8ce 100644 --- a/docs/REPO_LICENSE_TRACKING.md +++ b/docs/REPO_LICENSE_TRACKING.md @@ -94,33 +94,39 @@ 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 `24` +- the current implemented-row count is `25` - the four later restrictive clean-room rows now implemented after the original `2026-05-13` truth block are: - `cubing/alg.js` - `cubing/twisty.js` - `HactarCE/2x2x2x2-Scrambler` - `kash/cubedesk` +- the fifth additional implemented row is the same-day bounded permissive partial row: + - `HactarCE/Hyperspeedcube` - 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 current next bounded move is the source-backed `Phase 6R-A` `HactarCE/Hyperspeedcube` - runtime-anchor widening packet, not a new restrictive packet by default +- 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 - 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: `24` -- permissive live lanes: `13` +- currently verified live/implemented in checked `UnrealHyperTwist` surfaces: `25` +- permissive live lanes: `14` - boundary-sensitive live lanes: `6` - restrictive live lanes: `5` -The thirteen permissive live lanes are: +The fourteen permissive live lanes are: - `Aarav2709/KubeTimr` +- `HactarCE/Hyperspeedcube` - `apache/echarts` - `abunickabhi/5style-Trainer` - `google/model-viewer` @@ -203,8 +209,7 @@ 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 the bounded `Phase 6R-A` `HactarCE/Hyperspeedcube` runtime-anchor - widening packet +- the next bounded move is a source-backed `Phase 6R-B` `kkoomen/qbr` preparation/control pass Companion docs: @@ -219,6 +224,7 @@ Companion docs: - `docs/HYPERTWIST_PHASE_3R_PACKET_3R_A_KUBETIMR_IMPLEMENTATION_2026-05-13.md` - `docs/HYPERTWIST_PHASE_3R_PACKET_3R_B_HYPERCUBING_XYZ_IMPLEMENTATION_2026-05-13.md` - `docs/HYPERTWIST_PHASE_3R_PACKET_3R_F_BROWSER_SPATIAL_SUPPORT_IMPLEMENTATION_2026-05-13.md` +- `docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_PUZZLE_CATALOG_IMPLEMENTATION_PACKET_2026-05-20.md` - `docs/HYPERTWIST_REPO_STATE_BOARD_2026-05-11.md` ## Landed live-lane legal records @@ -1196,6 +1202,10 @@ Current licensing judgment: - not a clean-room case - treat as a bounded permissive donor candidate whose technical widening is still constrained by the retained `Phase 6R-A` ownership contract +- the first narrower bounded implementation slice is now landed in current code: + - hyper puzzle catalog contract +- the row remains only partially incorporated; DSL, notation, replay verification, and stats-shape + widening remain deferred Source basis: @@ -1231,6 +1241,9 @@ 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 ### `coqui-ai/TTS` diff --git a/docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_PUZZLE_CATALOG_IMPLEMENTATION_PACKET_2026-05-20.md b/docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_PUZZLE_CATALOG_IMPLEMENTATION_PACKET_2026-05-20.md new file mode 100644 index 0000000..b0c856f --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_PUZZLE_CATALOG_IMPLEMENTATION_PACKET_2026-05-20.md @@ -0,0 +1,130 @@ +# HyperTwist Phase 6R-A Hyperspeedcube puzzle catalog implementation packet + +Created on `2026-05-20` + +## Status + +- first-party HyperTwist packet +- bounded permissive `Phase 6R-A` implementation slice + +## Purpose + +This packet lands the first narrower bounded slice from the retained `HactarCE/Hyperspeedcube` +runtime-anchor row. + +The landed slice is: + +- first-party hyper puzzle catalog contract + +It is not: + +- a full `Hyperspeedcube` row transplant +- a puzzle-definition DSL packet +- a notation parser or serializer packet +- a replay verification packet +- a stats-shape packet +- a `qbr`, `rubix-cube-solver`, or `MagicTile` 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_A_HYPERSPEEDCUBE_RUNTIME_ANCHOR_PREPARATION_PACKET_2026-05-20.md` + +The retained owner remains: + +- `HactarCE/Hyperspeedcube` + +The granted family remains bounded to: + +- puzzle catalog contract +- puzzle-definition DSL boundary +- notation grammar and serialization boundary +- replay-event taxonomy and verification boundary +- stats-shape and solve-record contract + +This packet lands only the first family in that granted set. + +## Landed scope + +The current code now owns a retained hyper puzzle catalog contract through: + +- retained catalog contract types for canonical hyper puzzle entries and the catalog wrapper +- `UHyperTwistTrainingCatalogLibrary` catalog construction and lookup helpers +- canonical hyper-definition resolution for: + - `3x3x3x3` + - `2x2x2x2` + - `4x4x4x4` + - `3x3x3x3x3` + - `phys_2x2x2x2` +- replay-packet definition shaping in `UHyperTwistTrainingLibrary` +- session/runtime family and notation resolution in `UHyperTwistTrainingSubsystem` +- coach option family counting in `UHyperTwistTrainingCoachLibrary` +- sample hyper definition routing in `UHyperTwistContractLibrary` + +## Why this is still intentionally bounded + +This packet replaces sample / heuristic hyper puzzle identity with a catalog-owned contract, but it +does not widen into the neighboring retained families. + +Still deferred: + +- puzzle-definition DSL parsing and authoring +- notation parsing and serialization +- replay-event taxonomy widening and replay verification +- stats-shape / solve-record widening +- broad view / simulation UX +- non-Euclidean topology ownership +- `qbr` calibration / perception ownership +- `rubix-cube-solver` correction / explanation ownership + +## Validation + +Build validation: + +- `Build.bat UnrealHyperTwistEditor Win64 Development -Project='C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject' -WaitMutex -NoHotReloadFromIDE` + +Focused automation validation: + +- `Automation RunTests HyperTwist.Permissive.Hyperspeedcube.Phase6R.A` + +Regression automation validation: + +- `Automation RunTests HyperTwist.CleanRoom.CubeDesk` +- `Automation RunTests HyperTwist.CleanRoom.HactarCE` + +Expected covered tests: + +- `CatalogLookup` +- `SampleDefinition` +- `TrainingRunDefinition` +- existing `CubeDesk` clean-room regression suite +- existing `HactarCE` clean-room regression suite + +## Queue effect + +This packet consumes the current `Phase 6R-A` implementation slice. + +`HactarCE/Hyperspeedcube` remains only partially incorporated: + +- landed now: + - puzzle catalog contract +- still deferred: + - DSL + - notation + - replay verification + - stats-shape + +The next clean move is not another immediate `Hyperspeedcube` widening by default. + +The next queue head should be: + +- a source-backed `Phase 6R-B` `kkoomen/qbr` preparation/control pass + +Keep the legal sequencing guard visible there: + +- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation or + replacement diff --git a/docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_RUNTIME_ANCHOR_PREPARATION_PACKET_2026-05-20.md b/docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_RUNTIME_ANCHOR_PREPARATION_PACKET_2026-05-20.md index c79c1bb..295b903 100644 --- a/docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_RUNTIME_ANCHOR_PREPARATION_PACKET_2026-05-20.md +++ b/docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_RUNTIME_ANCHOR_PREPARATION_PACKET_2026-05-20.md @@ -4,8 +4,10 @@ Created on `2026-05-20` ## Status -- first-party HyperTwist packet +- historical same-day preparation authority - bounded post-Phase-`5R` preparation slice +- the first bounded implementation slice now lands separately under: + - `docs/arch/HYPERTWIST_PHASE6R_A_HYPERSPEEDCUBE_PUZZLE_CATALOG_IMPLEMENTATION_PACKET_2026-05-20.md` ## Purpose @@ -183,7 +185,7 @@ Before widening starts, that packet should: ## Suggested next packet -The next explicit implementation-facing packet after this preparation slice should be: +The next explicit implementation-facing packet after this preparation slice is now: - a bounded permissive `Phase 6R-A` `HactarCE/Hyperspeedcube` puzzle-catalog contract widening packet diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md index 97e3135..7d37889 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md @@ -3,20 +3,26 @@ 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 `24`, not `20` -- the additional current implemented rows are the later-landed restrictive clean-room lanes: +- currently implemented rows are now `25`, not `20` +- four of the additional current implemented rows are the later-landed restrictive clean-room lanes: - `cubing/alg.js` - `cubing/twisty.js` - `HactarCE/2x2x2x2-Scrambler` - `kash/cubedesk` +- the fifth additional current implemented row is the same-day bounded permissive partial row: + - `HactarCE/Hyperspeedcube` - 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` - `1860f14` `Add provider-backed recognition sidecar client` - `2473665` `Overlay imported training catalog packs and finite bundles` - the generic source-backed `Phase 6R-A` `HactarCE/Hyperspeedcube` control pass is now consumed -- the current next bounded move is the bounded permissive `Phase 6R-A` - `HactarCE/Hyperspeedcube` puzzle-catalog contract widening packet +- the bounded permissive `Phase 6R-A` `HactarCE/Hyperspeedcube` puzzle-catalog contract widening + 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 repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than waiting on another first-party packet @@ -44,8 +50,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: `24` -- permissive live lanes: `13` +- currently verified live/implemented in checked Unreal surfaces: `25` +- permissive live lanes: `14` - boundary-sensitive live lanes: `6` - restrictive live lanes: `5` - the restrictive landed lanes are: @@ -55,7 +61,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 `51` non-live rows +- `Phase 0R` is now closed for the remaining `50` 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 @@ -75,11 +81,10 @@ Current routing truth: - retained rows total: `68` - discarded from the active retained set: `3` -- active non-live implementation-board rows: `39` +- active non-live implementation-board rows: `38` - retained benchmark, oracle, or clean-room-later rows outside the active implementation board: `9` -The next bounded move is the bounded `Phase 6R-A` `HactarCE/Hyperspeedcube` puzzle-catalog -contract widening packet. +The next bounded move is a source-backed `Phase 6R-B` `kkoomen/qbr` preparation/control pass. Queue interpretation after that packet: @@ -87,14 +92,23 @@ Queue interpretation after that packet: - highest current repo-row queue pressure sits in: - `kkoomen/qbr` - `vivaansinghvi07/rubix-cube-solver` - - the bounded speech-input / voice sidecar set -- keep the adjacent legal sequencing guards visible when those rows come up: + - `roice3/MagicTile` +- `HactarCE/Hyperspeedcube` remains a partially landed row rather than a closed row: + - landed: + - puzzle catalog contract + - still deferred: + - DSL + - notation / serialization + - replay verification + - stats-shape +- after the `Phase 6R-B` control pass, 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 - `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