Implement Phase 6R-Q solve explanation shell

This commit is contained in:
axiomlogicnexus 2026-05-23 21:03:45 +02:00
parent 3c1a025e3c
commit 27615949d7
10 changed files with 1151 additions and 26 deletions

View file

@ -335,6 +335,123 @@ namespace HyperTwistContractLibraryInternal
return Profile;
}
FHyperTwistVisionSolveExplanationProfile MakeDefaultClassicCubeSolveExplanationProfile(
const FString& ProfileId
)
{
FHyperTwistVisionSolveExplanationProfile Profile;
Profile.ExplanationProfileId = ProfileId;
Profile.PuzzleId = TEXT("cube/3x3x3");
Profile.RecommendationMode = TEXT("bounded-stage-ladder-v1");
Profile.bSupportsStageRecommendation = true;
Profile.bSupportsStepNavigation = true;
Profile.bSupportsPlaybackStage = false;
auto AddStep = [&Profile](
const TCHAR* StepId,
const TCHAR* StageId,
const TCHAR* Title,
const TCHAR* Description,
const TCHAR* RecommendationLine,
const int32 StageOrdinal
)
{
FHyperTwistVisionSolveExplanationStep Step;
Step.StepId = StepId;
Step.StageId = StageId;
Step.Title = Title;
Step.Description = Description;
Step.RecommendationLine = RecommendationLine;
Step.StageOrdinal = StageOrdinal;
Step.bPlaybackDeferred = true;
Profile.Steps.Add(Step);
};
AddStep(
TEXT("orient-centers-step"),
TEXT("orient-centers"),
TEXT("Orient Centers"),
TEXT("Check the cube orientation and align the center reference before turning into the solve ladder."),
TEXT("Confirm the edited net and lock the reference orientation before solve guidance begins."),
0
);
AddStep(
TEXT("solve-white-cross-step"),
TEXT("solve-white-cross"),
TEXT("Build First Cross"),
TEXT("Assemble the opening cross with edge placement matched against the side colors."),
TEXT("Start with a stable cross so later first-layer turns stay bounded and readable."),
1
);
AddStep(
TEXT("solve-first-layer-corners-step"),
TEXT("solve-first-layer-corners"),
TEXT("Place First-Layer Corners"),
TEXT("Insert the matching corners to finish the first layer without disturbing the cross."),
TEXT("Close the first layer before moving into the middle-band edge work."),
2
);
AddStep(
TEXT("solve-second-layer-edges-step"),
TEXT("solve-second-layer-edges"),
TEXT("Solve Middle-Layer Edges"),
TEXT("Move the non-top edges into their middle-layer slots while preserving the solved first layer."),
TEXT("Complete the middle layer so orientation-only work remains on the last layer."),
3
);
AddStep(
TEXT("solve-oll-edges-step"),
TEXT("solve-oll-edges"),
TEXT("Orient Last-Layer Edges"),
TEXT("Turn the last-layer edges so the top-face color is established across the edge ring."),
TEXT("Orient the last-layer edges before promoting corner orientation."),
4
);
AddStep(
TEXT("solve-oll-corners-step"),
TEXT("solve-oll-corners"),
TEXT("Orient Last-Layer Corners"),
TEXT("Resolve the remaining top-face corner orientation while keeping the lower layers intact."),
TEXT("Finish top-face orientation before any final permutation pass."),
5
);
AddStep(
TEXT("solve-pll-corners-step"),
TEXT("solve-pll-corners"),
TEXT("Permute Last-Layer Corners"),
TEXT("Cycle the last-layer corners into their destination positions after orientation is complete."),
TEXT("Place the last-layer corners first so the final edge permutation stays bounded."),
6
);
AddStep(
TEXT("solve-pll-edges-step"),
TEXT("solve-pll-edges"),
TEXT("Permute Last-Layer Edges"),
TEXT("Finish the solve by cycling the last-layer edges into their final positions."),
TEXT("Close with the last-layer edges; playback ownership remains deferred in this packet."),
7
);
auto AddAction = [&Profile](
const TCHAR* ActionId,
const TCHAR* InputBinding,
const TCHAR* SurfaceId
)
{
FHyperTwistVisionShellActionBinding ActionBinding;
ActionBinding.ActionId = ActionId;
ActionBinding.InputBinding = InputBinding;
ActionBinding.SurfaceId = SurfaceId;
Profile.ActionBindings.Add(ActionBinding);
};
AddAction(TEXT("start-solve-explanation"), TEXT("button:start-explanation"), TEXT("cube-edit"));
AddAction(TEXT("step-solve-forward"), TEXT("button:next-step"), TEXT("solve-explanation-stage"));
AddAction(TEXT("step-solve-backward"), TEXT("button:previous-step"), TEXT("solve-explanation-stage"));
AddAction(TEXT("restart-solve-explanation"), TEXT("button:restart-explanation"), TEXT("solve-explanation-stage"));
return Profile;
}
void ResolveMockClassicFaceSwatch(
const FString& FaceId,
FString& OutColorId,
@ -600,6 +717,64 @@ namespace HyperTwistContractLibraryInternal
return BrowserShellState;
}
FHyperTwistVisionSolveExplanationState MakeClassicCubeSolveExplanationState(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession
)
{
FHyperTwistVisionSolveExplanationState SolveExplanationState;
const FHyperTwistVisionSolveExplanationProfile SolveExplanationProfile =
SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid()
? SessionConfig.SolveExplanationProfileDefinition
: MakeDefaultClassicCubeSolveExplanationProfile(
!SessionConfig.SolveExplanationProfileId.IsEmpty()
? SessionConfig.SolveExplanationProfileId
: TEXT("classic-cube-solve-explanation-v1")
);
SolveExplanationState.ExplanationProfileId = SolveExplanationProfile.ExplanationProfileId;
SolveExplanationState.TotalStepCount = SolveExplanationProfile.Steps.Num();
SolveExplanationState.bPlaybackDeferred = !SolveExplanationProfile.bSupportsPlaybackStage;
SolveExplanationState.bRequiresFullReconstruction = true;
const int32 RemainingFaceCount = ReconstructionSession.IsStructurallyValid()
? ReconstructionSession.MissingFaces.Num()
: 6;
if (ReconstructionSession.bHasCompleteClassicCubeNet)
{
SolveExplanationState.RecommendedStageId =
SolveExplanationProfile.Steps.Num() > 0
? SolveExplanationProfile.Steps[0].StageId
: TEXT("orient-centers");
SolveExplanationState.RecommendationStatusLine =
TEXT("Solve guidance is ready after cube-edit review.");
SolveExplanationState.RecommendationDetailLine =
TEXT("Review any manual corrections, then start the bounded stage ladder. Playback ownership stays deferred in this packet.");
SolveExplanationState.CurrentStepOrdinal = -1;
SolveExplanationState.bReadyForExplanation = true;
SolveExplanationState.bStartExplanationVisible = true;
SolveExplanationState.bNextStepVisible = false;
SolveExplanationState.bPreviousStepVisible = false;
}
else
{
SolveExplanationState.RecommendedStageId = TEXT("capture-remaining-faces");
SolveExplanationState.RecommendationStatusLine =
TEXT("Solve guidance unlocks after capture completes.");
SolveExplanationState.RecommendationDetailLine = FString::Printf(
TEXT("Commit the remaining %d face(s) and finish the bounded reconstruction pass before solve guidance unlocks."),
RemainingFaceCount
);
SolveExplanationState.CurrentStepOrdinal = -1;
SolveExplanationState.bReadyForExplanation = false;
SolveExplanationState.bStartExplanationVisible = false;
SolveExplanationState.bNextStepVisible = false;
SolveExplanationState.bPreviousStepVisible = false;
}
return SolveExplanationState;
}
}
FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition()
@ -839,6 +1014,11 @@ FHyperTwistVisionSessionConfig UHyperTwistContractLibrary::MakeSampleVisionSessi
HyperTwistContractLibraryInternal::MakeDefaultClassicCubeBrowserShellProfile(
Config.BrowserShellProfileId
);
Config.SolveExplanationProfileId = TEXT("classic-cube-solve-explanation-v1");
Config.SolveExplanationProfileDefinition =
HyperTwistContractLibraryInternal::MakeDefaultClassicCubeSolveExplanationProfile(
Config.SolveExplanationProfileId
);
Config.FrameSize = FIntPoint(1280, 720);
Config.MaxFrameRate = 30;
return Config;
@ -898,17 +1078,23 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe
{TEXT("F")},
Result.Confidence
);
const FHyperTwistVisionSessionConfig SessionConfig =
UHyperTwistContractLibrary::MakeSampleVisionSessionConfig();
Result.ShellState = HyperTwistContractLibraryInternal::MakeClassicCubeWebcamShellState(
UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(),
SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace.FaceId,
Result.CommittedUnit
);
Result.BrowserShellState = HyperTwistContractLibraryInternal::MakeClassicCubeBrowserShellState(
UHyperTwistContractLibrary::MakeSampleVisionSessionConfig(),
SessionConfig,
Result.ReconstructionSession,
Result.ObservedFace
);
Result.SolveExplanationState = HyperTwistContractLibraryInternal::MakeClassicCubeSolveExplanationState(
SessionConfig,
Result.ReconstructionSession
);
return Result;
}

View file

@ -2471,6 +2471,123 @@ namespace HyperTwistTrainingSubsystemInternal
return Profile;
}
FHyperTwistVisionSolveExplanationProfile BuildDefaultClassicCubeSolveExplanationProfile(
const FString& ProfileId
)
{
FHyperTwistVisionSolveExplanationProfile Profile;
Profile.ExplanationProfileId = ProfileId;
Profile.PuzzleId = TEXT("cube/3x3x3");
Profile.RecommendationMode = TEXT("bounded-stage-ladder-v1");
Profile.bSupportsStageRecommendation = true;
Profile.bSupportsStepNavigation = true;
Profile.bSupportsPlaybackStage = false;
auto AddStep = [&Profile](
const TCHAR* StepId,
const TCHAR* StageId,
const TCHAR* Title,
const TCHAR* Description,
const TCHAR* RecommendationLine,
const int32 StageOrdinal
)
{
FHyperTwistVisionSolveExplanationStep Step;
Step.StepId = StepId;
Step.StageId = StageId;
Step.Title = Title;
Step.Description = Description;
Step.RecommendationLine = RecommendationLine;
Step.StageOrdinal = StageOrdinal;
Step.bPlaybackDeferred = true;
Profile.Steps.Add(Step);
};
AddStep(
TEXT("orient-centers-step"),
TEXT("orient-centers"),
TEXT("Orient Centers"),
TEXT("Check the cube orientation and align the center reference before turning into the solve ladder."),
TEXT("Confirm the edited net and lock the reference orientation before solve guidance begins."),
0
);
AddStep(
TEXT("solve-white-cross-step"),
TEXT("solve-white-cross"),
TEXT("Build First Cross"),
TEXT("Assemble the opening cross with edge placement matched against the side colors."),
TEXT("Start with a stable cross so later first-layer turns stay bounded and readable."),
1
);
AddStep(
TEXT("solve-first-layer-corners-step"),
TEXT("solve-first-layer-corners"),
TEXT("Place First-Layer Corners"),
TEXT("Insert the matching corners to finish the first layer without disturbing the cross."),
TEXT("Close the first layer before moving into the middle-band edge work."),
2
);
AddStep(
TEXT("solve-second-layer-edges-step"),
TEXT("solve-second-layer-edges"),
TEXT("Solve Middle-Layer Edges"),
TEXT("Move the non-top edges into their middle-layer slots while preserving the solved first layer."),
TEXT("Complete the middle layer so orientation-only work remains on the last layer."),
3
);
AddStep(
TEXT("solve-oll-edges-step"),
TEXT("solve-oll-edges"),
TEXT("Orient Last-Layer Edges"),
TEXT("Turn the last-layer edges so the top-face color is established across the edge ring."),
TEXT("Orient the last-layer edges before promoting corner orientation."),
4
);
AddStep(
TEXT("solve-oll-corners-step"),
TEXT("solve-oll-corners"),
TEXT("Orient Last-Layer Corners"),
TEXT("Resolve the remaining top-face corner orientation while keeping the lower layers intact."),
TEXT("Finish top-face orientation before any final permutation pass."),
5
);
AddStep(
TEXT("solve-pll-corners-step"),
TEXT("solve-pll-corners"),
TEXT("Permute Last-Layer Corners"),
TEXT("Cycle the last-layer corners into their destination positions after orientation is complete."),
TEXT("Place the last-layer corners first so the final edge permutation stays bounded."),
6
);
AddStep(
TEXT("solve-pll-edges-step"),
TEXT("solve-pll-edges"),
TEXT("Permute Last-Layer Edges"),
TEXT("Finish the solve by cycling the last-layer edges into their final positions."),
TEXT("Close with the last-layer edges; playback ownership remains deferred in this packet."),
7
);
auto AddAction = [&Profile](
const TCHAR* ActionId,
const TCHAR* InputBinding,
const TCHAR* SurfaceId
)
{
FHyperTwistVisionShellActionBinding ActionBinding;
ActionBinding.ActionId = ActionId;
ActionBinding.InputBinding = InputBinding;
ActionBinding.SurfaceId = SurfaceId;
Profile.ActionBindings.Add(ActionBinding);
};
AddAction(TEXT("start-solve-explanation"), TEXT("button:start-explanation"), TEXT("cube-edit"));
AddAction(TEXT("step-solve-forward"), TEXT("button:next-step"), TEXT("solve-explanation-stage"));
AddAction(TEXT("step-solve-backward"), TEXT("button:previous-step"), TEXT("solve-explanation-stage"));
AddAction(TEXT("restart-solve-explanation"), TEXT("button:restart-explanation"), TEXT("solve-explanation-stage"));
return Profile;
}
FHyperTwistVisionShellState BuildClassicCubeWebcamShellState(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession,
@ -2578,6 +2695,64 @@ namespace HyperTwistTrainingSubsystemInternal
return BrowserShellState;
}
FHyperTwistVisionSolveExplanationState BuildClassicCubeSolveExplanationState(
const FHyperTwistVisionSessionConfig& SessionConfig,
const FHyperTwistVisionReconstructionSession& ReconstructionSession
)
{
FHyperTwistVisionSolveExplanationState SolveExplanationState;
const FHyperTwistVisionSolveExplanationProfile SolveExplanationProfile =
SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid()
? SessionConfig.SolveExplanationProfileDefinition
: BuildDefaultClassicCubeSolveExplanationProfile(
!SessionConfig.SolveExplanationProfileId.IsEmpty()
? SessionConfig.SolveExplanationProfileId
: TEXT("classic-cube-solve-explanation-v1")
);
SolveExplanationState.ExplanationProfileId = SolveExplanationProfile.ExplanationProfileId;
SolveExplanationState.TotalStepCount = SolveExplanationProfile.Steps.Num();
SolveExplanationState.bPlaybackDeferred = !SolveExplanationProfile.bSupportsPlaybackStage;
SolveExplanationState.bRequiresFullReconstruction = true;
const int32 RemainingFaceCount = ReconstructionSession.IsStructurallyValid()
? ReconstructionSession.MissingFaces.Num()
: 6;
if (ReconstructionSession.bHasCompleteClassicCubeNet)
{
SolveExplanationState.RecommendedStageId =
SolveExplanationProfile.Steps.Num() > 0
? SolveExplanationProfile.Steps[0].StageId
: TEXT("orient-centers");
SolveExplanationState.RecommendationStatusLine =
TEXT("Solve guidance is ready after cube-edit review.");
SolveExplanationState.RecommendationDetailLine =
TEXT("Review any manual corrections, then start the bounded stage ladder. Playback ownership stays deferred in this packet.");
SolveExplanationState.CurrentStepOrdinal = -1;
SolveExplanationState.bReadyForExplanation = true;
SolveExplanationState.bStartExplanationVisible = true;
SolveExplanationState.bNextStepVisible = false;
SolveExplanationState.bPreviousStepVisible = false;
}
else
{
SolveExplanationState.RecommendedStageId = TEXT("capture-remaining-faces");
SolveExplanationState.RecommendationStatusLine =
TEXT("Solve guidance unlocks after capture completes.");
SolveExplanationState.RecommendationDetailLine = FString::Printf(
TEXT("Commit the remaining %d face(s) and finish the bounded reconstruction pass before solve guidance unlocks."),
RemainingFaceCount
);
SolveExplanationState.CurrentStepOrdinal = -1;
SolveExplanationState.bReadyForExplanation = false;
SolveExplanationState.bStartExplanationVisible = false;
SolveExplanationState.bNextStepVisible = false;
SolveExplanationState.bPreviousStepVisible = false;
}
return SolveExplanationState;
}
EHyperTwistPuzzleFamily ResolvePuzzleFamilyFromPuzzleId(const FString& PuzzleId)
{
FHyperTwistPuzzleDefinitionRef RetainedHyperDefinition;
@ -7012,6 +7187,11 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit
Result.ReconstructionSession,
Result.ObservedFace
);
Result.SolveExplanationState =
HyperTwistTrainingSubsystemInternal::BuildClassicCubeSolveExplanationState(
ActiveRecognitionSessionState.SessionConfig,
Result.ReconstructionSession
);
}
const FString ResolvedStage = !Result.CommittedUnit.IsEmpty()
@ -7984,6 +8164,28 @@ FHyperTwistVisionSessionConfig UHyperTwistTrainingSubsystem::BuildActiveRecognit
SessionConfig.BrowserShellProfileDefinition.BrowserShellProfileId =
SessionConfig.BrowserShellProfileId;
}
if (SessionConfig.SolveExplanationProfileId.IsEmpty()
&& SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid())
{
SessionConfig.SolveExplanationProfileId =
SessionConfig.SolveExplanationProfileDefinition.ExplanationProfileId;
}
if (SessionConfig.SolveExplanationProfileId.IsEmpty())
{
SessionConfig.SolveExplanationProfileId = TEXT("classic-cube-solve-explanation-v1");
}
if (!SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid())
{
SessionConfig.SolveExplanationProfileDefinition =
HyperTwistTrainingSubsystemInternal::BuildDefaultClassicCubeSolveExplanationProfile(
SessionConfig.SolveExplanationProfileId
);
}
if (SessionConfig.SolveExplanationProfileDefinition.ExplanationProfileId.IsEmpty())
{
SessionConfig.SolveExplanationProfileDefinition.ExplanationProfileId =
SessionConfig.SolveExplanationProfileId;
}
}
return SessionConfig;

View file

@ -658,6 +658,155 @@ struct FHyperTwistVisionBrowserShellState
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionSolveExplanationStep
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StepId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StageId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Title;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Description;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecommendationLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 StageOrdinal = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bPlaybackDeferred = true;
bool IsStructurallyValid() const
{
return !StepId.IsEmpty()
&& !StageId.IsEmpty()
&& !Title.IsEmpty()
&& !Description.IsEmpty()
&& !RecommendationLine.IsEmpty()
&& StageOrdinal >= 0;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionSolveExplanationProfile
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ExplanationProfileId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PuzzleId = TEXT("cube/3x3x3");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecommendationMode = TEXT("bounded-stage-ladder-v1");
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSupportsStageRecommendation = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSupportsStepNavigation = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSupportsPlaybackStage = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionSolveExplanationStep> Steps;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistVisionShellActionBinding> ActionBindings;
bool IsStructurallyValid() const
{
if (ExplanationProfileId.IsEmpty()
|| PuzzleId.IsEmpty()
|| RecommendationMode.IsEmpty()
|| Steps.Num() <= 0
|| ActionBindings.Num() <= 0)
{
return false;
}
for (const FHyperTwistVisionSolveExplanationStep& Step : Steps)
{
if (!Step.IsStructurallyValid())
{
return false;
}
}
for (const FHyperTwistVisionShellActionBinding& ActionBinding : ActionBindings)
{
if (!ActionBinding.IsStructurallyValid())
{
return false;
}
}
return true;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionSolveExplanationState
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ExplanationProfileId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecommendedStageId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecommendationStatusLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecommendationDetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentStepId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 CurrentStepOrdinal = -1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 TotalStepCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bReadyForExplanation = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bStartExplanationVisible = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bNextStepVisible = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bPreviousStepVisible = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bPlaybackDeferred = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bRequiresFullReconstruction = true;
bool IsStructurallyValid() const
{
return !ExplanationProfileId.IsEmpty()
&& CurrentStepOrdinal >= -1
&& TotalStepCount >= 0;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistVisionSessionConfig
{
@ -699,6 +848,12 @@ struct FHyperTwistVisionSessionConfig
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionBrowserShellProfile BrowserShellProfileDefinition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SolveExplanationProfileId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionSolveExplanationProfile SolveExplanationProfileDefinition;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FIntPoint FrameSize = FIntPoint(1280, 720);
@ -822,6 +977,9 @@ struct FHyperTwistVisionCommitResult
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionBrowserShellState BrowserShellState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FHyperTwistVisionSolveExplanationState SolveExplanationState;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> Warnings;

View file

@ -0,0 +1,223 @@
// Copyright HyperTwist, Inc. All Rights Reserved.
#include "Misc/AutomationTest.h"
#include "Engine/GameInstance.h"
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
#include "UObject/UnrealType.h"
#if WITH_AUTOMATION_TESTS
namespace HyperTwistRubixCubeSolverPhase6RQTestInternal
{
FHyperTwistTrainingDeck MakeRecognitionDeck()
{
FHyperTwistTrainingDeck Deck;
Deck.DeckId = TEXT("phase6r-q/rubix-cube-solver-solve-explanation");
Deck.Title = TEXT("Phase 6R-Q Rubix Cube Solver Solve Explanation");
Deck.DeliveryModes = {
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
EHyperTwistTrainingDeliveryMode::CoachReviewed
};
FHyperTwistTrainingCase TrainingCase;
TrainingCase.CaseId = TEXT("phase6r-q-case");
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition;
TrainingCase.PromptLabel = TEXT("Phase 6R-Q Recognition");
TrainingCase.AllowedDeliveryModes = {
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
EHyperTwistTrainingDeliveryMode::CoachReviewed
};
Deck.Cases = {TrainingCase};
return Deck;
}
void ForceMockRecognitionClient(UHyperTwistTrainingSubsystem* TrainingSubsystem)
{
if (TrainingSubsystem == nullptr)
{
return;
}
if (FStrProperty* RecognitionClientKindProperty = FindFProperty<FStrProperty>(
UHyperTwistTrainingSubsystem::StaticClass(),
TEXT("RecognitionClientKind")
))
{
RecognitionClientKindProperty->SetPropertyValue_InContainer(TrainingSubsystem, TEXT("mock"));
}
}
UHyperTwistTrainingSubsystem* MakeRecognitionSubsystem(const FString& SessionId)
{
UGameInstance* GameInstance = NewObject<UGameInstance>(GetTransientPackage());
if (GameInstance == nullptr)
{
return nullptr;
}
UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
if (TrainingSubsystem == nullptr)
{
return nullptr;
}
ForceMockRecognitionClient(TrainingSubsystem);
const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
MakeRecognitionDeck(),
TEXT("phase6r-q-user"),
SessionId,
EHyperTwistTrainingDeliveryMode::RecognitionAssisted
);
return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr;
}
void SubmitPreviewFrame(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& FaceId, const int32 FrameOrdinal)
{
FHyperTwistVisionFrameEnvelope Frame;
Frame.FrameOrdinal = FrameOrdinal;
Frame.TargetFaceHint = FaceId;
TrainingSubsystem->SubmitActiveRecognitionFrame(Frame);
}
FHyperTwistVisionCommitResult CommitFace(
UHyperTwistTrainingSubsystem* TrainingSubsystem,
const FString& FaceId,
const int32 FrameOrdinal
)
{
SubmitPreviewFrame(TrainingSubsystem, FaceId, FrameOrdinal);
FHyperTwistVisionCommitRequest Request;
Request.TargetFace = FaceId;
return TrainingSubsystem->CommitActiveRecognitionObservation(Request);
}
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RQSolveExplanationSessionConfigTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q.SolveExplanationSessionConfig",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RQSolveExplanationSessionConfigTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RQTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-q-config-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-Q."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
FString OpenError;
TestTrue(TEXT("The recognition session must open for the solve-explanation route."), TrainingSubsystem->OpenActiveRecognitionSession(OpenError));
TestTrue(TEXT("Opening the recognition session must not report an error."), OpenError.IsEmpty());
const FHyperTwistTrainingRecognitionSessionState SessionState =
TrainingSubsystem->GetActiveRecognitionSessionState();
TestEqual(
TEXT("The solve-explanation route must normalize the first-party explanation profile id."),
SessionState.SessionConfig.SolveExplanationProfileId,
TEXT("classic-cube-solve-explanation-v1")
);
TestTrue(
TEXT("The solve-explanation route must provide a structurally valid explanation profile definition."),
SessionState.SessionConfig.SolveExplanationProfileDefinition.IsStructurallyValid()
);
TestEqual(
TEXT("The solve-explanation route must retain eight bounded solve stages."),
SessionState.SessionConfig.SolveExplanationProfileDefinition.Steps.Num(),
8
);
TestEqual(
TEXT("The solve-explanation route must retain four bounded explanation actions."),
SessionState.SessionConfig.SolveExplanationProfileDefinition.ActionBindings.Num(),
4
);
TestFalse(
TEXT("The solve-explanation route must still defer playback-stage ownership in this packet."),
SessionState.SessionConfig.SolveExplanationProfileDefinition.bSupportsPlaybackStage
);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RQSolveExplanationPartialStateTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q.SolveExplanationPartialState",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RQSolveExplanationPartialStateTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RQTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-q-partial-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-Q."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
const FHyperTwistVisionCommitResult Result =
HyperTwistRubixCubeSolverPhase6RQTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 1);
TestTrue(TEXT("The partial commit route must carry a structurally valid solve explanation state."), Result.SolveExplanationState.IsStructurallyValid());
TestFalse(TEXT("Solve guidance must stay locked before a full reconstruction is available."), Result.SolveExplanationState.bReadyForExplanation);
TestFalse(TEXT("The solve explanation start action must stay hidden before capture completes."), Result.SolveExplanationState.bStartExplanationVisible);
TestEqual(TEXT("The partial solve route must direct the user back to capture."), Result.SolveExplanationState.RecommendedStageId, TEXT("capture-remaining-faces"));
TestTrue(TEXT("The partial solve route must keep the full reconstruction guard explicit."), Result.SolveExplanationState.bRequiresFullReconstruction);
TestEqual(TEXT("The partial solve route must still expose the full bounded step count."), Result.SolveExplanationState.TotalStepCount, 8);
return true;
}
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
FHyperTwistRubixCubeSolverPhase6RQSolveExplanationCompleteStateTest,
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q.SolveExplanationCompleteState",
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
)
bool FHyperTwistRubixCubeSolverPhase6RQSolveExplanationCompleteStateTest::RunTest(const FString& Parameters)
{
UHyperTwistTrainingSubsystem* TrainingSubsystem =
HyperTwistRubixCubeSolverPhase6RQTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-q-complete-session"));
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-Q."), TrainingSubsystem);
if (TrainingSubsystem == nullptr)
{
return false;
}
const TArray<FString> FaceOrder = {
TEXT("U"),
TEXT("R"),
TEXT("F"),
TEXT("D"),
TEXT("L"),
TEXT("B")
};
FHyperTwistVisionCommitResult Result;
for (int32 FaceOrdinal = 0; FaceOrdinal < FaceOrder.Num(); ++FaceOrdinal)
{
Result = HyperTwistRubixCubeSolverPhase6RQTestInternal::CommitFace(
TrainingSubsystem,
FaceOrder[FaceOrdinal],
FaceOrdinal + 1
);
}
TestTrue(TEXT("The completed solve route must carry a structurally valid reconstruction session."), Result.ReconstructionSession.IsStructurallyValid());
TestTrue(TEXT("The completed solve route must carry a structurally valid solve explanation state."), Result.SolveExplanationState.IsStructurallyValid());
TestTrue(TEXT("The completed solve route must carry a full classic cube net."), Result.ReconstructionSession.bHasCompleteClassicCubeNet);
TestTrue(TEXT("Solve guidance must unlock after the full reconstruction completes."), Result.SolveExplanationState.bReadyForExplanation);
TestTrue(TEXT("The solve explanation start action must be visible after reconstruction completes."), Result.SolveExplanationState.bStartExplanationVisible);
TestFalse(TEXT("The solve explanation route must not expose next-step navigation before explanation starts."), Result.SolveExplanationState.bNextStepVisible);
TestFalse(TEXT("The solve explanation route must not expose previous-step navigation before explanation starts."), Result.SolveExplanationState.bPreviousStepVisible);
TestTrue(TEXT("The solve explanation route must keep playback deferred in this packet."), Result.SolveExplanationState.bPlaybackDeferred);
TestEqual(TEXT("The solve explanation route must recommend the orientation stage first."), Result.SolveExplanationState.RecommendedStageId, TEXT("orient-centers"));
TestEqual(TEXT("The solve explanation route must keep the bounded eight-stage ladder."), Result.SolveExplanationState.TotalStepCount, 8);
TestEqual(TEXT("The browser shell must remain in cube-edit after reconstruction completes."), Result.BrowserShellState.ActiveStageId, TEXT("cube-edit"));
return true;
}
#endif

View file

@ -153,9 +153,10 @@ Status update on `2026-05-21`:
- `HactarCE/Hyperspeedcube` is now closed for the currently justified retained families
- the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
packet is now landed in current code
- the current next bounded move is a source-backed `Phase 6R-Q`
`vivaansinghvi07/rubix-cube-solver` solve explanation/recommendation preparation/control pass,
not a new restrictive packet by default
- the bounded permissive `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver`
solve explanation/recommendation packet is now landed in current code
- the current next bounded move is a source-backed retained classic-cube recognition multi-face
correction/explanation preparation/control pass, not a new restrictive packet by default
- use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A`
ownership contract for the current queue after that correction
@ -259,9 +260,8 @@ Current practical interpretation:
- the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md`
- the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md`
- the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md`
- the next bounded move is a source-backed `Phase 6R-Q`
`vivaansinghvi07/rubix-cube-solver` preparation/control pass for a bounded solve
explanation/recommendation seam
- the next bounded move is a source-backed retained classic-cube recognition multi-face
correction/explanation preparation/control pass
Companion docs:
@ -803,8 +803,9 @@ Approved working posture:
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- browser/webcam shell profile and browser-shell session-state composition
- bounded solve explanation/recommendation stage ladder and recommendation state
- keep the row only partially incorporated by default:
- broad solve explanation or recommendation shell remains deferred
- multi-face correction or explanation shell beyond the bounded stage ladder remains deferred
- any redistribution of `frontend/lib/twistysim.min.js` still requires preserved or replaced
upstream provenance/license

View file

@ -0,0 +1,135 @@
# HyperTwist Phase 6R-Q rubix-cube-solver solve explanation recommendation implementation packet
Created on `2026-05-23`
## Status
- first-party HyperTwist packet
- bounded permissive `Phase 6R-Q` implementation slice
## Purpose
This packet lands the current retained `rubix-cube-solver` solve explanation/recommendation
family:
- first-party classic-cube solve explanation profile and recommendation-state composition above the
already landed reconstruction and browser-shell seams
It is not:
- a full `rubix-cube-solver` row transplant
- a bundled `twistysim.min.js` redistribution packet
- a generalized solver backend packet
- a broad multi-face correction shell packet
- a `qbr` multilingual shell packet
## Current authority basis
This implementation packet stands on:
- `docs/REPO_LICENSE_TRACKING.md`
- `docs/ops/HYPERTWIST_CROSS_LANE_AUTHORITY_HIERARCHY_AND_RECONCILIATION_2026-05-20.md`
- `docs/arch/HYPERTWIST_PHASE6R_Q_RUBIX_CUBE_SOLVER_SOLVE_EXPLANATION_RECOMMENDATION_PREPARATION_PACKET_2026-05-23.md`
The retained owner remains:
- `vivaansinghvi07/rubix-cube-solver`
The top-level product shell owner does not change here:
- first-party HyperTwist remains the broader recognition-session and product-shell owner
- this packet keeps `vivaansinghvi07/rubix-cube-solver` bounded to the narrower `A1 / R1 / F3`
solve explanation/recommendation slice
## Landed scope
The current code now owns a retained `rubix-cube-solver` solve explanation/recommendation contract
through:
- first-party `HyperTwistRecognition` contract types for:
- solve explanation step definition
- solve explanation profile definition
- solve explanation session-state readout
- solve explanation profile id on recognition session config
- commit result solve explanation payloads
- recognition session config defaults for:
- first-party classic-cube solve explanation profile id
- solve explanation profile definition
- first-party commit result shaping for:
- explanation readiness after full reconstruction
- recommendation stage and detail lines
- bounded step count
- start/previous/next visibility
- playback deferred state
- explicit full-reconstruction guard
- sample contract routing in:
- `UHyperTwistContractLibrary`
- active subsystem normalization in:
- `UHyperTwistTrainingSubsystem`
- focused automation coverage in:
- `HyperTwistRubixCubeSolverPhase6RQSolveExplanationContractTest.cpp`
## Why this is still intentionally bounded
This packet lands the bounded solve explanation/recommendation seam, but it does not widen into
neighboring families or broader ownership.
Still excluded:
- bundled `twistysim.min.js` redistribution
- generalized solver backend ownership
- broad multi-face correction shell ownership
- `qbr` multilingual shell ownership
- `qbr` bundled-font redistribution
## Validation
Build validation:
- `C:\Program Files\Epic Games\UE_5.7\Engine\Build\BatchFiles\Build.bat UnrealHyperTwistEditor Win64 Development -Project='C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject' -WaitMutex -NoHotReloadFromIDE -NoUba`
Focused automation validation:
- `C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject -unattended -nop4 -nosplash -NullRHI -log -stdout -FullStdOutLogOutput -AbsLog=C:\HyperTwist\UnrealHyperTwist\Saved\Logs\Phase6R-Q-Rubix-Verify.log -ReportExportPath=C:\HyperTwist\UnrealHyperTwist\Saved\AutomationReports\Phase6R-Q-Rubix-Verify -ExecCmds="Automation RunTests HyperTwist.Permissive.RubixCubeSolver.Phase6R.Q; Quit" -TestExit="Automation Test Queue Empty"`
Regression automation validation:
- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.P`
- `HyperTwist.Permissive.RubixCubeSolver.Phase6R.C`
- `HyperTwist.Permissive.Qbr.Phase6R.O`
- `HyperTwist.CleanRoom.CubeDesk`
Expected covered tests:
- `SolveExplanationSessionConfig`
- `SolveExplanationPartialState`
- `SolveExplanationCompleteState`
- existing `rubix-cube-solver`, `qbr`, and `CubeDesk` regression suites
## Queue effect
This packet consumes the current `Phase 6R-Q` implementation slice.
`vivaansinghvi07/rubix-cube-solver` remains partially incorporated:
- landed now:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- browser/webcam shell profile and browser-shell session-state composition
- bounded solve explanation/recommendation stage ladder and recommendation state
- still deferred:
- multi-face correction or explanation shell beyond the bounded stage ladder
- bundled `twistysim.min.js` redistribution
The next clean move is:
- a source-backed retained classic-cube recognition multi-face correction/explanation
preparation/control pass shared across `kkoomen/qbr` and `vivaansinghvi07/rubix-cube-solver`
Keep the future sequencing guards visible:
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing its
upstream provenance/license in any future widening that would ship that asset
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation or
replacement in any future widening that would ship that asset

View file

@ -0,0 +1,206 @@
# HyperTwist Phase 6R-Q rubix-cube-solver solve explanation recommendation preparation packet
Created on `2026-05-23`
## Status
- historical consumed preparation authority
- bounded post-`Phase 6R-P` control slice
- the first bounded implementation slice now lands separately after this packet
## Purpose
This packet freezes the next widening order after the landed `Phase 6R-P`
`vivaansinghvi07/rubix-cube-solver` browser/webcam shell slice.
The open task was:
- define the next bounded source-backed widening packet for `vivaansinghvi07/rubix-cube-solver`
as a solve explanation/recommendation seam above the already landed reconstruction and
browser-shell seams
It is not:
- a full `rubix-cube-solver` row transplant
- a bundled `twistysim.min.js` redistribution packet
- a generalized solver backend packet
- a broad multi-face correction shell packet
- a `qbr` multilingual shell packet
- a `qbr` bundled-font packet
## Current authority basis
This preparation packet stands on already-closed authority:
- `docs/REPO_LICENSE_TRACKING.md`
- `docs/v6_5_deep_manual_pack/HyperTwist/ROADMAP.md`
- `docs/arch/HYPERTWIST_PHASE6R_C_RUBIX_CUBE_SOLVER_RECONSTRUCTION_IMPLEMENTATION_PACKET_2026-05-20.md`
- `docs/arch/HYPERTWIST_PHASE6R_O_QBR_WEBCAM_UI_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md`
- `docs/arch/HYPERTWIST_PHASE6R_P_RUBIX_CUBE_SOLVER_BROWSER_WEBCAM_SHELL_IMPLEMENTATION_PACKET_2026-05-22.md`
The key accepted routing facts are:
- `vivaansinghvi07/rubix-cube-solver` remains the retained recognition-companion donor for the
bounded browser-assisted correction and solve-guidance seam
- the already landed `Phase 6R-C` slice owns:
- committed-face reconstruction session
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- the already landed `Phase 6R-P` slice owns:
- classic-cube browser shell profile
- browser-stage routing
- websocket transport metadata
- manual cube-edit handoff
- the donor value now strongest for the next narrower slice is:
- solve-stage recommendation semantics
- bounded step-ladder authoring
- explanation-state readiness after full reconstruction
- next/previous/restart explanation action routing
- ownership denied here is:
- do not widen into bundled `twistysim.min.js` redistribution
- do not widen into generalized solver backend ownership
- do not widen into `qbr` multilingual or bundled-font ownership
- do not widen into the broader multi-face correction shell in the same packet
## Why this was the next packet
The earlier retained queue heads were already landed:
- `Phase 6R-A` `HactarCE/Hyperspeedcube`
- `Phase 6R-B` `kkoomen/qbr`
- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver`
- `Phase 6R-D` `roice3/MagicTile`
- `Phase 6R-E` `ggml-org/whisper.cpp`
- `Phase 6R-F` `SYSTRAN/faster-whisper`
- `Phase 6R-G` `rhasspy/piper`
- `Phase 6R-H` `coqui-ai/TTS`
- `Phase 6R-I` `roice3/Magic120Cell`
- `Phase 6R-J` `roice3/MagicCube5D`
- `Phase 6R-K` `HactarCE/Hyperspeedcube`
- `Phase 6R-L` `HactarCE/Hyperspeedcube`
- `Phase 6R-M` `HactarCE/Hyperspeedcube`
- `Phase 6R-N` `HactarCE/Hyperspeedcube`
- `Phase 6R-O` `kkoomen/qbr`
- `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver`
That advanced the live queue to:
- `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver`
with the adjacent retained recognition remainder ordered behind it:
- retained classic-cube recognition multi-face correction/explanation shell
## Required result
The source-backed control pass for this packet is now complete.
The first actual `6R-Q` implementation packet should:
- define one bounded retained solve explanation/recommendation slice from `rubix-cube-solver`
- keep the slice inside the accepted recognition-shell seam above the landed reconstruction and
browser-shell routes
- widen only the first stage-ladder guidance family that can stand on its own without dragging in
bundled frontend-asset redistribution or generalized solver backend ownership
- explicitly state which neighboring retained rows stay closed in that packet
## Source-backed retained basis
The queue-head decision is now source-backed rather than README-only.
Inspected retained donor basis:
- `README.md`
- `backend/server.py`
- `backend/cv.py`
- `frontend/src/script.js`
Inspected first-party receiving basis:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RCReconstructionContractTest.cpp`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistRubixCubeSolverPhase6RPBrowserShellContractTest.cpp`
## Narrowed 6R-Q slice decision
The first widening slice is fixed as:
1. classic-cube solve explanation/recommendation profile and commit-state composition
not as bundled frontend playback or generalized solver backend ownership.
That narrowed slice covers:
- first-party solve explanation profile for:
- ordered bounded stage ids:
- `orient-centers`
- `solve-white-cross`
- `solve-first-layer-corners`
- `solve-second-layer-edges`
- `solve-oll-edges`
- `solve-oll-corners`
- `solve-pll-corners`
- `solve-pll-edges`
- bounded action bindings:
- `start-solve-explanation`
- `step-solve-forward`
- `step-solve-backward`
- `restart-solve-explanation`
- first-party solve explanation state readout for:
- explanation readiness after full reconstruction
- current recommendation stage
- recommendation status/detail lines
- total bounded step count
- start/previous/next visibility
- playback deferred state
- subsystem normalization and focused automation coverage
## Deferred neighboring families
The first `6R-Q` implementation packet must keep these capability families closed:
- bundled `twistysim.min.js` shipping or redistribution
- generalized solver backend ownership
- `qbr` multilingual shell ownership
- `qbr` bundled-font redistribution
- broad multi-face correction shell ownership
- broad playback runtime ownership
## Why this slice was first
- `rubix-cube-solver` is strongest for the bounded solve-ladder guidance that sits directly above
the already landed reconstruction and browser-shell seams
- current first-party recognition code already had the session, preview, commit, reconstruction,
and browser-shell envelopes, but it did not yet own a first-party solve explanation profile or
recommendation state
- widening into bundled frontend assets or generalized backend ownership would prematurely absorb
adjacent legal and routing concerns that are still narrower and separately governed
## Out of scope for the first 6R-Q packet
- bundled `twistysim.min.js` shipping
- generalized solver backend ownership
- `qbr` multilingual or bundled-font ownership
- broad multi-face correction shell ownership
- broad playback runtime ownership
## Acceptance criteria
- the packet records why the live queue advanced to `6R-Q`
- the packet records that the first `6R-Q` widening slice is solve explanation/recommendation only
- the packet states exact out-of-scope families for the first `6R-Q` pass
- the packet keeps the `rubix-cube-solver` frontend-asset guard visible
- the packet keeps the `qbr` bundled-font guard visible
## Validation checklist
1. confirm the retained `rubix-cube-solver` seam narrows cleanly to a bounded solve explanation
profile and recommendation-state contract
2. confirm the implementation packet is framed as bounded source-backed widening rather than a
donor frontend transplant
3. confirm bundled frontend-asset redistribution, generalized solver backend widening, and broader
multi-face correction remain explicitly deferred
That is the packet.

View file

@ -98,13 +98,17 @@ The next bounded move is now:
current code
6. the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
packet is now landed in current code
7. source-backed `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` solve
explanation/recommendation preparation/control pass is now the next bounded move
8. keep `Phase 6R-Q` narrower than bundled `twistysim.min.js` redistribution and generalized
solver backend ownership in the same packet
9. keep the `qbr` guard visible:
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation
or replacement in any future widening that would ship that asset
7. the bounded permissive `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver` solve
explanation/recommendation packet is now landed in current code
8. the next bounded move is a source-backed retained classic-cube recognition multi-face
correction/explanation preparation/control pass shared across `kkoomen/qbr` and
`vivaansinghvi07/rubix-cube-solver`
9. keep the `rubix-cube-solver` guard visible:
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing
its upstream provenance/license in any future widening that would ship that asset
10. keep the `qbr` guard visible:
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation
or replacement in any future widening that would ship that asset
## Memory-specific sequencing rule

View file

@ -110,6 +110,7 @@ Do not collapse those three tiers into one undifferentiated "features" voice.
| Classic-cube webcam shell | Implemented now | landed `qbr` `Phase 6R-O` | First-party webcam shell profile, bounded overlay panels, and input-action contract are live. |
| Committed-face reconstruction and final classic-net shaping | Implemented now | landed `rubix-cube-solver` packet | Reconstruction session boundary is live. |
| Browser-assisted recognition shell | Implemented now | landed `rubix-cube-solver` `Phase 6R-P` | First-party browser-shell profile, websocket session metadata, stage routing, and manual-edit handoff are live. |
| Bounded solve explanation and recommendation shell | Implemented now | landed `rubix-cube-solver` `Phase 6R-Q` | First-party stage-ladder guidance and recommendation-state readout are live after full reconstruction. |
| Hyper puzzle catalog contract | Implemented now | landed `Hyperspeedcube` `Phase 6R-A` | Narrow retained hyper-puzzle catalog slice is live. |
| Hyper notation and replay-log serialization boundary | Implemented now | landed `Hyperspeedcube` `Phase 6R-K` | Narrow retained notation and log-serialization slice is live. |
| Hyper replay verification boundary | Implemented now | landed `Hyperspeedcube` `Phase 6R-L` | Narrow retained replay-verification and solve-proof slice is live. |
@ -146,6 +147,7 @@ repo.
| Classic-cube webcam shell | Implemented now | `qbr` bounded packet | Live first-party webcam shell profile and shell-state composition above the bounded recognition intake. |
| Committed-face reconstruction session | Implemented now | `rubix-cube-solver` bounded packet | Live bounded reconstruction seam. |
| Browser recognition shell | Implemented now | `rubix-cube-solver` bounded packet | Live browser session-state, websocket transport routing, and manual-edit handoff above the bounded reconstruction seam. |
| Bounded solve explanation/recommendation shell | Implemented now | `rubix-cube-solver` bounded packet | Live stage-ladder recommendation state above the bounded browser recognition shell. |
| Provider-backed recognition service contract | Implemented now | first-party recognition client surfaces | First-party normalized recognition session boundary is real. |
| Multilingual recognition guidance shell | Deep-source grounded retained | `qbr` retained remainder | Kept deferred until a font-safe and provenance-safe shipping path is explicit. |
| Multi-face correction/explanation shell | Deep-source grounded retained | `qbr`, `rubix-cube-solver` retained remainder | Not yet widened into the live shell. |

View file

@ -124,9 +124,11 @@ Canonical discovery surfaces for roadmap interpretation:
current code
- the bounded permissive `Phase 6R-P` `vivaansinghvi07/rubix-cube-solver` browser/webcam shell
packet is now landed in current code
- the current next bounded move is a source-backed `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver`
solve explanation/recommendation preparation/control pass for one bounded correction-shell
widening slice
- the bounded permissive `Phase 6R-Q` `vivaansinghvi07/rubix-cube-solver`
solve explanation/recommendation packet is now landed in current code
- the current next bounded move is a source-backed retained classic-cube recognition multi-face
correction/explanation preparation/control pass, while keeping both the `twistysim.min.js` and
`arial-unicode-ms.ttf` redistribution guards explicit
- the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
waiting on another first-party packet
@ -188,8 +190,9 @@ Current routing truth:
- active non-live implementation-board rows: `34`
- retained benchmark, oracle, or clean-room-later rows outside the active implementation board: `9`
The next bounded move is a source-backed `Phase 6R-Q`
`vivaansinghvi07/rubix-cube-solver` solve explanation/recommendation preparation/control pass.
The next bounded move is a source-backed retained classic-cube recognition multi-face
correction/explanation preparation/control pass shared across `kkoomen/qbr` and
`vivaansinghvi07/rubix-cube-solver`.
Queue interpretation after that packet:
@ -242,13 +245,17 @@ Queue interpretation after that packet:
- face-vote replacement ledger
- final classic-net shaping above aggregated committed faces
- browser/webcam shell profile and browser-shell session-state composition
- bounded solve explanation/recommendation stage ladder and recommendation state
- still deferred:
- broad solve explanation or recommendation shell
- multi-face correction or explanation shell beyond the bounded stage ladder
- bundled `twistysim.min.js` redistribution
- after the `Phase 6R-P` implementation packet, keep the future legal sequencing guard visible:
- after the `Phase 6R-Q` implementation packet, keep the future legal sequencing guards visible:
- `vivaansinghvi07/rubix-cube-solver`
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing
its upstream provenance/license in any future widening that would ship that asset
- `kkoomen/qbr`
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate confirmation
or replacement in any future widening that would ship that asset
- `roice3/MagicTile` remains a partially landed row rather than a closed row:
- landed:
- tiling topology and geometry-family contract
@ -313,10 +320,11 @@ Queue interpretation after that packet:
- viseme / gesture runtime integration
- broad assistant-platform scope
- the next queue shape is now:
- `vivaansinghvi07/rubix-cube-solver` deferred solve explanation / recommendation remainder
- retained classic-cube recognition multi-face correction/explanation shell remainder
- the next bounded packet should stay narrow:
- solve explanation/recommendation preparation/control before widening into bundled
`twistysim.min.js` redistribution or generalized solver backend ownership
- multi-face correction/explanation preparation/control before widening into bundled
`twistysim.min.js` redistribution, generalized solver backend ownership, or any `qbr`
multilingual/font-shipping path
- keep the speech-input / voice sidecar legal sequencing guard visible:
- keep code-license judgments separate from model, voice, and payload-license review
- keep the provider-neutral speech-lane guard visible: